PageRenderTime 59ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/resources/javascript/backbone_models.js

https://bitbucket.org/pieterjan_broekaert/hogent_chamilo_assessment_stages
JavaScript | 686 lines | 601 code | 53 blank | 32 comment | 70 complexity | 5e8ef3890548080386f787dc01cb9c35 MD5 | raw file
  1. /**
  2. * Created with JetBrains PhpStorm.
  3. * User: Minas
  4. * Date: 6/03/13
  5. * Time: 10:17
  6. * To change this template use File | Settings | File Templates.
  7. */
  8. $(function () {
  9. app = {};
  10. app.models = app.models || {};
  11. app.collections = app.collections || {};
  12. /**
  13. * Contains static variables.
  14. */
  15. var CONFIG = (function() {
  16. var private = {
  17. 'CONTEXT': "repository\\content_object\\assessment",
  18. 'TYPE': 'POST'
  19. };
  20. return {
  21. get: function(name) { return private[name]; }
  22. };
  23. })();
  24. app.models.QuestionType = Backbone.Model.extend({
  25. initialize : function() {
  26. }, defaults : {
  27. name : "A questiontype",
  28. className : "questionType",
  29. img : "",
  30. icon : "",
  31. iconSmall : ""
  32. }
  33. });
  34. app.models.Category = Backbone.Model.extend({
  35. initialize : function(){
  36. }, defaults : {
  37. categoryName : "Mijn repository"
  38. }
  39. });
  40. app.collections.Categories = Backbone.Collection.extend({
  41. initialize:function () {
  42. this.assessment_id = app.utilities.get_assessment_id();
  43. },
  44. model : app.models.Category,
  45. urlRoot: "/chamilo",
  46. url: function() {
  47. var xhrOptions = {};
  48. var base = this.urlRoot + "/ajax.php";
  49. return base;
  50. },
  51. sync : function(method, model, options)
  52. {
  53. options.type = CONFIG.get('TYPE');
  54. options.data.context = CONFIG.get('CONTEXT');
  55. return Backbone.sync(method, model, options);
  56. },
  57. fetchCategories : function (options)
  58. {
  59. options || (options = {});
  60. options.data || (options.data = {});
  61. options.data.method = 'get_categories';
  62. return Backbone.Collection.prototype.fetch.call(this, options, {success : function(){app.models.Question.prototype.defaults.category = categoriesCollection.at(0);}});
  63. },
  64. parse : function(response, options)
  65. {
  66. if(response.result_code == 200)
  67. {
  68. response.properties || (response.properties = {});
  69. response.properties.categories || (response.properties.categories = "[]");
  70. return JSON.parse(response.properties.categories);
  71. }
  72. else
  73. {
  74. return JSON.parse("[]");
  75. }
  76. }
  77. });
  78. /**
  79. * Collection of all QuestionTypes.
  80. */
  81. app.collections.QuestionTypes = Backbone.Collection.extend({
  82. initialize : function() {
  83. },
  84. model : app.models.QuestionType,
  85. findByClassName : function (name) {
  86. return this.findWhere({className : name});
  87. }
  88. });
  89. /**
  90. * The superModel Question, every type of question extends from this Model.
  91. */
  92. app.models.Question = Backbone.Model.extend({
  93. initialize:function () {
  94. }, defaults:{
  95. type: new app.models.QuestionType(),
  96. title:"What is the question?",
  97. category: new app.models.Category({id:0}),
  98. description: "...",
  99. weight : 1,
  100. NotEmptyAttributes : ['title', 'description', 'weight']
  101. },
  102. urlRoot: "/chamilo",
  103. url: function() {
  104. var xhrOptions = {};
  105. var base = this.urlRoot + "/ajax.php";
  106. return base;
  107. },
  108. sync : function(method, model, options)
  109. {
  110. options.type = CONFIG.get('TYPE');
  111. options.data.context = CONFIG.get('CONTEXT');
  112. return Backbone.sync(method, model, options);
  113. },
  114. save : function(attributes, options)
  115. {
  116. attributes || (attributes = {});
  117. options || (options = {});
  118. options.data || (options.data = {});
  119. options.data.method = 'save_question';
  120. options.data.class_name = this.get('type').get('className');
  121. var attributeKeys = {};
  122. if(_.isUndefined(this.get('id')))
  123. {
  124. if(!_.isUndefined(this.get('contentId')))
  125. {
  126. if(addedQuestions.where({contentId : this.get('contentId')}).length > 1)
  127. {
  128. attributeKeys = this.attributes;
  129. }
  130. else
  131. {
  132. options.data.content_id = this.get('contentId');
  133. }
  134. }
  135. options.data.question_identifier = 0;
  136. attributeKeys = this.attributes;
  137. options.success =
  138. function(model, response, options)
  139. {
  140. var contentId = parseInt(response.properties.content_id);
  141. if(_.isUndefined(model.get('contentId')))
  142. {
  143. var clonedModel = model.clone();
  144. clonedModel.set('contentId', contentId);
  145. allQuestionsCollection.add(clonedModel);
  146. }
  147. model.set('id', parseInt(response.properties.question_id));
  148. model.set('contentId', contentId);
  149. }
  150. }
  151. else
  152. {
  153. options.data.question_identifier = this.id;
  154. }
  155. for(var key in this.changes)
  156. {
  157. attributeKeys[key] = this.changes[key];
  158. }
  159. var newAttributes = {};
  160. for(var key in attributeKeys)
  161. {
  162. if(typeof this.get(key) == 'object' && !_.isUndefined(this['get_' + key]))
  163. {
  164. newAttributes[key] = this['get_' + key]();
  165. }
  166. else
  167. {
  168. newAttributes[key] = attributeKeys[key];
  169. }
  170. }
  171. options.data.assessment_id = addedQuestions.assessment_id;
  172. options.data.attributes = JSON.stringify(newAttributes);
  173. options.validate = false;
  174. Backbone.Model.prototype.save.call(this, attributes, options);
  175. },
  176. destroy : function(options)
  177. {
  178. if(!_.isUndefined(this.id))
  179. {
  180. options || (options = {});
  181. options.data || (options.data = {});
  182. options.data.method = 'delete_question';
  183. options.data.question_identifier = this.id;
  184. options.data.class_name = this.get('type').get('className');
  185. return Backbone.Model.prototype.destroy.call(this, options);
  186. }
  187. else
  188. {
  189. addedQuestions.remove(this);
  190. }
  191. },
  192. validate : function(attrs) {
  193. var notEmptyAttributes = this.get('NotEmptyAttributes');
  194. var errors = [];
  195. for(var i=0; i < notEmptyAttributes.length; i++)
  196. {
  197. var attribute = notEmptyAttributes[i];
  198. if(!attrs[attribute])
  199. {
  200. errors.push({name : attribute, message: 'Please fill in the ' + attribute +' field.'});
  201. }
  202. }
  203. if(attrs.weight && attrs.weight == 0)
  204. {
  205. errors.push({name : 'weight', message: 'Not allowed to be 0'});
  206. }
  207. return errors.length > 0 ? errors : false;
  208. },
  209. get_category : function()
  210. {
  211. return this.get('category').get('id');
  212. },
  213. set_category : function(id)
  214. {
  215. var category = categoriesCollection.get(id);
  216. if(!category)
  217. {
  218. category = new app.models.Category({id : id});
  219. categoriesCollection.add(category);
  220. }
  221. this.set('category', category);
  222. }
  223. });
  224. app.models.Option = Backbone.Model.extend({
  225. initialize:function () {
  226. }, defaults:{
  227. id:0,
  228. name:"",
  229. feedback:"",
  230. score:"1"
  231. }
  232. });
  233. app.models.MatchOption = app.models.Option.extend({
  234. initialize:function () {
  235. }, defaults:{
  236. match:"A"
  237. }
  238. });
  239. _.extend(app.models.MatchOption.prototype.defaults, app.models.Option.prototype.defaults);
  240. app.models.Match = Backbone.Model.extend({
  241. initialize:function () {
  242. }, defaults:{
  243. id : 0,
  244. match:"A",
  245. name:""
  246. }
  247. });
  248. app.models.NumericOption = Backbone.Model.extend({
  249. initialize:function () {
  250. }, defaults:{
  251. id: 0,
  252. tolerance:"1",
  253. feedback:"",
  254. score:""
  255. }
  256. });
  257. app.models.Item = Backbone.Model.extend({
  258. initialize:function () {
  259. },
  260. defaults:{
  261. id:0,
  262. possibleAnswer:"",
  263. tolerance:"1",
  264. feedback:"",
  265. score:""
  266. }
  267. });
  268. /**
  269. * AssessmentOpenQuestion Model
  270. * @method setquestion_type : sets the type of question based upon question_type_Enums
  271. */
  272. app.models.AssessmentOpenQuestion = app.models.Question.extend(
  273. {
  274. initialize:function () {
  275. },
  276. defaults:{
  277. question_type_Enums : {
  278. OpenQuestion : {value : 1, name: "Open question", type : "OpenQuestion"},
  279. OpenQuestionWithDocument : {value : 2, name: "Open question with document", type: "OpenQuestionWithDocument"},
  280. DocumentQuestion : {value : 3, name: "Document", type: "DocumentQuestion"}},
  281. question_type: "OpenQuestion",
  282. feedback:"",
  283. hint:""
  284. },
  285. set_question_type : function(value) {
  286. for(var typeEnum in this.get('question_type_Enums'))
  287. {
  288. if(this.get('question_type_Enums')[typeEnum].value == value)
  289. {
  290. var enumeration = this.get('question_type_Enums')[typeEnum];
  291. this.set('question_type', enumeration);
  292. break;
  293. }
  294. }
  295. },
  296. get_question_type : function() {
  297. return this.get('question_type').value;
  298. },
  299. get_category : function(){
  300. return this.get('category').get('id');
  301. }
  302. });
  303. app.models.AssessmentOpenQuestion.prototype.defaults.question_type = app.models.AssessmentOpenQuestion.prototype.defaults.question_type_Enums.OpenQuestion;
  304. _.extend(app.models.AssessmentOpenQuestion.prototype.defaults, app.models.Question.prototype.defaults);
  305. app.models.FillInBlanksQuestion = app.models.Question.extend(
  306. {
  307. initialize:function () {
  308. },
  309. defaults:{
  310. spec:"auto-sized text field",
  311. inputShow:"1",
  312. caseSensitive:"1",
  313. posScore:"1",
  314. negScore:"0",
  315. fieldSizeVar:"0",
  316. exercise:""
  317. }
  318. });
  319. _.extend(app.models.FillInBlanksQuestion.prototype.defaults, app.models.Question.prototype.defaults);
  320. /**
  321. * Not implemented yet
  322. */
  323. app.models.HotspotQuestion = app.models.Question.extend(
  324. {
  325. initialize:function () {
  326. },
  327. defaults:{
  328. img:"",
  329. feedback:"",
  330. score:"1"
  331. }
  332. });
  333. _.extend(app.models.HotspotQuestion.prototype.defaults, app.models.Question.prototype.defaults);
  334. app.models.AssessmentMatchingQuestion = app.models.Question.extend(
  335. {
  336. initialize:function () {
  337. },
  338. defaults:{
  339. option:new app.models.MatchOption(),
  340. match:new app.models.Match(),
  341. feedback:"",
  342. score:"1"
  343. }
  344. });
  345. _.extend(app.models.AssessmentMatchingQuestion.prototype.defaults, app.models.Question.prototype.defaults);
  346. /**
  347. * Similar to MatchingQuestion
  348. * Possibility to allow multiple matching answers
  349. * Different interface
  350. */
  351. app.models.AssessmentMatrixQuestion = app.models.AssessmentMatchingQuestion.extend(
  352. {
  353. initialize:function () {
  354. },
  355. defaults:{
  356. multipleMatches:"0"
  357. }
  358. });
  359. _.extend(app.models.AssessmentMatrixQuestion.prototype.defaults, app.models.AssessmentMatchingQuestion.prototype.defaults);
  360. app.models.AssessmentMultipleChoiceQuestion = app.models.Question.extend(
  361. {
  362. initialize:function () {
  363. },
  364. defaults:{
  365. option:new Option(),
  366. feedback:"",
  367. score:"1",
  368. multipleAnswers:"0",
  369. hint:""
  370. }
  371. });
  372. _.extend(app.models.AssessmentMultipleChoiceQuestion.prototype.defaults, app.models.Question.prototype.defaults);
  373. app.models.AssessmentMatchNumericQuestion = app.models.Question.extend(
  374. {
  375. initialize:function () {
  376. },
  377. defaults:{
  378. toleranceType:"absolute",
  379. hint:"",
  380. numericOption:new app.models.NumericOption()
  381. }
  382. });
  383. _.extend(app.models.AssessmentMatchNumericQuestion.prototype.defaults, app.models.Question.prototype.defaults);
  384. app.models.OrderingQuestion = app.models.Question.extend(
  385. {
  386. initialize:function () {
  387. },
  388. defaults:{
  389. item:new app.models.Item()
  390. }
  391. }
  392. );
  393. _.extend(app.models.OrderingQuestion.prototype.defaults, app.models.Question.prototype.defaults);
  394. app.models.AssessmentRatingQuestion = app.models.Question.extend({
  395. initialize:function(){
  396. },
  397. defaults:{
  398. min:"0",
  399. max:"100",
  400. correctValue:"",
  401. hint:""
  402. }
  403. });
  404. _.extend(app.models.AssessmentRatingQuestion.prototype.defaults, app.models.Question.prototype.defaults);
  405. app.models.AssessmentSelectQuestion = app.models.Question.extend({
  406. initialize:function(){
  407. },
  408. defaults:{
  409. option : new Option(),
  410. multipleAnswers:"0",
  411. hint:""
  412. }
  413. });
  414. _.extend(app.models.AssessmentSelectQuestion.prototype.defaults, app.models.Question.prototype.defaults);
  415. app.models.MatchQuestion = app.models.Question.extend({
  416. initialize:function(){
  417. },
  418. defaults:{
  419. option : new app.models.Option(),
  420. hint:""
  421. }
  422. });
  423. _.extend(app.models.MatchQuestion.prototype.defaults, app.models.Question.prototype.defaults);
  424. /**
  425. * Same name as above, think of another name
  426. */
  427. app.models.AssessmentMatchTextQuestion = app.models.MatchQuestion.extend
  428. ({
  429. initialize:function(){
  430. },
  431. defaults:{
  432. wildcard:"1",
  433. caseInsensitive:"1"
  434. }
  435. });
  436. _.extend(app.models.AssessmentMatchTextQuestion.prototype.defaults, app.models.MatchQuestion.prototype.defaults);
  437. app.collections.Questions = Backbone.Collection.extend({
  438. initialize:function () {
  439. this.assessment_id = app.utilities.get_assessment_id();
  440. },
  441. model : app.models.Question,
  442. urlRoot: "/chamilo",
  443. url: function() {
  444. var xhrOptions = {};
  445. var base = this.urlRoot + "/ajax.php";
  446. return base;
  447. },
  448. comparator : function(question)
  449. {
  450. return question.get('display_order');
  451. },
  452. addQuestion : function(question, index)
  453. {
  454. if(index)
  455. {
  456. if(index < this.length - 1)
  457. {
  458. for(var i = index; i < this.length; i++)
  459. {
  460. var movedQuestion = this.at(i);
  461. movedQuestion.set('display_order', i + 2);
  462. movedQuestion.save();
  463. }
  464. }
  465. }
  466. else
  467. {
  468. index = this.length;
  469. }
  470. question.set('display_order', index + 1);
  471. this.add(question, {at : index});
  472. },
  473. updateDisplayOrder : function()
  474. {
  475. for(var i=0; i < this.length; i++)
  476. {
  477. if(this.at(i).get('display_order') !== (i+1))
  478. {
  479. this.at(i).set('display_order', i+1);
  480. this.at(i).changes['display_order'] = this.at(i).get('display_order');
  481. this.at(i).save();
  482. }
  483. }
  484. },
  485. sync : function(method, model, options)
  486. {
  487. options || (options={});
  488. options.type = CONFIG.get('TYPE');
  489. options.data.context = CONFIG.get('CONTEXT');
  490. return Backbone.sync(method, model, options);
  491. },
  492. parse : function(response, options)
  493. {
  494. if(response.result_code == 200)
  495. {
  496. response.properties || (response.properties = {});
  497. response.properties.questions || (response.properties.questions = "[]");
  498. return JSON.parse(response.properties.questions);
  499. }
  500. else
  501. {
  502. return JSON.parse("[]");
  503. }
  504. },
  505. fetch : function (options)
  506. {
  507. options || (options = {});
  508. options.data || (options.data = {});
  509. options.data.method = 'get_questions';
  510. options.data.assessment_identifier = this.assessment_id;
  511. options.success = function(collection, response, options) {
  512. var questions = collection.parse(response, options);
  513. collection.reset();
  514. for(var i=0; i <questions.length; i++)
  515. {
  516. var question = addedQuestions.create_question(questions[i]);
  517. collection.add(question);
  518. }
  519. };
  520. return Backbone.Collection.prototype.fetch.call(this, options);
  521. },
  522. fetchAll : function (options)
  523. {
  524. options || (options = {});
  525. options.data || (options.data = {});
  526. options.data.method = 'get_all_questions';
  527. options.success = function(collection, response, options) {
  528. var questions = collection.parse(response, options);
  529. collection.reset();
  530. for(var i=0; i <questions.length; i++)
  531. {
  532. var question = allQuestionsCollection.create_question(questions[i]);
  533. if(question)
  534. {
  535. collection.add(question, {silent : true});
  536. }
  537. }
  538. allQuestionsView.listenTo(collection,'change reset add remove', allQuestionsView.render);
  539. allQuestionsView.render();
  540. //makeDraggable();
  541. };
  542. return Backbone.Collection.prototype.fetch.call(this, options);
  543. },
  544. create_question : function(attributes)
  545. {
  546. if(!_.isUndefined(attributes.className))
  547. {
  548. if(!_.isUndefined(app.models[attributes.className]))
  549. {
  550. if(!_.isUndefined(attributes.description))
  551. {
  552. attributes.description = _.unescape(attributes.description);
  553. }
  554. var question = new app.models[attributes.className]();
  555. for(var key in attributes)
  556. {
  557. if(question.get(key) && typeof question.get(key) == 'object' && !_.isUndefined(question['set_' + key]))
  558. {
  559. question['set_'+key](attributes[key]);
  560. }
  561. else
  562. {
  563. if(isNaN(attributes[key]) || _.isEmpty(attributes[key]))
  564. {
  565. question.set(key, attributes[key]);
  566. }
  567. else
  568. {
  569. question.set(key, parseInt(attributes[key]));
  570. }
  571. }
  572. }
  573. question.set('type', questionTypeCollection.findByClassName(attributes.className));
  574. question.unset('className');
  575. question.changes = {};
  576. if(_.isUndefined(attributes.title))
  577. question.set('title', question.get('type').get('name'));
  578. return question;
  579. }
  580. }
  581. },
  582. destroy_questions : function(questions, options)
  583. {
  584. options || (options = {});
  585. options.data || (options.data = {});
  586. options.data.method = 'delete_questions';
  587. var questionsAjax = new Array();
  588. var j = 0;
  589. for(var i=0; i < questions.length; i++)
  590. {
  591. var question = questions.at(i);
  592. if(!_.isUndefined(question.get('id')))
  593. {
  594. questionsAjax[j] = new Array(2);
  595. questionsAjax[j][0] = question.get('id');
  596. questionsAjax[j][1] = question.get('type').get('className');
  597. j++;
  598. }
  599. this.remove(question);
  600. }
  601. if(questionsAjax.length > 0)
  602. {
  603. options.data.questions = JSON.stringify(questionsAjax);
  604. return this.sync('delete',this, options);
  605. }
  606. },
  607. getTotalWeight : function()
  608. {
  609. var total = 0;
  610. for(var i=0; i < this.length; i++)
  611. {
  612. total += this.at(i).get('weight');
  613. }
  614. return total;
  615. },
  616. divideTotalWeight : function(totalWeight)
  617. {
  618. var modulus = totalWeight % this.length;
  619. var weight = (totalWeight - modulus) / this.length;
  620. for(var i = 0; i < this.length; i++)
  621. {
  622. var question = this.at(i);
  623. if(question.get('weight') != weight)
  624. {
  625. question.changes || (question.changes = {});
  626. question.set('weight', weight);
  627. question.changes.weight = weight;
  628. question.save();
  629. }
  630. }
  631. }
  632. });
  633. });