diff --git a/.gitignore b/.gitignore index e0c84e8..f334720 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,4 @@ cordova/platforms/wp8/*.suo cordova/platforms/wp8/*.csproj.user # funzo -public/courses +public/content/courses diff --git a/README.md b/README.md index 2252fe2..470ac3e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Funzo-app 0.1.1 +# Funzo-app 0.2.0 [![Build Status](https://travis-ci.org/tunapanda/funzo-app.svg?branch=master)](https://travis-ci.org/tunapanda/funzo-app) [![Join the chat at https://gitter.im/tunapanda/funzo-app](https://badges.gitter.im/tunapanda/funzo-app.svg)](https://gitter.im/tunapanda/funzo-app?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) @@ -6,16 +6,13 @@ This is a preview of ember and H5P running as an app inside Phonegap/Cordova. See [here](https://github.com/tunapanda/funzo-app/wiki/Development-Guide) for information on how to contribute. -Courses during development are added via bower E.G. `bower install tunapanda/funzo-CSE-1000 --save`. - -Prefix any new courses with `funzo` to ensure they are added during the build process. +Courses during development are copied to `public/content/courses`, see https://https://github.com/tunapanda/funzo-CSE-1000 for an example course. ## Prerequisites You will need the following things properly installed on your computer. * [Git](http://git-scm.com/) -* [Git-LFS](https://github.com/github/git-lfs) * [Node.js](http://nodejs.org/) (with NPM) * [Bower](http://bower.io/) * [Ember CLI](http://www.ember-cli.com/) @@ -28,7 +25,6 @@ You will need the following things properly installed on your computer. * change into the new directory * `npm install` * `bower install` -* `bower install git@github.com:tunapanda/funzo-CSE-1000 --save` the demo course (*be sure your github SSH key is loaded!*) ## Running / Development diff --git a/app/pods/application/route.js b/app/pods/application/route.js index b22e620..9db40c6 100644 --- a/app/pods/application/route.js +++ b/app/pods/application/route.js @@ -3,72 +3,72 @@ import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mi export default Ember.Route.extend(ApplicationRouteMixin, { session: Ember.inject.service('session'), - beforeModel() { - return this.store.query('course', { permalink: 'funzo-CSE-1000' }).then((courses) => { - if (!courses.get('length')) { - return new Ember.RSVP.Promise((resolve, reject) => Ember.$.getJSON('courses/funzo-CSE-1000/content.json', (content, status) => { - if (status !== 'success') { - return reject(status); - } - return resolve(content); - }).then((content) => { - let modules = content.modules; - delete content.modules; - - let modulePermalinks = modules.map((module) => module.permalink); - - let activitys = []; - - modules.forEach((module) => { - if (module.activities) { - activitys.push(...module.activities); - let activityPermalinks = module.activities.map((activity) => activity.permalink); - module.activities = activityPermalinks; - } else { - module.activities = []; - } - }); - - let course = content; - - course.modules = modulePermalinks; - - let hash = { - course, - modules, - activitys - }; - - return Ember.RSVP.resolve(this.store.pushPayload(hash)); - - // let modules = Ember.A(content.modules); - // delete content.modules; - - // let course = this.store.createRecord('course', content); - - // return course.save().then(() => { - // modules = modules.map((module) => { - // module.course = course; - // let activities = module.activities; - - // activities.map((activity) => { - // activity = this.store.createRecord('activity', activity); - // activity. - // }); - - // return this.store.createRecord('module', module); - // }); - - // return Ember.RSVP.all(modules.invoke('save')); - // }).then((modules) => { - // course.get('modules').pushObjects(modules); - // return course.save(); - // }); - })); - } - return Ember.RSVP.resolve(); - }); - }, + // beforeModel() { + // return this.store.query('course', { permalink: 'funzo-CSE-1000' }).then((courses) => { + // if (!courses.get('length')) { + // return new Ember.RSVP.Promise((resolve, reject) => Ember.$.getJSON('courses/funzo-CSE-1000/content.json', (content, status) => { + // if (status !== 'success') { + // return reject(status); + // } + // return resolve(content); + // }).then((content) => { + // let modules = content.modules; + // delete content.modules; + + // let modulePermalinks = modules.map((module) => module.permalink); + + // let activitys = []; + + // modules.forEach((module) => { + // if (module.activities) { + // activitys.push(...module.activities); + // let activityPermalinks = module.activities.map((activity) => activity.permalink); + // module.activities = activityPermalinks; + // } else { + // module.activities = []; + // } + // }); + + // let course = content; + + // course.modules = modulePermalinks; + + // let hash = { + // course, + // modules, + // activitys + // }; + + // return Ember.RSVP.resolve(this.store.pushPayload(hash)); + + // // let modules = Ember.A(content.modules); + // // delete content.modules; + + // // let course = this.store.createRecord('course', content); + + // // return course.save().then(() => { + // // modules = modules.map((module) => { + // // module.course = course; + // // let activities = module.activities; + + // // activities.map((activity) => { + // // activity = this.store.createRecord('activity', activity); + // // activity. + // // }); + + // // return this.store.createRecord('module', module); + // // }); + + // // return Ember.RSVP.all(modules.invoke('save')); + // // }).then((modules) => { + // // course.get('modules').pushObjects(modules); + // // return course.save(); + // // }); + // })); + // } + // return Ember.RSVP.resolve(); + // }); + // }, actions: { back() { diff --git a/app/pods/components/h5p-standalone/component.js b/app/pods/components/h5p-standalone/component.js index 391b82e..37e3a1a 100644 --- a/app/pods/components/h5p-standalone/component.js +++ b/app/pods/components/h5p-standalone/component.js @@ -5,7 +5,7 @@ export default Ember.Component.extend({ currentUser: Ember.inject.service('currentUser'), didInsertElement() { - let h5pContent = `courses/${this.get('coursePath')}/modules/${this.get('modulePath')}`; + let h5pContent = `content/courses/${this.get('coursePath')}/modules/${this.get('modulePath')}`; h5pContent += `/${this.get('activityPath')}`; let frameJs = 'assets/h5p-standalone-frame.js'; diff --git a/app/pods/course/adapter.js b/app/pods/course/adapter.js new file mode 100644 index 0000000..1c5389d --- /dev/null +++ b/app/pods/course/adapter.js @@ -0,0 +1,11 @@ +import DS from 'ember-data'; + +export default DS.RESTAdapter.extend({ + urlForFindAll() { + return 'content/courses/index.json'; + }, + + urlForFindRecord(permalink) { + return `content/courses/${permalink}/content.json`; + } +}); \ No newline at end of file diff --git a/app/pods/course/serializer.js b/app/pods/course/serializer.js new file mode 100644 index 0000000..51ffdd7 --- /dev/null +++ b/app/pods/course/serializer.js @@ -0,0 +1,45 @@ +import DS from 'ember-data'; + +export default DS.RESTSerializer.extend({ + primaryKey: 'permalink', + normalizeFindAllResponse(store, primaryModelClass, payload, id, requestType) { + payload = { courses: payload, modules: [], activities: [] }; + + payload.courses.forEach(course => { + let modules = course.modules; + course.modules = modules.map(module => module.permalink); + payload.modules.push.apply(payload.modules, modules); + + modules.forEach(module => { + if (!module.activities) { + return; + } + let activities = module.activities; + module.activities = activities.map(module => module.permalink); + payload.activities.push.apply(payload.activities, activities); + }); + }); + + return this._super(store, primaryModelClass, payload, id, requestType); + }, + + normalizeFindRecordResponse(store, primaryModelClass, payload, id, requestType) { + let course = payload; + payload = { course: payload, modules: [], activities: [] }; + + let modules = course.modules; + course.modules = modules.map(module => module.permalink); + payload.modules.push.apply(payload.modules, modules); + + modules.forEach(module => { + if (!module.activities) { + return; + } + let activities = module.activities; + module.activities = activities.map(module => module.permalink); + payload.activities.push.apply(payload.activities, activities); + }); + + return this._super(store, primaryModelClass, payload, id, requestType); + } +}); \ No newline at end of file diff --git a/app/pods/module/serializer.js b/app/pods/module/serializer.js new file mode 100644 index 0000000..c277b4c --- /dev/null +++ b/app/pods/module/serializer.js @@ -0,0 +1,5 @@ +import DS from 'ember-data'; + +export default DS.RESTSerializer.extend({ + primaryKey: 'permalink' +}); \ No newline at end of file diff --git a/ember-cli-build.js b/ember-cli-build.js index 889c2d3..c072dec 100644 --- a/ember-cli-build.js +++ b/ember-cli-build.js @@ -1,8 +1,23 @@ /*jshint node:true*/ /* global require, module */ var EmberApp = require('ember-cli/lib/broccoli/ember-app'); -var mergeTrees = require('broccoli-merge-trees'); -var Funnel = require('broccoli-funnel'); +// var mergeTrees = require('broccoli-merge-trees'); +// var Funnel = require('broccoli-funnel'); +var fs = require('fs'); + +// Create an index of books available at build-time +var coursesDir = 'public/content/courses'; +var bookList = fs.readdirSync(coursesDir).map(dir => { + var courseDir = `${coursesDir}/${dir}`; + if (!fs.statSync(courseDir).isDirectory()) { + // TODO: filter out nulls + return; + } + // TODO: catch exceptions and filter out invalid JSON + return JSON.parse(fs.readFileSync(courseDir + '/content.json')); +}).filter(e => e); + +fs.writeFileSync(`${coursesDir}/index.json`, JSON.stringify(bookList)); module.exports = function(defaults) { var app = new EmberApp(defaults, { @@ -26,10 +41,10 @@ module.exports = function(defaults) { } }); - var course = new Funnel('bower_components', { - include: ['funzo-*/**'], - destDir: 'courses/' - }); + // var course = new Funnel('bower_components', { + // include: ['funzo-*/**'], + // destDir: 'courses/' + // }); // app.import('bower_components/materialize/dist/css/materialize.css'); app.import('bower_components/tincan/build/tincan.js'); @@ -61,6 +76,6 @@ module.exports = function(defaults) { // please specify an object with the list of modules as keys // along with the exports of each module as its value. // return app.toTree(); - // return app.toTree(); - return mergeTrees([app.toTree(), course]); + return app.toTree(); + // return mergeTrees([app.toTree(), course]); }; diff --git a/package.json b/package.json index 2e1be2d..53096f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "funzo-app", - "version": "0.1.1", + "version": "0.2.0", "description": "Small description for funzo-app goes here", "private": true, "directories": { @@ -24,7 +24,7 @@ "broccoli-jscs": "^1.2.2", "broccoli-merge-trees": "^1.1.1", "ember-ajax": "0.7.1", - "ember-cli": "2.3.0", + "ember-cli": "2.5.0", "ember-cli-app-version": "^1.0.0", "ember-cli-babel": "^5.1.5", "ember-cli-cordova": "0.0.17", diff --git a/tests/integration/pods/components/activity-tabs/component-test.js b/tests/integration/pods/components/activity-tabs/component-test.js deleted file mode 100644 index afc846b..0000000 --- a/tests/integration/pods/components/activity-tabs/component-test.js +++ /dev/null @@ -1,24 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -moduleForComponent('activity-tabs', 'Integration | Component | activity tabs', { - integration: true -}); - -test('it renders', function(assert) { - // Set any properties with this.set('myProperty', 'value'); - // Handle any actions with this.on('myAction', function(val) { ... });" - - this.render(hbs`{{activity-tabs}}`); - - assert.equal(this.$().text().trim(), ''); - - // Template block usage:" - this.render(hbs` - {{#activity-tabs}} - template block text - {{/activity-tabs}} - `); - - assert.equal(this.$().text().trim(), 'template block text'); -}); diff --git a/tests/integration/pods/components/add-course/component-test.js b/tests/integration/pods/components/add-course/component-test.js deleted file mode 100644 index 8ee9152..0000000 --- a/tests/integration/pods/components/add-course/component-test.js +++ /dev/null @@ -1,24 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -moduleForComponent('add-course', 'Integration | Component | add course', { - integration: true -}); - -test('it renders', function(assert) { - // Set any properties with this.set('myProperty', 'value'); - // Handle any actions with this.on('myAction', function(val) { ... });" - - this.render(hbs`{{add-course}}`); - - assert.equal(this.$().text().trim(), ''); - - // Template block usage:" - this.render(hbs` - {{#add-course}} - template block text - {{/add-course}} - `); - - assert.equal(this.$().text().trim(), 'template block text'); -}); diff --git a/tests/integration/pods/components/bootstrap-modal/component-test.js b/tests/integration/pods/components/bootstrap-modal/component-test.js deleted file mode 100644 index 133451a..0000000 --- a/tests/integration/pods/components/bootstrap-modal/component-test.js +++ /dev/null @@ -1,24 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -moduleForComponent('bootstrap-modal', 'Integration | Component | bootstrap modal', { - integration: true -}); - -test('it renders', function(assert) { - // Set any properties with this.set('myProperty', 'value'); - // Handle any actions with this.on('myAction', function(val) { ... });" - - this.render(hbs`{{bootstrap-modal}}`); - - assert.equal(this.$().text().trim(), ''); - - // Template block usage:" - this.render(hbs` - {{#bootstrap-modal}} - template block text - {{/bootstrap-modal}} - `); - - assert.equal(this.$().text().trim(), 'template block text'); -}); diff --git a/tests/integration/pods/components/login-user-select/component-test.js b/tests/integration/pods/components/login-user-select/component-test.js deleted file mode 100644 index 193c4c6..0000000 --- a/tests/integration/pods/components/login-user-select/component-test.js +++ /dev/null @@ -1,17 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -moduleForComponent('login-user-select', 'Integration | Component | login user select', { - integration: true -}); - -test('it renders', function(assert) { - - // Set any properties with this.set('myProperty', 'value'); - // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL + - - this.render(hbs`{{login-user-select}}`); - - assert.equal(this.$().text().trim(), ''); - -}); diff --git a/tests/integration/pods/components/login-user/component-test.js b/tests/integration/pods/components/login-user/component-test.js deleted file mode 100644 index e008dbc..0000000 --- a/tests/integration/pods/components/login-user/component-test.js +++ /dev/null @@ -1,16 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -moduleForComponent('login-user', 'Integration | Component | login user', { - integration: true -}); - -test('it renders', function(assert) { - - // Set any properties with this.set('myProperty', 'value'); - // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL + - - this.render(hbs`{{login-user}}`); - - assert.equal(this.$().text().trim(), 'account_circle'); -}); diff --git a/tests/integration/pods/components/nav-bar/component-test.js b/tests/integration/pods/components/nav-bar/component-test.js deleted file mode 100644 index 6fcbe29..0000000 --- a/tests/integration/pods/components/nav-bar/component-test.js +++ /dev/null @@ -1,17 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -moduleForComponent('nav-bar', 'Integration | Component | nav bar', { - integration: true -}); - -test('it renders', function(assert) { - - // Set any properties with this.set('myProperty', 'value'); - // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL + - - this.render(hbs`{{nav-bar}}`); - - assert.ok(this.$('nav')); - -}); diff --git a/tests/integration/pods/components/pin-input/component-test.js b/tests/integration/pods/components/pin-input/component-test.js deleted file mode 100644 index fb011b3..0000000 --- a/tests/integration/pods/components/pin-input/component-test.js +++ /dev/null @@ -1,17 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -moduleForComponent('pin-input', 'Integration | Component | pin input', { - integration: true -}); - -test('it renders', function(assert) { - - // Set any properties with this.set('myProperty', 'value'); - // Handle any actions with this.on('myAction', function(val) { ... });" + EOL + EOL + - - this.render(hbs`{{pin-input value=pin}}`); - - assert.equal(this.$().text().trim(), ''); - -}); diff --git a/tests/unit/pods/activity/model-test.js b/tests/unit/pods/activity/model-test.js index 8ab7eca..6a8e65c 100644 --- a/tests/unit/pods/activity/model-test.js +++ b/tests/unit/pods/activity/model-test.js @@ -2,7 +2,7 @@ import { moduleForModel, test } from 'ember-qunit'; moduleForModel('activity', 'Unit | Model | activity', { // Specify the other units that are required for this test. - needs: [] + needs: ['model:module'] }); test('it exists', function(assert) { diff --git a/tests/unit/pods/application/serializer-test.js b/tests/unit/pods/application/serializer-test.js deleted file mode 100644 index 705e9ec..0000000 --- a/tests/unit/pods/application/serializer-test.js +++ /dev/null @@ -1,15 +0,0 @@ -import { moduleForModel, test } from 'ember-qunit'; - -moduleForModel('application', 'Unit | Serializer | application', { - // Specify the other units that are required for this test. - needs: ['serializer:application'] -}); - -// Replace this with your real tests. -test('it serializes records', function(assert) { - let record = this.subject(); - - let serializedRecord = record.serialize(); - - assert.ok(serializedRecord); -}); diff --git a/tests/unit/pods/module/model-test.js b/tests/unit/pods/module/model-test.js index 4ffad12..5f5676b 100644 --- a/tests/unit/pods/module/model-test.js +++ b/tests/unit/pods/module/model-test.js @@ -2,7 +2,7 @@ import { moduleForModel, test } from 'ember-qunit'; moduleForModel('module', 'Unit | Model | module', { // Specify the other units that are required for this test. - needs: ['model:course'] + needs: ['model:course', 'model:activity'] }); test('it exists', function(assert) { diff --git a/tests/unit/pods/user/serializer-test.js b/tests/unit/pods/user/serializer-test.js index 19e4a38..384d9c5 100644 --- a/tests/unit/pods/user/serializer-test.js +++ b/tests/unit/pods/user/serializer-test.js @@ -2,7 +2,7 @@ import { moduleForModel, test } from 'ember-qunit'; moduleForModel('user', 'Unit | Serializer | user', { // Specify the other units that are required for this test. - needs: ['serializer:user'] + needs: ['serializer:user', 'model:course', 'model:module', 'model:x-api-statement'] }); // Replace this with your real tests.