From 6c2c85f28ad4a8b342f3326854202f2aff633f9f Mon Sep 17 00:00:00 2001 From: c0cky Date: Wed, 12 Nov 2014 14:46:50 -0500 Subject: [PATCH 01/58] onomy stuff --- media/js/app/taxonomy/taxonomy.js | 367 +++++++++++++++++++----------- 1 file changed, 239 insertions(+), 128 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index f89e1a4cc..367053658 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -1,19 +1,19 @@ (function (jQuery) { - + Term = Backbone.Model.extend({ urlRoot: '/api/term/', - toTemplate: function() { + toTemplate: function () { return _(this.attributes).clone(); - } - }); + } + }); var TermList = Backbone.Collection.extend({ urlRoot: '/api/term/', model: Term, - comparator: function(obj) { + comparator: function (obj) { return obj.get("display_name"); - }, - toTemplate: function() { + }, + toTemplate: function () { var a = []; this.forEach(function (item) { a.push(item.toTemplate()); @@ -25,32 +25,32 @@ return this.get(internalId); } }); - + Vocabulary = Backbone.Model.extend({ urlRoot: '/api/vocabulary/', - parse: function(response) { + parse: function (response) { if (response) { response.term_set = new TermList(response.term_set); } return response; - }, - toTemplate: function() { + }, + toTemplate: function () { var json = _(this.attributes).clone(); json.term_set = this.get('term_set').toTemplate(); return json; - } + } }); - + var VocabularyList = Backbone.Collection.extend({ urlRoot: '/api/vocabulary/', model: Vocabulary, - comparator: function(obj) { + comparator: function (obj) { return obj.get("display_name"); }, - parse: function(response) { + parse: function (response) { return response.objects || response; }, - toTemplate: function() { + toTemplate: function () { var a = []; this.forEach(function (item) { a.push(item.toTemplate()); @@ -62,12 +62,12 @@ return this.get(internalId); } }); - + window.VocabularyListView = Backbone.View.extend({ events: { 'click a.delete-vocabulary': 'deleteVocabulary', 'click a.create-vocabulary-open': 'toggleCreateVocabulary', - 'click a.create-vocabulary-close': 'toggleCreateVocabulary', + 'click a.create-vocabulary-close': 'toggleCreateVocabulary', 'click a.edit-vocabulary-open': 'toggleEditVocabulary', 'click a.edit-vocabulary-close': 'toggleEditVocabulary', 'click a.create-vocabulary-submit': 'createVocabulary', @@ -80,42 +80,45 @@ 'keypress input[name="term_name"]': 'keypressTermName', 'click a.edit-term-submit': 'updateTerm', 'click a.edit-term-open': 'showEditTerm', - 'click a.edit-term-close': 'hideEditTerm', - 'click a.delete-term': 'deleteTerm' + 'click a.edit-term-close': 'hideEditTerm', + 'click a.delete-term': 'deleteTerm', + 'click a.onomy-terms-submit': 'createOnomyVocabulary' }, - initialize: function(options) { + initialize: function (options) { _.bindAll(this, - "render", - "createVocabulary", - "updateVocabulary", - "deleteVocabulary", - "createTerm", - "keypressTermName", - "updateTerm", - "deleteTerm", - "activateTab"); - + "render", + "createVocabulary", + "updateVocabulary", + "deleteVocabulary", + "createTerm", + "keypressTermName", + "updateTerm", + "deleteTerm", + //"createOnomyVocabulary", + "activateTab"); + this.context = options; this.vocabularyTemplate = _.template(jQuery("#vocabulary-template").html()); - + this.collection = new VocabularyList(); this.collection.on("add", this.render); this.collection.on("remove", this.render); - this.collection.on("reset", this.render); + this.collection.on("reset", this.render); this.collection.fetch(); + }, - activateTab: function(evt, ui) { + activateTab: function (evt, ui) { jQuery(ui.oldTab).find("div.vocabulary-edit, div.vocabulary-create").hide(); jQuery(ui.oldTab).find("div.vocabulary-display").show(); var vid = jQuery(ui.newTab).data("id"); this.selected = this.collection.getByDataId(vid); }, - render: function() { + render: function () { this.context.vocabularies = this.collection.toTemplate(); var markup = this.vocabularyTemplate(this.context); jQuery(this.el).html(markup); - + var elt = jQuery(this.el).find("div.vocabularies"); jQuery(elt).tabs({ 'activate': this.activateTab @@ -123,44 +126,44 @@ // remove the default tabs key processing jQuery(elt).find('li').off('keydown'); jQuery(elt).addClass("ui-tabs-vertical ui-helper-clearfix"); - jQuery(this.el).find("div.vocabularies li").removeClass("ui-corner-top").addClass( "ui-corner-left"); - + jQuery(this.el).find("div.vocabularies li").removeClass("ui-corner-top").addClass("ui-corner-left"); + if (this.selected !== undefined) { var idx = this.collection.indexOf(this.selected); jQuery(elt).tabs("option", "active", idx); } else { this.selected = this.collection.at(0); - } + } }, - toggleCreateVocabulary: function(evt) { + toggleCreateVocabulary: function (evt) { evt.preventDefault(); var parent = jQuery(evt.currentTarget).parents("li")[0]; jQuery(parent).find("div.vocabulary-display, div.vocabulary-create").toggle(); - return false; + return false; }, - toggleEditVocabulary: function(evt) { + toggleEditVocabulary: function (evt) { evt.preventDefault(); var parent = jQuery(evt.currentTarget).parents("li")[0]; jQuery(parent).find("div.vocabulary-display, div.vocabulary-edit").toggle(); jQuery(parent).find("input[name='display_name']").focus(); - return false; + return false; }, - createVocabulary: function(evt) { + createVocabulary: function (evt) { evt.preventDefault(); var self = this; var parent = jQuery(evt.currentTarget).parent(); var elt = jQuery(parent).find('input[name="display_name"]')[0]; - if (jQuery(elt).hasClass("default")) { + if (jQuery(elt).hasClass("default")) { showMessage("Please name your concept.", undefined, "Error"); return; } - - var display_name = jQuery(elt).attr("value").trim(); + + var display_name = jQuery(elt).attr("value").trim(); if (display_name === undefined || display_name.length < 1) { showMessage("Please name your concept.", undefined, "Error"); return; } - + var v = new Vocabulary({ 'display_name': display_name, 'content_type_id': jQuery(parent).find('input[name="content_type_id"]').attr("value"), @@ -168,42 +171,42 @@ 'term_set': undefined }); v.save({}, { - success: function() { + success: function () { self.selected = v; self.collection.add(v); }, - error: function(model, response) { + error: function (model, response) { var responseText = jQuery.parseJSON(response.responseText); showMessage(responseText.vocabulary.error_message, undefined, "Error"); } }); return false; }, - updateVocabulary: function(evt) { + updateVocabulary: function (evt) { evt.preventDefault(); var self = this; var parent = jQuery(evt.currentTarget).parent(); - + var elt = jQuery(parent).find('input[name="display_name"]')[0]; - if (jQuery(elt).hasClass("default")) { + if (jQuery(elt).hasClass("default")) { showMessage("Please name your concept.", undefined, "Error"); return; } - - var display_name = jQuery(elt).attr("value").trim(); + + var display_name = jQuery(elt).attr("value").trim(); if (display_name === undefined || display_name.length < 1) { showMessage("Please name your concept.", undefined, "Error"); return; } - + var id = jQuery(parent).find('input[name="vocabulary_id"]').attr("value").trim(); var v = this.collection.getByDataId(id); if (v.get('display_name') !== 'display_name') { v.save({'display_name': display_name}, { - success: function() { + success: function () { self.render(); }, - error: function(model, response) { + error: function (model, response) { var responseText = jQuery.parseJSON(response.responseText); showMessage(responseText.vocabulary.error_message, undefined, "Error"); } @@ -211,32 +214,32 @@ } return false; }, - deleteVocabulary: function(evt) { + deleteVocabulary: function (evt) { var self = this; - + var id = jQuery(evt.currentTarget).attr('href'); var vocabulary = self.collection.getByDataId(id); var msg = "Deleting " + vocabulary.get('display_name') + "" + - " removes its terms" + + " removes its terms" + " from all associated course items."; - + var dom = jQuery(evt.currentTarget).parents('li'); jQuery(dom).addClass('about-to-delete'); - - jQuery("#dialog-confirm").html(msg); + + jQuery("#dialog-confirm").html(msg); jQuery("#dialog-confirm").dialog({ resizable: false, modal: true, title: "Are you sure?", - close: function(event, ui) { + close: function (event, ui) { jQuery(dom).removeClass('about-to-delete'); }, buttons: { - "Cancel": function() { - jQuery(this).dialog("close"); + "Cancel": function () { + jQuery(this).dialog("close"); }, - "OK": function() { + "OK": function () { jQuery(this).dialog("close"); self.collection.remove(vocabulary); vocabulary.destroy(); @@ -245,76 +248,77 @@ }); return false; }, - focusVocabularyName: function(evt) { + focusVocabularyName: function (evt) { if (jQuery(evt.currentTarget).hasClass("default")) { jQuery(evt.currentTarget).removeClass("default"); jQuery(evt.currentTarget).attr("value", ""); } }, - blurVocabularyName: function(evt) { + blurVocabularyName: function (evt) { if (jQuery(evt.currentTarget).attr("value") === '') { jQuery(evt.currentTarget).addClass("default"); jQuery(evt.currentTarget).attr("value", "Type concept name here"); } }, - showEditTerm: function(evt) { + showEditTerm: function (evt) { evt.preventDefault(); var container = jQuery(evt.currentTarget).parents("div.terms"); jQuery(container).find("div.term-display").show(); jQuery(container).find("div.term-edit").hide(); - + var parent = jQuery(evt.currentTarget).parents("div.term")[0]; jQuery(parent).find("div.term-display").hide(); jQuery(parent).find("div.term-edit").show(); jQuery(parent).find("input[name='display_name']").focus(); - return false; - }, - hideEditTerm: function(evt) { + return false; + }, + hideEditTerm: function (evt) { evt.preventDefault(); var parent = jQuery(evt.currentTarget).parents("div.term")[0]; jQuery(parent).find("div.term-display").show(); jQuery(parent).find("div.term-edit").hide(); - return false; - }, - keypressTermName: function(evt) { + return false; + }, + keypressTermName: function (evt) { var self = this; if (evt.which == 13) { - evt.preventDefault(); + evt.preventDefault(); jQuery(evt.currentTarget).next().click(); } }, - createTerm: function(evt) { + createTerm: function (evt) { evt.preventDefault(); var self = this; - var elt = jQuery(evt.currentTarget).prev(); - if (jQuery(elt).hasClass("default")) { - showMessage("Please enter a term name.", undefined, "Error"); + var et = jQuery(evt.currentTarget).prev(); + if (jQuery(et).hasClass("default")) { + //when both fields are left blank on submit + showMessage("Please enter a term name", undefined, "Error"); return; - } - - var display_name = jQuery(elt).attr("value").trim(); - if (display_name === undefined || display_name.length < 1) { - showMessage("Please enter a term name.", undefined, "Error"); - return; - } - - var t = new Term({ - 'display_name': display_name, - 'vocabulary_id': this.selected.get('id') - }); - t.save({}, { - success: function() { - self.selected.get('term_set').add(t); - self.render(); - }, - error: function(model, response) { - var responseText = jQuery.parseJSON(response.responseText); - showMessage(responseText.term.error_message, undefined, "Error"); + } else { + //if you want to create a term from user input + var display_name = jQuery(et).attr("value").trim(); + if (display_name === undefined || display_name.length < 1) { + showMessage("Please enter a term name.", undefined, "Error"); + return; } - }); - return false; + var t = new Term({ + 'display_name': display_name, + 'vocabulary_id': this.selected.get('id') + }); + t.save({}, { + success: function () { + self.selected.get('term_set').add(t); + self.render(); + }, + error: function (model, response) { + var responseText = jQuery.parseJSON(response.responseText); + showMessage(responseText.term.error_message, undefined, "Error"); + } + }); + return false; + } }, - updateTerm: function(evt) { + updateTerm: function (evt) { evt.preventDefault(); var self = this; var elt = jQuery(evt.currentTarget).prevAll("input[type='text']"); @@ -322,57 +326,57 @@ showMessage("Please enter a term name.", undefined, "Error"); return; } - - var display_name = jQuery(elt).attr("value").trim(); + + var display_name = jQuery(elt).attr("value").trim(); if (display_name === undefined || display_name.length < 1) { showMessage("Please enter a term name.", undefined, "Error"); return; } - + var tid = jQuery(evt.currentTarget).data('id'); var term = this.selected.get("term_set").getByDataId(tid); - + if (term.get('display_name') !== 'display_name') { term.set('display_name', display_name); term.save({}, { - success: function() { + success: function () { self.render(); }, - error: function(model, response) { + error: function (model, response) { var responseText = jQuery.parseJSON(response.responseText); showMessage(responseText.term.error_message, undefined, "Error"); } }); } - return false; + return false; }, - deleteTerm: function(evt) { + deleteTerm: function (evt) { evt.preventDefault(); var self = this; - + var id = jQuery(evt.currentTarget).attr('href'); var term = self.selected.get('term_set').getByDataId(id); var msg = "Deleting the term " + term.get('display_name') + "" + - " removes this term" + + " removes this term" + " from all associated course items."; - + var dom = jQuery(evt.currentTarget).parents('div.term'); jQuery(dom).addClass('about-to-delete'); - - jQuery("#dialog-confirm").html(msg); + + jQuery("#dialog-confirm").html(msg); jQuery("#dialog-confirm").dialog({ resizable: false, modal: true, title: "Are you sure?", - close: function(event, ui) { + close: function (event, ui) { jQuery(dom).removeClass('about-to-delete'); }, buttons: { - "Cancel": function() { - jQuery(this).dialog("close"); + "Cancel": function () { + jQuery(this).dialog("close"); }, - "OK": function() { + "OK": function () { jQuery(this).dialog("close"); self.selected.get('term_set').remove(term); term.destroy(); @@ -382,18 +386,125 @@ }); return false; }, - focusTermName: function(evt) { + focusTermName: function (evt) { if (jQuery(evt.currentTarget).hasClass("default")) { jQuery(evt.currentTarget).removeClass("default"); jQuery(evt.currentTarget).attr("value", ""); } }, - blurTermName: function(evt) { + blurTermName: function (evt) { if (jQuery(evt.currentTarget).attr("value") === '') { jQuery(evt.currentTarget).addClass("default"); jQuery(evt.currentTarget).attr("value", "Type new term name here"); } - } + } + , + createOnomyVocabulary: function (evt) { + evt.preventDefault(); + var et = jQuery(evt.currentTarget).prev(); + + var self = this; + var vocabulary_id = this.selected.get('id'); + //'http://www.corsproxy.com/' + + jQuery.get(jQuery(et).attr("value").trim(), + function (data) { + x = JSON.parse(data); + //change 4 to x.terms.length after testing. + for (var i = 0; i < 2; i++) { + var pL = x.terms[i]['rdfs:parentLabel'].trim(); + var display_name = x.terms[i]['rdfs:label'].trim(); + //demorgans law + if (!(pL === undefined || pL.length < 1)) { + var p = self.context.vocabularies; + var obj = jQuery.grep(p, function (e) { + return e.display_name == pL; + }); + console.log(obj); + var v; + if (obj.length == 0) { + console.log("did not find concept"); + //we create a new vocabulary or "concept" if the parent is not created so we can add + // the term to the vocabulary that previously didnt exist. + console.log("creating vocab"); + v = new Vocabulary({ + 'display_name': pL, + 'content_type_id': 14, + 'object_id': 1, + 'term_set': undefined + }); + + v.save({}, { + success: function () { + //imma comment this out for now because I'm not trying to make the one I just created the + //current selected "concept"/vocabulary. + self.selected = v; + self.collection.add(v); + }, + error: function (model, response) { + var responseText = jQuery.parseJSON(response.responseText); + showMessage(responseText.vocabulary.error_message, undefined, "Error"); + } + }); + v = v['attributes']; + console.log(v); + console.log("moving on"); + } + else //if obj != undefined and we can get the vocab by underscore find method + { + v = obj[0]; + self.selected = v; + } + console.log(v.id); + var t; + t = new Term({ + 'display_name': display_name, + 'vocabulary_id': v.id + }); + t.save({}, { + success: function (it) { + v['term_set'].push(new Term({ + 'display_name': it.attributes['display_name'], + 'vocabulary_id': it.attributes['vocabulary_id'] + })); + self.render(); + }, + error: function (model, response) { + var responseText = jQuery.parseJSON(response.responseText); + showMessage(responseText.term.error_message, + undefined, "Error"); + } + }); + + } else { + + if (display_name === undefined || display_name.length < 1) { + continue; + } + var t; + t = new Term({ + 'display_name': display_name, + 'vocabulary_id': vocabulary_id + }); + t.save({}, { + wait: true, + success: function (it) { + self.selected.get('term_set').add(new Term({ + 'display_name': it.attributes['display_name'], + 'vocabulary_id': it.attributes['vocabulary_id'] + })); + self.render(); + }, + error: function (model, response) { + var responseText = jQuery.ParseJson(response.responseText); + showMessage(responseText.term.error_message, + undefined, "Error"); + } + }); + } + } + }); + } + + }); - -}(jQuery)); \ No newline at end of file +}(jQuery)); From 83f67aa1e17777681784241ec5c8db5bdfc2bdd8 Mon Sep 17 00:00:00 2001 From: c0cky Date: Wed, 12 Nov 2014 14:47:53 -0500 Subject: [PATCH 02/58] taxonomy template for n concepts --- mediathread/templates/taxonomy/taxonomy.html | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/mediathread/templates/taxonomy/taxonomy.html b/mediathread/templates/taxonomy/taxonomy.html index 102427734..643808ad6 100644 --- a/mediathread/templates/taxonomy/taxonomy.html +++ b/mediathread/templates/taxonomy/taxonomy.html @@ -39,7 +39,7 @@ <% } %> - <% if (vocabularies.length < 3) { %> + <% if (vocabularies.length < 10) { %>
  • @@ -64,7 +64,17 @@ <% for (var i=0; i < vocabularies.length; i++) { %>
    -

    <%=vocabularies[i].display_name%> Concept

    +

    + + refresh +

    + +

    <%=vocabularies[i].display_name%> Concept

    Terms

    From abda84941d8d387b2525bdca33d4114400ddd834 Mon Sep 17 00:00:00 2001 From: c0cky Date: Fri, 14 Nov 2014 17:00:18 -0500 Subject: [PATCH 03/58] parenting 101 --- media/js/app/taxonomy/taxonomy.js | 165 ++++++++++++++++++------------ 1 file changed, 102 insertions(+), 63 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 367053658..2b2034007 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -41,6 +41,38 @@ } }); + function proxyAjaxEvent(event, options, dit) { + var eventCallback = options[event]; + options[event] = function() { + // check if callback for event exists and if so pass on request + if (eventCallback) { eventCallback(arguments) } + dit.processQueue(); // move onto next save request in the queue + } + }; + Backbone.Model.prototype._save = Backbone.Model.prototype.save; + Backbone.Model.prototype.save = function( attrs, options ) { + if (!options) { options = {}; } + if (this.saving) { + this.saveQueue = this.saveQueue || new Array(); + this.saveQueue.push({ attrs: _.extend({}, this.attributes, attrs), options: options }); + } else { + this.saving = true; + proxyAjaxEvent('success', options, this); + proxyAjaxEvent('error', options, this); + Backbone.Model.prototype._save.call( this, attrs, options ); + } + }; + Backbone.Model.prototype.processQueue = function() { + if (this.saveQueue && this.saveQueue.length) { + var saveArgs = this.saveQueue.shift(); + proxyAjaxEvent('success', saveArgs.options, this); + proxyAjaxEvent('error', saveArgs.options, this); + Backbone.Model.prototype._save.call( this, saveArgs.attrs, saveArgs.options ); + } else { + this.saving = false; + } + }; + var VocabularyList = Backbone.Collection.extend({ urlRoot: '/api/vocabulary/', model: Vocabulary, @@ -294,7 +326,7 @@ //when both fields are left blank on submit showMessage("Please enter a term name", undefined, "Error"); return; - } else { + } //if you want to create a term from user input var display_name = jQuery(et).attr("value").trim(); if (display_name === undefined || display_name.length < 1) { @@ -316,7 +348,7 @@ } }); return false; - } + }, updateTerm: function (evt) { evt.preventDefault(); @@ -410,82 +442,81 @@ function (data) { x = JSON.parse(data); //change 4 to x.terms.length after testing. - for (var i = 0; i < 2; i++) { + var MAX = 2; + var parents = []; + for (var i = 0; i < MAX; i++) { var pL = x.terms[i]['rdfs:parentLabel'].trim(); - var display_name = x.terms[i]['rdfs:label'].trim(); + var display = x.terms[i]['rdfs:label'].trim(); + + console.log(pL); + console.log(display); //demorgans law if (!(pL === undefined || pL.length < 1)) { - var p = self.context.vocabularies; - var obj = jQuery.grep(p, function (e) { - return e.display_name == pL; - }); - console.log(obj); - var v; - if (obj.length == 0) { - console.log("did not find concept"); - //we create a new vocabulary or "concept" if the parent is not created so we can add - // the term to the vocabulary that previously didnt exist. - console.log("creating vocab"); - v = new Vocabulary({ - 'display_name': pL, - 'content_type_id': 14, - 'object_id': 1, - 'term_set': undefined - }); - - v.save({}, { - success: function () { - //imma comment this out for now because I'm not trying to make the one I just created the - //current selected "concept"/vocabulary. - self.selected = v; - self.collection.add(v); - }, - error: function (model, response) { - var responseText = jQuery.parseJSON(response.responseText); - showMessage(responseText.vocabulary.error_message, undefined, "Error"); - } - }); - v = v['attributes']; - console.log(v); - console.log("moving on"); + var search = undefined; + parents.forEach(function(a){if(a.display_name == pL){search = a;}}); + console.log(search); + if(search === undefined) + { + //create the Vocabulary + temp = {'display_name': pL, 'term_set':[]}; + parents.push(temp); + console.log(temp); + parents[parents.indexOf(temp)].term_set.push({'display_name': display}); } - else //if obj != undefined and we can get the vocab by underscore find method + else { - v = obj[0]; - self.selected = v; + //add the term to the Vocabulary in parents + v = temp; + parents[parents.indexOf(v)].term_set.push({'display_name': display}); } - console.log(v.id); - var t; - t = new Term({ - 'display_name': display_name, - 'vocabulary_id': v.id - }); - t.save({}, { - success: function (it) { - v['term_set'].push(new Term({ - 'display_name': it.attributes['display_name'], - 'vocabulary_id': it.attributes['vocabulary_id'] - })); - self.render(); - }, - error: function (model, response) { - var responseText = jQuery.parseJSON(response.responseText); - showMessage(responseText.term.error_message, - undefined, "Error"); + if(i == MAX -1) + { + var tempV; + for (var j = 0; j < parents.length; j++) + { + tempV = new Vocabulary({ + 'display_name': parents[j].display_name, + 'content_type_id': 14, + 'object_id': 1, + 'term_set': undefined + }); + tempV._save({},{ + success: function(it){ + self.selected = it; + self.collection.add(it); + console.log('it'); + console.log(it); + for (var z = 0; z < parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set.length; z ++) + { + tempT = new Term({ + 'display_name':parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set[z].display_name, + 'vocabulary_id': it.attributes['id'] + }); + tempT._save({},{ + success: function (itT) { + self.selected.get('term_set').add(new Term({ + 'display_name': itT.attributes['display_name'], + 'vocabulary_id': itT.attributes['vocabulary_id'] + })); + self.render(); + } + }); + } + }}); } - }); + } } else { - if (display_name === undefined || display_name.length < 1) { + if (display === undefined || display.length < 1) { continue; } var t; t = new Term({ - 'display_name': display_name, + 'display_name': display, 'vocabulary_id': vocabulary_id }); - t.save({}, { + t._save({}, { wait: true, success: function (it) { self.selected.get('term_set').add(new Term({ @@ -495,7 +526,7 @@ self.render(); }, error: function (model, response) { - var responseText = jQuery.ParseJson(response.responseText); + var responseText = jQuery.parseJSON(response.responseText); showMessage(responseText.term.error_message, undefined, "Error"); } @@ -507,4 +538,12 @@ }); + function findUtil(array, thing){ + return jQuery.grep(array, function(item){ + return item.display_name == thing; + }); + }; + }(jQuery)); + + From b65703200f05ec2879039c9d25c4b2c6a4915b6a Mon Sep 17 00:00:00 2001 From: c0cky Date: Fri, 14 Nov 2014 18:49:16 -0500 Subject: [PATCH 04/58] parent update --- media/js/app/taxonomy/taxonomy.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 2b2034007..433f371eb 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -442,7 +442,7 @@ function (data) { x = JSON.parse(data); //change 4 to x.terms.length after testing. - var MAX = 2; + var MAX = x.terms.length; var parents = []; for (var i = 0; i < MAX; i++) { var pL = x.terms[i]['rdfs:parentLabel'].trim(); @@ -454,24 +454,23 @@ if (!(pL === undefined || pL.length < 1)) { var search = undefined; parents.forEach(function(a){if(a.display_name == pL){search = a;}}); - console.log(search); if(search === undefined) { //create the Vocabulary temp = {'display_name': pL, 'term_set':[]}; parents.push(temp); - console.log(temp); parents[parents.indexOf(temp)].term_set.push({'display_name': display}); } else { //add the term to the Vocabulary in parents - v = temp; + v = search; parents[parents.indexOf(v)].term_set.push({'display_name': display}); } if(i == MAX -1) { var tempV; + console.log(parents); for (var j = 0; j < parents.length; j++) { tempV = new Vocabulary({ From 29e0e066866257e4e2fdefeb3068577ce4d7fb5a Mon Sep 17 00:00:00 2001 From: c0cky Date: Mon, 17 Nov 2014 00:40:26 -0500 Subject: [PATCH 05/58] just missing refresh --- media/js/app/taxonomy/taxonomy.js | 70 +++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 22 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 433f371eb..8b4e72b51 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -333,6 +333,7 @@ showMessage("Please enter a term name.", undefined, "Error"); return; } + var t = new Term({ 'display_name': display_name, 'vocabulary_id': this.selected.get('id') @@ -438,11 +439,13 @@ var self = this; var vocabulary_id = this.selected.get('id'); //'http://www.corsproxy.com/' + - jQuery.get(jQuery(et).attr("value").trim(), + //this should be sanitized in the future. + onomyURL = jQuery(et).attr("value").trim(); + jQuery.get(onomyURL, function (data) { x = JSON.parse(data); //change 4 to x.terms.length after testing. - var MAX = x.terms.length; + var MAX = 1//x.terms.length; var parents = []; for (var i = 0; i < MAX; i++) { var pL = x.terms[i]['rdfs:parentLabel'].trim(); @@ -457,7 +460,7 @@ if(search === undefined) { //create the Vocabulary - temp = {'display_name': pL, 'term_set':[]}; + temp = {'display_name': pL, 'term_set':[], 'self':undefined}; parents.push(temp); parents[parents.indexOf(temp)].term_set.push({'display_name': display}); } @@ -470,21 +473,45 @@ if(i == MAX -1) { var tempV; - console.log(parents); for (var j = 0; j < parents.length; j++) { - tempV = new Vocabulary({ - 'display_name': parents[j].display_name, - 'content_type_id': 14, - 'object_id': 1, - 'term_set': undefined - }); - tempV._save({},{ - success: function(it){ - self.selected = it; - self.collection.add(it); - console.log('it'); - console.log(it); + model_search = _.find(self.collection.models, function(model){return model.attributes.display_name == parents[j].display_name}); + if(model_search === undefined) + { + //if we cant find the vocab in the collection we create a new one. + tempV = new Vocabulary({ + 'display_name': parents[j].display_name, + 'content_type_id': 14, + 'object_id': 1, + 'term_set': undefined, + 'onomy_url': onomyURL + }); + tempV._save({},{ + success: function(it){ + self.selected = it; + self.collection.add(it); + console.log('it'); + console.log(it); + for (var z = 0; z < parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set.length; z ++) + { + tempT = new Term({ + 'display_name':parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set[z].display_name, + 'vocabulary_id': it.attributes['id'] + }); + tempT._save({},{ + success: function (itT) { + self.selected.get('term_set').add(new Term({ + 'display_name': itT.attributes['display_name'], + 'vocabulary_id': itT.attributes['vocabulary_id'] + })); + self.render(); + } + }); + } + }}); + } else { + //we do find the model. we just add the term to it. + self.selected = model_search; for (var z = 0; z < parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set.length; z ++) { tempT = new Term({ @@ -501,7 +528,11 @@ } }); } - }}); + + } + + + } } @@ -523,11 +554,6 @@ 'vocabulary_id': it.attributes['vocabulary_id'] })); self.render(); - }, - error: function (model, response) { - var responseText = jQuery.parseJSON(response.responseText); - showMessage(responseText.term.error_message, - undefined, "Error"); } }); } From 4badca9480927062880e71207095c90a14a28204 Mon Sep 17 00:00:00 2001 From: c0cky Date: Mon, 17 Nov 2014 01:28:33 -0500 Subject: [PATCH 06/58] updated parenting adds --- media/js/app/taxonomy/taxonomy.js | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 8b4e72b51..6bf19103e 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -445,7 +445,7 @@ function (data) { x = JSON.parse(data); //change 4 to x.terms.length after testing. - var MAX = 1//x.terms.length; + var MAX = x.terms.length; var parents = []; for (var i = 0; i < MAX; i++) { var pL = x.terms[i]['rdfs:parentLabel'].trim(); @@ -489,6 +489,8 @@ tempV._save({},{ success: function(it){ self.selected = it; + console.log(parents); + parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self = self.selected; self.collection.add(it); console.log('it'); console.log(it); @@ -500,7 +502,7 @@ }); tempT._save({},{ success: function (itT) { - self.selected.get('term_set').add(new Term({ + parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self.get('term_set').add(new Term({ 'display_name': itT.attributes['display_name'], 'vocabulary_id': itT.attributes['vocabulary_id'] })); @@ -512,11 +514,11 @@ } else { //we do find the model. we just add the term to it. self.selected = model_search; - for (var z = 0; z < parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set.length; z ++) + for (var z = 0; z < parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set.length; z ++) { tempT = new Term({ - 'display_name':parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set[z].display_name, - 'vocabulary_id': it.attributes['id'] + 'display_name':parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set[z].display_name, + 'vocabulary_id': model_search.attributes['id'] }); tempT._save({},{ success: function (itT) { @@ -559,6 +561,7 @@ } } }); + //location.reload(); } From a5cc57e9b1b870ac6d83f5ae8a32968a1f3b6ffb Mon Sep 17 00:00:00 2001 From: c0cky Date: Tue, 18 Nov 2014 21:34:00 -0500 Subject: [PATCH 07/58] backup --- media/js/app/taxonomy/taxonomy.js | 32 +++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 6bf19103e..19b578674 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -29,9 +29,12 @@ Vocabulary = Backbone.Model.extend({ urlRoot: '/api/vocabulary/', parse: function (response) { + console.log(response); if (response) { response.term_set = new TermList(response.term_set); } + console.log("parse"); + console.log(response); return response; }, toTemplate: function () { @@ -114,7 +117,8 @@ 'click a.edit-term-open': 'showEditTerm', 'click a.edit-term-close': 'hideEditTerm', 'click a.delete-term': 'deleteTerm', - 'click a.onomy-terms-submit': 'createOnomyVocabulary' + 'click a.onomy-terms-submit': 'createOnomyVocabulary', + 'click a.refresh-button-submit' : 'refreshOnomy' }, initialize: function (options) { _.bindAll(this, @@ -126,7 +130,8 @@ "keypressTermName", "updateTerm", "deleteTerm", - //"createOnomyVocabulary", + "createOnomyVocabulary", + "refreshOnomy", "activateTab"); this.context = options; @@ -200,18 +205,22 @@ 'display_name': display_name, 'content_type_id': jQuery(parent).find('input[name="content_type_id"]').attr("value"), 'object_id': jQuery(parent).find('input[name="object_id"]').attr("value"), - 'term_set': undefined + 'term_set': undefined, + }); - v.save({}, { + console.log(v); + v.save({'onomy_url': undefined}, { success: function () { self.selected = v; self.collection.add(v); + console.log('success'); }, error: function (model, response) { var responseText = jQuery.parseJSON(response.responseText); showMessage(responseText.vocabulary.error_message, undefined, "Error"); } }); + return false; }, updateVocabulary: function (evt) { @@ -479,13 +488,16 @@ if(model_search === undefined) { //if we cant find the vocab in the collection we create a new one. + urlList = []; + urlList.push(onomyURL); tempV = new Vocabulary({ 'display_name': parents[j].display_name, 'content_type_id': 14, 'object_id': 1, 'term_set': undefined, - 'onomy_url': onomyURL + 'onomy_url': urlList }); + tempV._save({},{ success: function(it){ self.selected = it; @@ -514,6 +526,7 @@ } else { //we do find the model. we just add the term to it. self.selected = model_search; + model_search.onomy_url.push(onomyURL); for (var z = 0; z < parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set.length; z ++) { tempT = new Term({ @@ -561,7 +574,14 @@ } } }); - //location.reload(); + + }, + refreshOnomy: function(evt) + { + var self = this; + urlArray = _.map(self.collection.models, function(model){return model.attributes.onomy_url}); + console.log(urlArray); + console.log(self.collection); } From 61ea51d7edc6627182934d7c03a93bed9c010ee4 Mon Sep 17 00:00:00 2001 From: c0cky Date: Tue, 18 Nov 2014 23:45:19 -0500 Subject: [PATCH 08/58] adding Onomy update --- mediathread/taxonomy/api.py | 80 +++++++++++++++++++++++++++++----- mediathread/taxonomy/models.py | 20 ++++++++- mediathread/taxonomy/views.py | 7 +-- 3 files changed, 92 insertions(+), 15 deletions(-) diff --git a/mediathread/taxonomy/api.py b/mediathread/taxonomy/api.py index 103c47c4a..30d333b46 100644 --- a/mediathread/taxonomy/api.py +++ b/mediathread/taxonomy/api.py @@ -1,4 +1,4 @@ -#pylint: disable-msg=R0904 +# pylint: disable-msg=R0904 from courseaffils.models import Course from django.contrib.contenttypes.models import ContentType from django.db.models import Count @@ -7,6 +7,34 @@ from tastypie.fields import ToManyField from tastypie.resources import ModelResource from tastypie.validation import Validation +import inspect + + +class OnomyValidation(Validation): + def is_valid(self, bundle, request=None): + errors = {} + + a = Onomy.objects.filter( + url=bundle.data['url'] + ) + + if len(a) > 0: + if 'pk' not in bundle.data or a[0].pk != int(bundle.ddata['pk']): + msg = 'A %s term already exists. Please choose another name' \ + % bundle.data['display_name'] + errors['error_message'] = [msg] + return errors + + +class OnomyResource(ModelResource): + class Meta: + queryset = Term.objects.all().order_by('id') + list_allowed_methods = ['get', 'post'] + detail_allowed_methods = ['get', 'put', 'delete'] + authentication = ClassLevelAuthentication() + authorization = FacultyAuthorization() + excludes = ['description', 'ordinality'] + validation = OnomyValidation() class TermValidation(Validation): @@ -21,13 +49,12 @@ def is_valid(self, bundle, request=None): if 'pk' not in bundle.data or a[0].pk != int(bundle.data['pk']): # a vocabulary already exists with this name msg = 'A %s term already exists. Please choose another name' \ - % bundle.data['display_name'] + % bundle.data['display_name'] errors['error_message'] = [msg] return errors class TermResource(ModelResource): - class Meta: queryset = Term.objects.all().order_by('id') list_allowed_methods = ['get', 'post'] @@ -57,30 +84,35 @@ def render_one(self, request, term): class VocabularyValidation(Validation): def is_valid(self, bundle, request=None): errors = {} - + print "DID WE STRIKE GOLD CAMRON?" + print bundle + print Vocabulary + print Validation a = Vocabulary.objects.filter( content_type_id=bundle.data['content_type_id'], display_name=bundle.data['display_name'], - object_id=bundle.data['object_id']) + object_id=bundle.data['object_id'] + ) + print "a" + print a if len(a) > 0: # vocabulary exists with this name if 'pk' not in bundle.data or a[0].pk != int(bundle.data['pk']): # a vocabulary already exists with this name msg = 'A %s concept exists. Please choose another name' \ - % bundle.data['display_name'] + % bundle.data['display_name'] errors['error_message'] = [msg] return errors class VocabularyAuthorization(FacultyAuthorization): - def read_list(self, object_list, bundle): request = bundle.request - course_type = ContentType.objects.get_for_model(request.course) object_list = object_list.filter(content_type=course_type, object_id=request.course.id) - + print course_type + print object_list[0] return object_list.order_by('id') @@ -92,6 +124,9 @@ class VocabularyResource(ModelResource): class Meta: queryset = Vocabulary.objects.all().order_by('id') + print 'queryset' + print queryset + print 'end queryset' list_allowed_methods = ['get', 'post'] detail_allowed_methods = ['get', 'put', 'delete'] authentication = ClassLevelAuthentication() @@ -101,33 +136,56 @@ class Meta: validation = VocabularyValidation() def alter_list_data_to_serialize(self, request, to_be_serialized): + print "entering alter_list_data" to_be_serialized['objects'] = sorted( to_be_serialized['objects'], key=lambda bundle: bundle.data['display_name']) + print to_be_serialized + print "leaving alter_list_data" return to_be_serialized def dehydrate(self, bundle): + print "entering dehydratei" + print bundle.obj.content_type.id + print bundle.data bundle.data['content_type_id'] = bundle.obj.content_type.id + print bundle.data['content_type_id'] + + print bundle + print "leaving dehydrate" return bundle def hydrate(self, bundle): + print "entering hydrate" + print bundle.obj + print ContentType.objects bundle.obj.content_type = ContentType.objects.get( id=bundle.data['content_type_id']) bundle.obj.course = Course.objects.get(id=bundle.data['object_id']) + print bundle + print "leaving hydrate" return bundle def render_one(self, request, vocabulary): + print "render_one" bundle = self.build_bundle(obj=vocabulary, request=request) dehydrated = self.full_dehydrate(bundle) + print bundle + print dehydrated + print "leaving render_one" return self._meta.serializer.to_simple(dehydrated, None) def render_list(self, request, vocabularies): + print "entering render_list" data = [] for vocabulary in vocabularies: data.append(self.render_one(request, vocabulary)) + print data + print "leaving render_list" return data def render_related(self, request, object_list): + print "entering render_related" if len(object_list) < 1: return [] @@ -156,6 +214,8 @@ def render_related(self, request, object_list): values = ctx.values() values.sort(lambda a, b: cmp(a['display_name'].lower(), - b['display_name'].lower())) + b['display_name'].lower())) + print values + print "leaving render_related" return values diff --git a/mediathread/taxonomy/models.py b/mediathread/taxonomy/models.py index 3071a12c3..e68ffaaff 100644 --- a/mediathread/taxonomy/models.py +++ b/mediathread/taxonomy/models.py @@ -21,12 +21,10 @@ class Vocabulary(models.Model): display_name = models.CharField(max_length=50) description = models.TextField(null=True, blank=True) single_select = models.BooleanField(default=False) - # Map this taxonomy to something else. like a course. content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() content_object = generic.GenericForeignKey('content_type', 'object_id') - objects = GenericRelationshipManager() class Meta: @@ -44,6 +42,24 @@ class VocabularyForm(forms.ModelForm): class Meta: model = Vocabulary +class Onomy(models.Model): + url = models.CharField(max_length=100) + vocabulary = models.ForeignKey(Vocabulary) + + def save(self, force_insert=False, force_update=False): + self.name = slugify(self.display_name) + super(Vocabulary, self).save(force_insert, force_update) + + def to_json(self): + return { + 'onomy_url':self.onomy_url, + 'vocabulary': self.vocabulary + } + +class OnomyForm(forms.ModelForm): + class Meta: + model = Onomy + class Term(models.Model): name = models.SlugField() diff --git a/mediathread/taxonomy/views.py b/mediathread/taxonomy/views.py index 739fd03e6..f44649f52 100644 --- a/mediathread/taxonomy/views.py +++ b/mediathread/taxonomy/views.py @@ -14,18 +14,19 @@ def taxonomy_workspace(request): vocabularies = Vocabulary.objects.get_for_object(request.course) for v in vocabularies: v.form = VocabularyForm(instance=v) - course_type = ContentType.objects.get_for_model(request.course) form = VocabularyForm(initial={'name': 'initial', 'content_type': course_type, 'object_id': request.course.id}) - + print vocabularies + print request return { 'vocabularies': vocabularies, 'vocabulary_form': form, 'course': request.course, 'course_type': course_type, 'term_form': TermForm(), + 'test' : 'tttttt' } @@ -33,7 +34,7 @@ def update_vocabulary_terms(request, content_object): concepts = dict((key[len('vocabulary-'):], request.POST.getlist(key)) for key, val in request.POST.items() if key.startswith('vocabulary-')) - + print concepts # Retrieve concepts/terms that this object is currently associated with associations = TermRelationship.objects.get_for_object(content_object) From 1299d13b928820130d9042e95f835dca9e68a5c2 Mon Sep 17 00:00:00 2001 From: c0cky Date: Thu, 20 Nov 2014 21:24:20 -0500 Subject: [PATCH 09/58] finished refresh button --- media/js/app/taxonomy/taxonomy.js | 145 ++++++++++++++++++++---------- 1 file changed, 100 insertions(+), 45 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 19b578674..27b6a094a 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -29,12 +29,11 @@ Vocabulary = Backbone.Model.extend({ urlRoot: '/api/vocabulary/', parse: function (response) { - console.log(response); + if (response) { response.term_set = new TermList(response.term_set); } - console.log("parse"); - console.log(response); + return response; }, toTemplate: function () { @@ -76,6 +75,8 @@ } }; + + var VocabularyList = Backbone.Collection.extend({ urlRoot: '/api/vocabulary/', model: Vocabulary, @@ -206,10 +207,11 @@ 'content_type_id': jQuery(parent).find('input[name="content_type_id"]').attr("value"), 'object_id': jQuery(parent).find('input[name="object_id"]').attr("value"), 'term_set': undefined, - + 'onomy_url': "" }); console.log(v); - v.save({'onomy_url': undefined}, { + v.save({}, { + success: function () { self.selected = v; self.collection.add(v); @@ -397,8 +399,10 @@ var self = this; var id = jQuery(evt.currentTarget).attr('href'); + console.log(jQuery(evt.currentTarget)); + console.log(id); var term = self.selected.get('term_set').getByDataId(id); - + console.log(term); var msg = "Deleting the term " + term.get('display_name') + "" + " removes this term" + " from all associated course items."; @@ -446,20 +450,66 @@ var et = jQuery(evt.currentTarget).prev(); var self = this; - var vocabulary_id = this.selected.get('id'); + var vocabulary_id; + vocabulary_id = this.selected.get('id'); //'http://www.corsproxy.com/' + //this should be sanitized in the future. onomyURL = jQuery(et).attr("value").trim(); - jQuery.get(onomyURL, + console.log(onomyURL); + getTheOnomy(onomyURL, self); + + }, + refreshOnomy: function(evt) + { + var self = this; + urlArray = _.map(self.collection.models, function(model){return model.attributes.onomy_url}); + var address; + for (var i = 0; i < urlArray.length; i++) + { + address = urlArray[i].toString(); + if (!address == "") + { + getTheOnomy(address, self); + } + + } + +// console.log(self.collection); +// var urlcsv = self.selected.get('onomy_url'); +// url = urlcsv.split(','); +// url.push('http://testthis.com/allegedly/onomy'); +// //self.selected.get('onomy_url').add(urlcsv); +// console.log(self.selected.get('onomy_url')); + } + + + + + + }); + function findUtil(array, thing){ + return jQuery.grep(array, function(item){ + return item.display_name == thing; + }); + }; + function getTheOnomy(onomyURL, self) + { + + var vocabulary_id; + vocabulary_id = self.selected.get('id'); + jQuery.get(onomyURL, function (data) { + var x; x = JSON.parse(data); - //change 4 to x.terms.length after testing. - var MAX = x.terms.length; + + var MAX; var parents = []; + MAX = x.terms.length; //change to x.terms.length after done testing for (var i = 0; i < MAX; i++) { - var pL = x.terms[i]['rdfs:parentLabel'].trim(); - var display = x.terms[i]['rdfs:label'].trim(); - + var pL; + var display; + pL = x.terms[i]['rdfs:parentLabel'].trim(); + display = x.terms[i]['rdfs:label'].trim(); console.log(pL); console.log(display); //demorgans law @@ -488,14 +538,13 @@ if(model_search === undefined) { //if we cant find the vocab in the collection we create a new one. - urlList = []; - urlList.push(onomyURL); + tempV = new Vocabulary({ 'display_name': parents[j].display_name, 'content_type_id': 14, 'object_id': 1, 'term_set': undefined, - 'onomy_url': urlList + 'onomy_url': onomyURL }); tempV._save({},{ @@ -514,10 +563,7 @@ }); tempT._save({},{ success: function (itT) { - parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self.get('term_set').add(new Term({ - 'display_name': itT.attributes['display_name'], - 'vocabulary_id': itT.attributes['vocabulary_id'] - })); + parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self.get('term_set').add(itT); self.render(); } }); @@ -526,7 +572,16 @@ } else { //we do find the model. we just add the term to it. self.selected = model_search; - model_search.onomy_url.push(onomyURL); + var urlcsv; + var url; + urlcsv = self.selected.get('onomy_url'); + url = urlcsv.split(','); + if(!(_.contains(url, onomyURL))) + { + url.push(onomyURL); + model_search.save({'onomy_url': url.toString()}); + } + for (var z = 0; z < parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set.length; z ++) { tempT = new Term({ @@ -535,12 +590,10 @@ }); tempT._save({},{ success: function (itT) { - self.selected.get('term_set').add(new Term({ - 'display_name': itT.attributes['display_name'], - 'vocabulary_id': itT.attributes['vocabulary_id'] - })); + self.selected.get('term_set').add(itT); self.render(); } + }); } @@ -556,42 +609,44 @@ if (display === undefined || display.length < 1) { continue; } + var id; + var v; + var urlcsv; + var url; + console.log(self.selected); + id = self.selected.attributes.id; + v = self.collection.getByDataId(id); + urlcsv = self.selected.get('onomy_url'); + url = urlcsv.split(','); + + //if this vocabulary doesn't contain the url we punched in + if(!(_.contains(url, onomyURL))) + { + //add it to our array we made and save it in the vocab + url.push(onomyURL); + v.save({'onomy_url': url.toString()}); + } + //we create our term var t; t = new Term({ 'display_name': display, 'vocabulary_id': vocabulary_id }); + //then save it with our overriden queued save t._save({}, { wait: true, success: function (it) { - self.selected.get('term_set').add(new Term({ - 'display_name': it.attributes['display_name'], - 'vocabulary_id': it.attributes['vocabulary_id'] - })); + //add it to our term + self.selected.get('term_set').add(it); self.render(); } }); } } }); - - }, - refreshOnomy: function(evt) - { - var self = this; - urlArray = _.map(self.collection.models, function(model){return model.attributes.onomy_url}); - console.log(urlArray); - console.log(self.collection); - } - - - }); - function findUtil(array, thing){ - return jQuery.grep(array, function(item){ - return item.display_name == thing; - }); }; + }(jQuery)); From 08cec877c821a520ae13a918861519e3c3c7350e Mon Sep 17 00:00:00 2001 From: c0cky Date: Tue, 25 Nov 2014 00:41:24 -0500 Subject: [PATCH 10/58] added test view that dumps data --- mediathread/assetmgr/api.py | 2 +- mediathread/assetmgr/urls.py | 1 + mediathread/assetmgr/views.py | 29 +++++++++++++++++++++++++++-- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/mediathread/assetmgr/api.py b/mediathread/assetmgr/api.py index 35a1fe3af..e425f14a6 100644 --- a/mediathread/assetmgr/api.py +++ b/mediathread/assetmgr/api.py @@ -16,7 +16,7 @@ class AssetResource(ModelResource): class Meta: queryset = Asset.objects.none() excludes = ['added', 'modified', 'course', - 'active', 'metadata_blob'] + 'active']#, 'metadata_blob'] getting rid of this doesn't break anything list_allowed_methods = [] detail_allowed_methods = [] authentication = ClassLevelAuthentication() diff --git a/mediathread/assetmgr/urls.py b/mediathread/assetmgr/urls.py index 6b8dfaac6..949bc86c0 100644 --- a/mediathread/assetmgr/urls.py +++ b/mediathread/assetmgr/urls.py @@ -43,6 +43,7 @@ 'final_cut_pro_xml', name="final_cut_pro_xml"), + url(r'test/', 'test_dump', name='test_dump'), # Asset workspace variations url(r'^$', AssetWorkspaceView.as_view(), {}, 'asset-collection-view'), ) diff --git a/mediathread/assetmgr/views.py b/mediathread/assetmgr/views.py index 182679972..61d84d5b8 100644 --- a/mediathread/assetmgr/views.py +++ b/mediathread/assetmgr/views.py @@ -23,7 +23,7 @@ from djangohelpers.lib import allow_http from courseaffils.lib import in_course, in_course_or_404, AUTO_COURSE_SELECT -from mediathread.api import UserResource, TagResource +from mediathread.api import UserResource, TagResource, ClassLevelAuthentication from mediathread.assetmgr.api import AssetResource from mediathread.assetmgr.models import Asset, Source from mediathread.discussions.api import DiscussionIndexResource @@ -135,25 +135,29 @@ def asset_create(request): raise AssertionError("no arguments were supplied to make an asset") if asset is None: + try: asset = Asset(title=title[:1020], # max title length course=request.course, author=user) asset.save() - for source in sources_from_args(request, asset).values(): if len(source.url) <= 4096: + print source source.save() if "tag" in metadata: for each_tag in metadata["tag"]: + print user, each_tag asset.save_tag(user, each_tag) asset.metadata_blob = json.dumps(metadata) asset.save() + except: # we'll make it here if someone doesn't submit # any primary_labels as arguments + # THIS WILL ALSO HAPPEN IF THE USER IS NOT PART OF THE CLASS # @todo verify the above comment. raise AssertionError("no primary source provided") @@ -441,6 +445,26 @@ def final_cut_pro_xml(request, asset_id): return HttpResponse('Not Implemented: No Final Cut Pro Xmeml support', status=503) +def test_dump(request): + user = request.user + user_id = user.id + asset = Asset.objects.filter(author_id=user_id) + ar = AssetResource() + ar.Meta.excludes = ['added', 'modified', 'course', 'active'] + lst = [] + for a in asset.all(): + abundle = ar.build_bundle(obj=a, request=request) + dehydrated = ar.full_dehydrate(abundle) + ctx = ar._meta.serializer.to_simple(dehydrated, None) + lst.append(ctx) + for l in lst: + for key, value in ctx.items(): + print key, value + print "\n" + return HttpResponse(lst) + + + class AssetReferenceView(LoggedInMixin, RestrictedMaterialsMixin, AjaxRequiredMixin, JSONResponseMixin, View): @@ -463,6 +487,7 @@ def get(self, request, asset_id): # projects & discussions title, object_pk, content_type, modified indicies = DiscussionIndex.objects.filter( asset=asset).order_by('-modified') + print DiscussionIndexResource().render_list(request, indicies) ctx.update(DiscussionIndexResource().render_list(request, indicies)) From 2b01a5a9be5c1f804aa1582b0f739fb9e21ce4ba Mon Sep 17 00:00:00 2001 From: c0cky Date: Thu, 27 Nov 2014 19:31:12 -0500 Subject: [PATCH 11/58] fixed annotations need to add XML creation --- mediathread/assetmgr/views.py | 70 +++++++++++++++++++++++++---------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/mediathread/assetmgr/views.py b/mediathread/assetmgr/views.py index 61d84d5b8..ce4386908 100644 --- a/mediathread/assetmgr/views.py +++ b/mediathread/assetmgr/views.py @@ -21,7 +21,7 @@ from django.template import RequestContext, loader from django.views.generic.base import View from djangohelpers.lib import allow_http - +from mediathread.djangosherd.api import SherdNoteResource from courseaffils.lib import in_course, in_course_or_404, AUTO_COURSE_SELECT from mediathread.api import UserResource, TagResource, ClassLevelAuthentication from mediathread.assetmgr.api import AssetResource @@ -31,6 +31,7 @@ from mediathread.djangosherd.views import create_annotation, edit_annotation, \ delete_annotation, update_annotation from mediathread.main.models import UserSetting +from mediathread.main.course_details import cached_course_is_faculty from mediathread.mixins import ajax_required, LoggedInMixin, \ JSONResponseMixin, AjaxRequiredMixin, RestrictedMaterialsMixin from mediathread.taxonomy.api import VocabularyResource @@ -448,20 +449,49 @@ def final_cut_pro_xml(request, asset_id): def test_dump(request): user = request.user user_id = user.id - asset = Asset.objects.filter(author_id=user_id) - ar = AssetResource() + assets = Asset.objects.filter(author_id=user_id) + ar = AssetResource(include_annotations=True) ar.Meta.excludes = ['added', 'modified', 'course', 'active'] lst = [] - for a in asset.all(): - abundle = ar.build_bundle(obj=a, request=request) - dehydrated = ar.full_dehydrate(abundle) - ctx = ar._meta.serializer.to_simple(dehydrated, None) - lst.append(ctx) - for l in lst: - for key, value in ctx.items(): - print key, value - print "\n" - return HttpResponse(lst) + # acv = AssetCollectionView() + # acv.record_owner = user_id + # acv.record_viewer = request.user + # acv.is_viewer_faculty = cached_course_is_faculty(request.course, + # request.user) + # acv.visible_authors = [request.user.id] + + # notes = SherdNote.objects.get_related_notes(asset, user_id, request.user.id) + notes = SherdNote.objects.get_related_notes( + assets, user_id or None, [request.user.id]) + print notes + # for a in assets: + # print a + j = ar.render_list(request, [request.user.id], assets, notes) + print j + note_resource = SherdNoteResource() + note_ctx = note_resource.render_one(request, notes[0], "") + the_json = {} + if notes[0].is_global_annotation(): + the_json['global_annotation'] = note_ctx + the_json['global_annotation_analysis'] = ( + len(note_ctx['vocabulary']) > 0 or + len(note_ctx['metadata']['body']) > 0 or + len(note_ctx['metadata']['tags']) > 0) + else: + the_json['annotations'].append(note_ctx) + + # print the_json + #it keeps adding the annotations for all the videos.... whadduhfux + + + # for a in asset.all(): + # abundle = ar.build_bundle(obj=a, request=request) + # dehydrated = ar.full_dehydrate(abundle) + # ctx = ar._meta.serializer.to_simple(dehydrated, None) + # lst.append(ctx) + # + # lst.append(acv.get(request)) + return HttpResponse(j) @@ -617,21 +647,21 @@ def get_context(self, request, assets, notes): 'is_faculty': self.is_viewer_faculty } - if self.record_owner: - ctx['space_owner'] = ures.render_one(request, self.record_owner) + # if self.record_owner: + # ctx['space_owner'] = ures.render_one(request, self.record_owner) ctx['active_filters'] = {} for key, val in request.GET.items(): if (key in self.valid_filters or key.startswith('vocabulary-')): ctx['active_filters'][key] = val - ctx['editable'] = self.viewing_own_records - ctx['citable'] = citable + # ctx['editable'] = self.viewing_own_records + # ctx['citable'] = citable # render the assets - ares = AssetResource(include_annotations=include_annotations, - extras={'editable': self.viewing_own_records, - 'citable': citable}) + ares = AssetResource(include_annotations=include_annotations) + # extras={'editable': self.viewing_own_records, + # 'citable': citable}) ctx['assets'] = ares.render_list(request, self.record_viewer, assets, notes) From 5a284fb10061d966bd8c91a08279c7c5c93ffc25 Mon Sep 17 00:00:00 2001 From: c0cky Date: Sat, 29 Nov 2014 02:47:26 -0500 Subject: [PATCH 12/58] rdf --- mediathread/assetmgr/views.py | 109 ++++++++++++++++++++++++++++++---- 1 file changed, 96 insertions(+), 13 deletions(-) diff --git a/mediathread/assetmgr/views.py b/mediathread/assetmgr/views.py index ce4386908..844c8ea89 100644 --- a/mediathread/assetmgr/views.py +++ b/mediathread/assetmgr/views.py @@ -6,6 +6,7 @@ import re import urllib import urllib2 +import lxml.etree as ET from courseaffils.models import CourseAccess from django.conf import settings @@ -463,22 +464,103 @@ def test_dump(request): # notes = SherdNote.objects.get_related_notes(asset, user_id, request.user.id) notes = SherdNote.objects.get_related_notes( assets, user_id or None, [request.user.id]) - print notes + # print notes # for a in assets: # print a j = ar.render_list(request, [request.user.id], assets, notes) - print j - note_resource = SherdNoteResource() - note_ctx = note_resource.render_one(request, notes[0], "") - the_json = {} - if notes[0].is_global_annotation(): - the_json['global_annotation'] = note_ctx - the_json['global_annotation_analysis'] = ( - len(note_ctx['vocabulary']) > 0 or - len(note_ctx['metadata']['body']) > 0 or - len(note_ctx['metadata']['tags']) > 0) - else: - the_json['annotations'].append(note_ctx) + + # note_resource = SherdNoteResource() + # note_ctx = note_resource.render_one(request, notes[0], "") + # the_json = {} + # if notes[0].is_global_annotation(): + # the_json['global_annotation'] = note_ctx + # the_json['global_annotation_analysis'] = ( + # len(note_ctx['vocabulary']) > 0 or + # len(note_ctx['metadata']['body']) > 0 or + # len(note_ctx['metadata']['tags']) > 0) + # else: + # the_json['annotations'].append(note_ctx) + + data = j[0] + #we need to turn the blob into json so we can grab data easier + ascii_blob = data.get('metadata_blob').encode('ascii', 'ignore') + jsonmetadata_blob = dict() + jsonmetadata_blob = json.loads(ascii_blob) + print type(jsonmetadata_blob) + print jsonmetadata_blob.get('category') + + #this maps the xmlns paths and all that fun stuff + NS_MAP = {"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", + "art" : "http://simile.mit.edu/2003/10/ontologies/artstor#", + "foaf" : "http://xmlns.com/foaf/0.1/", + "dcterms" : "http://purl.org/dc/terms/", + "sioc" : "http://rdfs.org/sioc/ns#", + "oa" : "http://www.openannotation.org/ns/"} + #short hands the ns + RDF = "{%s}" % NS_MAP['rdf'] + ART = "{%s}" % NS_MAP['art'] + FOAF = "{%s}" % NS_MAP['foaf'] + DCTERMS = "{%s}" % NS_MAP['dcterms'] + SIOC = "{%s}" % NS_MAP['sioc'] + OA = "{%s}" % NS_MAP['oa'] + + #rdf is the 'root' + rdf = ET.Element(RDF + "RDF", nsmap=NS_MAP) + + #creating the main rdf for the video + description = ET.SubElement(rdf, "{%s}" % NS_MAP['rdf'] + "Description") + description.attrib[RDF + 'about'] = data.get('local_url') + rdf_type = ET.SubElement(description, RDF + "type") + rdf_type.attrib[RDF + 'resource'] = data.get('primary_type') + art_thumb = ET.SubElement(description, ART + 'thumbnail') + art_thumb.attrib[RDF + 'resource'] = data.get('sources').get('thumb')['url'] + art_url = ET.SubElement(description, ART + 'url') + art_url.attrib[RDF + 'resource'] = data.get('sources').get('url')['url'] + art_source = ET.SubElement(description, ART + 'sourceLocation') + art_source.attrib[RDF + 'resource'] = data.get('sources').get('youtube')['url'] + dc_title = ET.SubElement(description, DCTERMS + 'title') + dc_title.text = data.get('title') + dc_desc = ET.SubElement(description, DCTERMS + 'description') + dc_desc.text = jsonmetadata_blob.get('description')[0] + dc_vers = ET.SubElement(description, DCTERMS + 'isVersionOf') + dc_vers.attrib[RDF + 'resource'] = data.get('sources').get('url')['url'] + dc_pub = ET.SubElement(description, DCTERMS + 'publisher') + dc_pub.text = jsonmetadata_blob.get('author')[0] + dc_cntb = ET.SubElement(description, DCTERMS + 'contributor') + dc_cntb.text = jsonmetadata_blob.get('author')[0] + dc_date = ET.SubElement(description, DCTERMS + 'date') + dc_date.text = jsonmetadata_blob.get('published')[0] + dc_form = ET.SubElement(description, DCTERMS + 'format') + dc_form.text = data.get('media_type_label') + + #this can be found if the video has annotations but if it doesnt it doesnt show up + dc_ext = ET.SubElement(description, DCTERMS + 'extent') + dc_ext.text = "CANNOT BE FOUND" + + dc_type = ET.SubElement(description, DCTERMS + 'type') + dc_type.text = data.get('mediat_type_label') + dc_datesub = ET.SubElement(description, DCTERMS + 'datesubmitted') + dc_datesub.text = jsonmetadata_blob.get('published')[0] + dc_rel = ET.SubElement(description, DCTERMS + 'relation') + dc_rel.text = jsonmetadata_blob.get('category')[0] + + #now we do annotations and tags etc. + for i in range(0, data.get('annotation_count')): + anno = ET.SubElement(rdf, RDF + 'Description') + anno.attrib[RDF + 'about'] = data.get('annotations')[i]['url'] + anno_resource = ET.SubElement(anno, OA + 'hasBody') + anno_resource[RDF + 'resource'] = data.get('annotations')[i]['url'] + vocab = data.get('annotations')[i]['vocabulary'] + for j in range(0, len(vocab)): + anno_vocab = ET.SubElement(anno, OA + 'hasBody') + anno_vocab.attrib[RDF + 'nodeID'] = vocab[j]['display_name'] + + anno_author = ET.SubElement(anno, OA + 'annotatedBy') + anno_author[RDF + 'resource'] = data.get('annotations')[i]['author']['username'] + anno_time = ET.SubElement(anno, OA + 'annotatedAt') + anno_time.text = data.get('annotations')[i]['metadata']['modified'] + # print the_json #it keeps adding the annotations for all the videos.... whadduhfux @@ -491,6 +573,7 @@ def test_dump(request): # lst.append(ctx) # # lst.append(acv.get(request)) + return HttpResponse(j) From a1d55a7c80d5ff8342568c5c61e108d8dfce0228 Mon Sep 17 00:00:00 2001 From: c0cky Date: Sun, 30 Nov 2014 02:19:02 -0500 Subject: [PATCH 13/58] rdf export --- mediathread/assetmgr/views.py | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/mediathread/assetmgr/views.py b/mediathread/assetmgr/views.py index 844c8ea89..8684f32f8 100644 --- a/mediathread/assetmgr/views.py +++ b/mediathread/assetmgr/views.py @@ -499,6 +499,7 @@ def test_dump(request): "oa" : "http://www.openannotation.org/ns/"} #short hands the ns RDF = "{%s}" % NS_MAP['rdf'] + RDFS = "{%s}" % NS_MAP['rdfs'] ART = "{%s}" % NS_MAP['art'] FOAF = "{%s}" % NS_MAP['foaf'] DCTERMS = "{%s}" % NS_MAP['dcterms'] @@ -545,23 +546,37 @@ def test_dump(request): dc_rel = ET.SubElement(description, DCTERMS + 'relation') dc_rel.text = jsonmetadata_blob.get('category')[0] + vocab = [] #now we do annotations and tags etc. for i in range(0, data.get('annotation_count')): anno = ET.SubElement(rdf, RDF + 'Description') anno.attrib[RDF + 'about'] = data.get('annotations')[i]['url'] anno_resource = ET.SubElement(anno, OA + 'hasBody') - anno_resource[RDF + 'resource'] = data.get('annotations')[i]['url'] - vocab = data.get('annotations')[i]['vocabulary'] + anno_resource.attrib[RDF + 'resource'] = data.get('annotations')[i]['url'] + vocab.append(data.get('annotations')[i]['vocabulary']) for j in range(0, len(vocab)): + #create the sub elements for the annotations anno_vocab = ET.SubElement(anno, OA + 'hasBody') - anno_vocab.attrib[RDF + 'nodeID'] = vocab[j]['display_name'] + anno_vocab.attrib[RDF + 'nodeID'] = vocab[j][0]['display_name'] + + #create the description for that vocab while we are at it + for t in vocab[j][0]['terms']: + term = ET.SubElement(rdf, RDF + 'Description') + term.attrib[RDF + 'nodeID'] = t['name'] + rdfs_label = ET.SubElement(term, RDFS + 'label') + rdfs_label.text = t['display_name'] + foaf_page = ET.SubElement(term, FOAF + "page") + foaf_page.attrib[RDF + 'resource'] = t['resource_uri'] + anno_author = ET.SubElement(anno, OA + 'annotatedBy') - anno_author[RDF + 'resource'] = data.get('annotations')[i]['author']['username'] + anno_author.attrib[RDF + 'resource'] = data.get('annotations')[i]['author']['username'] anno_time = ET.SubElement(anno, OA + 'annotatedAt') anno_time.text = data.get('annotations')[i]['metadata']['modified'] + foaf_name = ET.SubElement(anno, FOAF + "name") + foaf_name.text = data.get('annotations')[i]['author']['public_name'] - + print ET.tostring(rdf, pretty_print=True) # print the_json #it keeps adding the annotations for all the videos.... whadduhfux @@ -574,7 +589,7 @@ def test_dump(request): # # lst.append(acv.get(request)) - return HttpResponse(j) + return HttpResponse(ET.tostring(rdf, pretty_print=True)) From 6d29dc00ac15df8cfc98842d2579300f6fe54868 Mon Sep 17 00:00:00 2001 From: c0cky Date: Fri, 19 Dec 2014 16:32:03 -0500 Subject: [PATCH 14/58] export done --- mediathread/assetmgr/views.py | 235 +++++++++++++++------------------- 1 file changed, 102 insertions(+), 133 deletions(-) diff --git a/mediathread/assetmgr/views.py b/mediathread/assetmgr/views.py index 8684f32f8..70d298459 100644 --- a/mediathread/assetmgr/views.py +++ b/mediathread/assetmgr/views.py @@ -37,6 +37,7 @@ JSONResponseMixin, AjaxRequiredMixin, RestrictedMaterialsMixin from mediathread.taxonomy.api import VocabularyResource from mediathread.taxonomy.models import Vocabulary +#from waffle.decorators import waffle_switch @login_required @@ -447,6 +448,7 @@ def final_cut_pro_xml(request, asset_id): return HttpResponse('Not Implemented: No Final Cut Pro Xmeml support', status=503) +#@waffle_switch('ONOMY_SWITCH') def test_dump(request): user = request.user user_id = user.id @@ -454,144 +456,111 @@ def test_dump(request): ar = AssetResource(include_annotations=True) ar.Meta.excludes = ['added', 'modified', 'course', 'active'] lst = [] - # acv = AssetCollectionView() - # acv.record_owner = user_id - # acv.record_viewer = request.user - # acv.is_viewer_faculty = cached_course_is_faculty(request.course, - # request.user) - # acv.visible_authors = [request.user.id] - - # notes = SherdNote.objects.get_related_notes(asset, user_id, request.user.id) - notes = SherdNote.objects.get_related_notes( - assets, user_id or None, [request.user.id]) - # print notes - # for a in assets: - # print a - j = ar.render_list(request, [request.user.id], assets, notes) - - # note_resource = SherdNoteResource() - # note_ctx = note_resource.render_one(request, notes[0], "") - # the_json = {} - # if notes[0].is_global_annotation(): - # the_json['global_annotation'] = note_ctx - # the_json['global_annotation_analysis'] = ( - # len(note_ctx['vocabulary']) > 0 or - # len(note_ctx['metadata']['body']) > 0 or - # len(note_ctx['metadata']['tags']) > 0) - # else: - # the_json['annotations'].append(note_ctx) - - data = j[0] - #we need to turn the blob into json so we can grab data easier - ascii_blob = data.get('metadata_blob').encode('ascii', 'ignore') - jsonmetadata_blob = dict() - jsonmetadata_blob = json.loads(ascii_blob) - print type(jsonmetadata_blob) - print jsonmetadata_blob.get('category') + notes = SherdNote.objects.get_related_notes(assets, user_id or None, [request.user.id]) + + api_response = ar.render_list(request, [request.user.id], assets, notes) + if len(api_response) == 0: + return HttpResponse("There are no videos in your collection") + for i in range(0, len(api_response)): + data = api_response[i] + #we need to turn the blob into json so we can grab data easier + utf_blob = data.get('metadata_blob') + jsonmetadata_blob = dict() + if utf_blob is not None: + utf_blob = utf_blob.encode('UTF-8', 'ignore') + jsonmetadata_blob = json.loads(utf_blob) #this maps the xmlns paths and all that fun stuff - NS_MAP = {"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "rdfs" : "http://www.w3.org/2000/01/rdf-schema#", - "art" : "http://simile.mit.edu/2003/10/ontologies/artstor#", - "foaf" : "http://xmlns.com/foaf/0.1/", - "dcterms" : "http://purl.org/dc/terms/", - "sioc" : "http://rdfs.org/sioc/ns#", - "oa" : "http://www.openannotation.org/ns/"} + NS_MAP = {"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + "art": "http://simile.mit.edu/2003/10/ontologies/artstor#", + "foaf": "http://xmlns.com/foaf/0.1/", + "dcterms": "http://purl.org/dc/terms/", + "sioc": "http://rdfs.org/sioc/ns#", + "oa": "http://www.openannotation.org/ns/"} #short hands the ns - RDF = "{%s}" % NS_MAP['rdf'] - RDFS = "{%s}" % NS_MAP['rdfs'] - ART = "{%s}" % NS_MAP['art'] - FOAF = "{%s}" % NS_MAP['foaf'] - DCTERMS = "{%s}" % NS_MAP['dcterms'] - SIOC = "{%s}" % NS_MAP['sioc'] - OA = "{%s}" % NS_MAP['oa'] + RDF = "{%s}" % NS_MAP['rdf'] + RDFS = "{%s}" % NS_MAP['rdfs'] + ART = "{%s}" % NS_MAP['art'] + FOAF = "{%s}" % NS_MAP['foaf'] + DCTERMS = "{%s}" % NS_MAP['dcterms'] + SIOC = "{%s}" % NS_MAP['sioc'] + OA = "{%s}" % NS_MAP['oa'] #rdf is the 'root' - rdf = ET.Element(RDF + "RDF", nsmap=NS_MAP) + rdf = ET.Element(RDF + "RDF", nsmap=NS_MAP) #creating the main rdf for the video - description = ET.SubElement(rdf, "{%s}" % NS_MAP['rdf'] + "Description") - description.attrib[RDF + 'about'] = data.get('local_url') - rdf_type = ET.SubElement(description, RDF + "type") - rdf_type.attrib[RDF + 'resource'] = data.get('primary_type') - art_thumb = ET.SubElement(description, ART + 'thumbnail') - art_thumb.attrib[RDF + 'resource'] = data.get('sources').get('thumb')['url'] - art_url = ET.SubElement(description, ART + 'url') - art_url.attrib[RDF + 'resource'] = data.get('sources').get('url')['url'] - art_source = ET.SubElement(description, ART + 'sourceLocation') - art_source.attrib[RDF + 'resource'] = data.get('sources').get('youtube')['url'] - dc_title = ET.SubElement(description, DCTERMS + 'title') - dc_title.text = data.get('title') - dc_desc = ET.SubElement(description, DCTERMS + 'description') - dc_desc.text = jsonmetadata_blob.get('description')[0] - dc_vers = ET.SubElement(description, DCTERMS + 'isVersionOf') - dc_vers.attrib[RDF + 'resource'] = data.get('sources').get('url')['url'] - dc_pub = ET.SubElement(description, DCTERMS + 'publisher') - dc_pub.text = jsonmetadata_blob.get('author')[0] - dc_cntb = ET.SubElement(description, DCTERMS + 'contributor') - dc_cntb.text = jsonmetadata_blob.get('author')[0] - dc_date = ET.SubElement(description, DCTERMS + 'date') - dc_date.text = jsonmetadata_blob.get('published')[0] - dc_form = ET.SubElement(description, DCTERMS + 'format') - dc_form.text = data.get('media_type_label') + description = ET.SubElement(rdf, "{%s}" % NS_MAP['rdf'] + "Description") + description.attrib[RDF + 'about'] = data.get('local_url') + rdf_type = ET.SubElement(description, RDF + "type") + rdf_type.attrib[RDF + 'resource'] = data.get('primary_type') + art_thumb = ET.SubElement(description, ART + 'thumbnail') + art_thumb.attrib[RDF + 'resource'] = data.get('sources').get('thumb')['url'] + art_url = ET.SubElement(description, ART + 'url') + art_url.attrib[RDF + 'resource'] = data.get('sources').get('url')['url'] + art_source = ET.SubElement(description, ART + 'sourceLocation') + art_source.attrib[RDF + 'resource'] = data.get('sources').get('youtube')['url'] + dc_title = ET.SubElement(description, DCTERMS + 'title') + dc_title.text = data.get('title') + dc_desc = ET.SubElement(description, DCTERMS + 'description') + dc_desc.text = jsonmetadata_blob.get('description')[0] + dc_vers = ET.SubElement(description, DCTERMS + 'isVersionOf') + dc_vers.attrib[RDF + 'resource'] = data.get('sources').get('url')['url'] + dc_pub = ET.SubElement(description, DCTERMS + 'publisher') + dc_pub.text = jsonmetadata_blob.get('author')[0] + dc_cntb = ET.SubElement(description, DCTERMS + 'contributor') + dc_cntb.text = jsonmetadata_blob.get('author')[0] + dc_date = ET.SubElement(description, DCTERMS + 'date') + dc_date.text = jsonmetadata_blob.get('published')[0] + dc_form = ET.SubElement(description, DCTERMS + 'format') + dc_form.text = data.get('media_type_label') #this can be found if the video has annotations but if it doesnt it doesnt show up - dc_ext = ET.SubElement(description, DCTERMS + 'extent') - dc_ext.text = "CANNOT BE FOUND" - - dc_type = ET.SubElement(description, DCTERMS + 'type') - dc_type.text = data.get('mediat_type_label') - dc_datesub = ET.SubElement(description, DCTERMS + 'datesubmitted') - dc_datesub.text = jsonmetadata_blob.get('published')[0] - dc_rel = ET.SubElement(description, DCTERMS + 'relation') - dc_rel.text = jsonmetadata_blob.get('category')[0] - - vocab = [] - #now we do annotations and tags etc. - for i in range(0, data.get('annotation_count')): - anno = ET.SubElement(rdf, RDF + 'Description') - anno.attrib[RDF + 'about'] = data.get('annotations')[i]['url'] - anno_resource = ET.SubElement(anno, OA + 'hasBody') - anno_resource.attrib[RDF + 'resource'] = data.get('annotations')[i]['url'] - vocab.append(data.get('annotations')[i]['vocabulary']) - for j in range(0, len(vocab)): - #create the sub elements for the annotations - anno_vocab = ET.SubElement(anno, OA + 'hasBody') - anno_vocab.attrib[RDF + 'nodeID'] = vocab[j][0]['display_name'] - - #create the description for that vocab while we are at it - for t in vocab[j][0]['terms']: - term = ET.SubElement(rdf, RDF + 'Description') - term.attrib[RDF + 'nodeID'] = t['name'] - rdfs_label = ET.SubElement(term, RDFS + 'label') - rdfs_label.text = t['display_name'] - foaf_page = ET.SubElement(term, FOAF + "page") - foaf_page.attrib[RDF + 'resource'] = t['resource_uri'] - - - anno_author = ET.SubElement(anno, OA + 'annotatedBy') - anno_author.attrib[RDF + 'resource'] = data.get('annotations')[i]['author']['username'] - anno_time = ET.SubElement(anno, OA + 'annotatedAt') - anno_time.text = data.get('annotations')[i]['metadata']['modified'] - foaf_name = ET.SubElement(anno, FOAF + "name") - foaf_name.text = data.get('annotations')[i]['author']['public_name'] - - print ET.tostring(rdf, pretty_print=True) - # print the_json - #it keeps adding the annotations for all the videos.... whadduhfux - - - # for a in asset.all(): - # abundle = ar.build_bundle(obj=a, request=request) - # dehydrated = ar.full_dehydrate(abundle) - # ctx = ar._meta.serializer.to_simple(dehydrated, None) - # lst.append(ctx) - # - # lst.append(acv.get(request)) - - return HttpResponse(ET.tostring(rdf, pretty_print=True)) - - + dc_ext = ET.SubElement(description, DCTERMS + 'extent') + dc_ext.text = "CANNOT BE FOUND" + + dc_type = ET.SubElement(description, DCTERMS + 'type') + dc_type.text = data.get('media_type_label') + dc_datesub = ET.SubElement(description, DCTERMS + 'datesubmitted') + dc_datesub.text = jsonmetadata_blob.get('published')[0] + dc_rel = ET.SubElement(description, DCTERMS + 'relation') + dc_rel.text = jsonmetadata_blob.get('category')[0] + + vocab = [] + #now we do annotations and tags etc. + for i in range(0, data.get('annotation_count')): + anno = ET.SubElement(rdf, RDF + 'Description') + anno.attrib[RDF + 'about'] = data.get('annotations')[i]['url'] + anno_resource = ET.SubElement(anno, OA + 'hasBody') + anno_resource.attrib[RDF + 'resource'] = data.get('annotations')[i]['url'] + vocab.append(data.get('annotations')[i]['vocabulary']) + for j in range(0, len(vocab)): + #create the sub elements for the annotations + anno_vocab = ET.SubElement(anno, OA + 'hasBody') + anno_vocab.attrib[RDF + 'nodeID'] = vocab[j][0]['display_name'] + + #create the description for that vocab while we are at it + for t in vocab[j][0]['terms']: + term = ET.SubElement(rdf, RDF + 'Description') + term.attrib[RDF + 'nodeID'] = t['name'] + rdfs_label = ET.SubElement(term, RDFS + 'label') + rdfs_label.text = t['display_name'] + foaf_page = ET.SubElement(term, FOAF + "page") + foaf_page.attrib[RDF + 'resource'] = t['resource_uri'] + + + anno_author = ET.SubElement(anno, OA + 'annotatedBy') + anno_author.attrib[RDF + 'resource'] = data.get('annotations')[i]['author']['username'] + anno_time = ET.SubElement(anno, OA + 'annotatedAt') + anno_time.text = data.get('annotations')[i]['metadata']['modified'] + foaf_name = ET.SubElement(anno, FOAF + "name") + foaf_name.text = data.get('annotations')[i]['author']['public_name'] + + lst.append(ET.tostring(rdf, pretty_print=True, xml_declaration=True, encoding="UTF-8")) + print ET.tostring(rdf, pretty_print=True, xml_declaration=True, encoding="UTF-8") + + return HttpResponse(lst) class AssetReferenceView(LoggedInMixin, RestrictedMaterialsMixin, @@ -745,16 +714,16 @@ def get_context(self, request, assets, notes): 'is_faculty': self.is_viewer_faculty } - # if self.record_owner: - # ctx['space_owner'] = ures.render_one(request, self.record_owner) + if self.record_owner: + ctx['space_owner'] = ures.render_one(request, self.record_owner) ctx['active_filters'] = {} for key, val in request.GET.items(): if (key in self.valid_filters or key.startswith('vocabulary-')): ctx['active_filters'][key] = val - # ctx['editable'] = self.viewing_own_records - # ctx['citable'] = citable + ctx['editable'] = self.viewing_own_records + ctx['citable'] = citable # render the assets ares = AssetResource(include_annotations=include_annotations) From b8ab18e7f197048ee95ebabbf13d1c609412f8f9 Mon Sep 17 00:00:00 2001 From: c0cky Date: Sat, 20 Dec 2014 19:36:41 -0500 Subject: [PATCH 15/58] added onomy_url attribute --- mediathread/taxonomy/models.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/mediathread/taxonomy/models.py b/mediathread/taxonomy/models.py index e68ffaaff..1b97e536a 100644 --- a/mediathread/taxonomy/models.py +++ b/mediathread/taxonomy/models.py @@ -21,6 +21,7 @@ class Vocabulary(models.Model): display_name = models.CharField(max_length=50) description = models.TextField(null=True, blank=True) single_select = models.BooleanField(default=False) + onomy_url = models.TextField(null=True, blank=True) # Map this taxonomy to something else. like a course. content_type = models.ForeignKey(ContentType) object_id = models.PositiveIntegerField() @@ -42,23 +43,23 @@ class VocabularyForm(forms.ModelForm): class Meta: model = Vocabulary -class Onomy(models.Model): - url = models.CharField(max_length=100) - vocabulary = models.ForeignKey(Vocabulary) +#class Onomy(models.Model): +# url = models.CharField(max_length=100) +# vocabulary = models.ForeignKey(Vocabulary) - def save(self, force_insert=False, force_update=False): - self.name = slugify(self.display_name) - super(Vocabulary, self).save(force_insert, force_update) +# def save(self, force_insert=False, force_update=False): +# self.name = slugify(self.display_name) +# super(Vocabulary, self).save(force_insert, force_update) - def to_json(self): - return { - 'onomy_url':self.onomy_url, - 'vocabulary': self.vocabulary - } +# def to_json(self): +# return { +# 'onomy_url':self.onomy_url, +# 'vocabulary': self.vocabulary +# } -class OnomyForm(forms.ModelForm): - class Meta: - model = Onomy +#class OnomyForm(forms.ModelForm): +# class Meta: +# model = Onomy class Term(models.Model): From d1150edb0f63379050682d9d8b618cb51bd3a431 Mon Sep 17 00:00:00 2001 From: c0cky Date: Sat, 20 Dec 2014 19:55:18 -0500 Subject: [PATCH 16/58] fixed my own mistakes --- mediathread/taxonomy/api.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/mediathread/taxonomy/api.py b/mediathread/taxonomy/api.py index 30d333b46..05cd0ad20 100644 --- a/mediathread/taxonomy/api.py +++ b/mediathread/taxonomy/api.py @@ -84,18 +84,12 @@ def render_one(self, request, term): class VocabularyValidation(Validation): def is_valid(self, bundle, request=None): errors = {} - print "DID WE STRIKE GOLD CAMRON?" - print bundle - print Vocabulary - print Validation a = Vocabulary.objects.filter( content_type_id=bundle.data['content_type_id'], display_name=bundle.data['display_name'], object_id=bundle.data['object_id'] ) - print "a" - print a if len(a) > 0: # vocabulary exists with this name if 'pk' not in bundle.data or a[0].pk != int(bundle.data['pk']): # a vocabulary already exists with this name @@ -111,8 +105,6 @@ def read_list(self, object_list, bundle): course_type = ContentType.objects.get_for_model(request.course) object_list = object_list.filter(content_type=course_type, object_id=request.course.id) - print course_type - print object_list[0] return object_list.order_by('id') From e3d6da09a5a4cad956abdd11579ef5f817dfb748 Mon Sep 17 00:00:00 2001 From: c0cky Date: Sat, 20 Dec 2014 21:36:28 -0500 Subject: [PATCH 17/58] fixed import error --- mediathread/assetmgr/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediathread/assetmgr/urls.py b/mediathread/assetmgr/urls.py index 949bc86c0..829df00a8 100644 --- a/mediathread/assetmgr/urls.py +++ b/mediathread/assetmgr/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import patterns, url +from django.conf.urls import patterns, url from mediathread.assetmgr.views import AssetWorkspaceView, AssetReferenceView import os.path From eee460d264e0e6ea6b6c5a825ef2f04b717c70d6 Mon Sep 17 00:00:00 2001 From: c0cky Date: Mon, 22 Dec 2014 20:19:09 -0500 Subject: [PATCH 18/58] merge conflicts --- media/css/mediathread.css | 14 + media/js/app/tiny_mce_init3.js | 2 +- media/templates/gallery.mustache | 4 +- mediathread/api.py | 61 +- .../migrations/0003_add_upload_permission.py | 6 +- mediathread/assetmgr/models.py | 25 +- mediathread/assetmgr/tests/test_api.py | 714 +++++++++++------- mediathread/assetmgr/tests/test_models.py | 415 +++++----- .../assetmgr/tests/test_templatetags.py | 23 +- mediathread/assetmgr/tests/test_views.py | 367 +++++---- mediathread/discussions/models.py | 93 +-- mediathread/discussions/urls.py | 2 +- mediathread/discussions/utils.py | 14 - mediathread/djangosherd/models.py | 20 +- mediathread/djangosherd/tests/__init__.py | 4 +- mediathread/djangosherd/tests/test_model.py | 87 ++- mediathread/djangosherd/views.py | 8 +- mediathread/factories.py | 155 +++- mediathread/main/admin.py | 17 +- mediathread/main/clumper.py | 4 +- mediathread/main/course_details.py | 41 +- mediathread/main/features/homepage.feature | 2 +- mediathread/main/features/instructor.feature | 10 +- mediathread/main/forms.py | 79 +- mediathread/main/models.py | 41 + mediathread/main/tests/__init__.py | 6 +- mediathread/main/tests/test_api.py | 42 +- mediathread/main/tests/test_views.py | 586 +++++++++++--- mediathread/main/views.py | 289 ++++--- mediathread/mixins.py | 12 +- mediathread/projects/api.py | 2 +- mediathread/projects/tests/__init__.py | 2 +- mediathread/projects/tests/test_api.py | 320 +++++--- mediathread/projects/tests/test_homepage.py | 311 ++++---- mediathread/projects/tests/test_model.py | 408 +++++----- .../projects/tests/test_templatetags.py | 19 +- mediathread/projects/tests/test_views.py | 143 ++-- mediathread/projects/urls.py | 2 +- mediathread/reports/urls.py | 2 +- mediathread/reports/views.py | 22 +- mediathread/settings_production.py | 1 + mediathread/settings_shared.py | 63 +- mediathread/settings_test.py | 3 +- mediathread/taxonomy/tests/__init__.py | 3 +- mediathread/taxonomy/tests/test_api.py | 149 +++- mediathread/taxonomy/tests/test_views.py | 85 +-- mediathread/taxonomy/urls.py | 2 +- mediathread/templates/base.html | 43 +- .../templates/dashboard/base_dashboard.html | 19 +- .../dashboard/class_assignments.html | 6 +- .../dashboard/class_manage_sources.html | 16 +- .../templates/dashboard/class_settings.html | 139 +++- .../templates/dashboard/class_summary.html | 5 +- .../djangosherd/annotator_resources.html | 9 +- .../djangosherd/annotator_resources_css.html | 5 + mediathread/templates/homepage.html | 56 +- mediathread/templates/main/contact.html | 202 +---- .../templates/main/course_request.html | 315 +------- .../main/course_request_description.txt | 6 +- .../templates/registration/description.html | 4 - mediathread/templates/registration/login.html | 3 + mediathread/templates/taxonomy/taxonomy.html | 14 +- mediathread/urls.py | 84 ++- requirements.txt | 25 +- requirements/js.txt | 4 + scripts/lettuce_base.db | Bin 479232 -> 479232 bytes structuredcollaboration/admin.py | 6 + structuredcollaboration/models.py | 11 - structuredcollaboration/policies.py | 6 +- structuredcollaboration/tests/test_models.py | 20 +- structuredcollaboration/urls.py | 2 +- 71 files changed, 3246 insertions(+), 2434 deletions(-) diff --git a/media/css/mediathread.css b/media/css/mediathread.css index e4107161e..45af97840 100644 --- a/media/css/mediathread.css +++ b/media/css/mediathread.css @@ -699,6 +699,10 @@ input.autocomplete.default { color: #999; } +input[name='decoy'] { + display: none !important; +} + /* ########## jQuery UI extensions/overrides ########## */ @@ -2917,6 +2921,16 @@ div.gallery-item h4.asset_title { { display:inline-block; } + +.sherd-flowplayer5-wrapper, +.sherd-flowplayer5-wrapper div { + display: block; +} + +.sherd-flowplayer5 { + margin: 0 auto; +} + .asset-display { text-align:center; diff --git a/media/js/app/tiny_mce_init3.js b/media/js/app/tiny_mce_init3.js index d0a92c18f..9191269af 100644 --- a/media/js/app/tiny_mce_init3.js +++ b/media/js/app/tiny_mce_init3.js @@ -5,7 +5,7 @@ var tiny_mce_settings = { editor_selector: "mceEditor", entity_encoding : "numeric", /*CUSTOM CCNMTL: added 'citation' and 'editorwindow' --see bottom for explicit loading from a location */ - plugins: "searchreplace,table,-citation,inlinepopups,-editorwindow,xhtmlxtras,paste", + plugins: "searchreplace,table,-citation,inlinepopups,-editorwindow,xhtmlxtras,paste,wordcount", /* CUSTOM CCNMTL: visual is set to false, so anchor tags don't get messed up. This is probably a bug to be reported to tinyMCE */ visual: false, diff --git a/media/templates/gallery.mustache b/media/templates/gallery.mustache index e366b8975..0a0108805 100644 --- a/media/templates/gallery.mustache +++ b/media/templates/gallery.mustache @@ -132,7 +132,7 @@ data-placeholder="Select tags" multiple="multiple"> {{#active_tags}} - + {{/active_tags}}
    @@ -143,7 +143,7 @@
    diff --git a/mediathread/api.py b/mediathread/api.py index 244457e13..0df2488e6 100644 --- a/mediathread/api.py +++ b/mediathread/api.py @@ -1,21 +1,22 @@ -#pylint: disable-msg=R0904 -from courseaffils.lib import get_public_name -from courseaffils.models import Course, CourseInfo +# pylint: disable-msg=R0904 +'''From the TastyPie Docs: +Authentication is the component needed to verify who a certain user +is and to validate their access to the API. +Authentication answers the question "Who is this person?" +This usually involves requiring credentials, such as an API key or +username/password or oAuth tokens.''' + from django.contrib.auth.models import User, Group from tagging.models import Tag -from tastypie import fields from tastypie.authentication import Authentication from tastypie.authorization import Authorization from tastypie.constants import ALL from tastypie.resources import ModelResource - -'''From the TastyPie Docs: -Authentication is the component needed to verify who a certain user -is and to validate their access to the API. -Authentication answers the question "Who is this person?" -This usually involves requiring credentials, such as an API key or -username/password or oAuth tokens.''' +from courseaffils.lib import get_public_name +from courseaffils.models import Course, CourseInfo +from mediathread.djangosherd.models import SherdNote +from tastypie import fields class ClassLevelAuthentication(Authentication): @@ -63,9 +64,8 @@ class Meta: allowed_methods = ['get'] authentication = ClassLevelAuthentication() authorization = UserAuthorization() - ordering = 'id' - filtering = {'id': ALL, - 'username': ALL} + ordering = ['last_name', 'first_name', 'username'] + filtering = {'id': ALL, 'username': ALL} def dehydrate(self, bundle): bundle.data['public_name'] = get_public_name(bundle.obj, @@ -78,6 +78,7 @@ def render_one(self, request, user): return dehydrated.data def render_list(self, request, lst): + lst = lst.order_by('last_name', 'first_name', 'username') data = [] for user in lst: bundle = self.build_bundle(obj=user, request=request) @@ -87,9 +88,10 @@ def render_list(self, request, lst): class GroupResource(ModelResource): - user_set = fields.ToManyField('mediathread.api.UserResource', - 'user_set', - full=True) + user_set = fields.ToManyField( + 'mediathread.api.UserResource', full=True, + attribute=lambda bundle: bundle.obj.user_set.all().order_by( + "last_name", "first_name", "username")) class Meta: queryset = Group.objects.none() @@ -138,6 +140,31 @@ def render_related(self, request, object_list): data.append(dehydrated.data) return data + def render_for_course(self, request, object_list): + notes = SherdNote.objects.filter(asset__course=request.course) + tags = Tag.objects.usage_for_queryset(notes) + tags.sort(lambda a, b: cmp(a.name.lower(), b.name.lower())) + + counts = [] + if len(object_list) > 0: + counts = Tag.objects.usage_for_queryset(object_list, counts=True) + + data = [] + tag_last = len(tags) - 1 + for idx, tag in enumerate(tags): + if idx == tag_last: + setattr(tag, 'last', idx == tag_last) + try: + x = counts.index(tag) + setattr(tag, 'count', counts[x].count) + except ValueError: + setattr(tag, 'count', 0) + + bundle = self.build_bundle(obj=tag, request=request) + dehydrated = self.full_dehydrate(bundle) + data.append(dehydrated.data) + return data + class CourseMemberAuthorization(Authorization): diff --git a/mediathread/assetmgr/migrations/0003_add_upload_permission.py b/mediathread/assetmgr/migrations/0003_add_upload_permission.py index 22504ab53..57bcf4bd1 100644 --- a/mediathread/assetmgr/migrations/0003_add_upload_permission.py +++ b/mediathread/assetmgr/migrations/0003_add_upload_permission.py @@ -3,11 +3,15 @@ import datetime from south.db import db from south.v2 import DataMigration -from django.db import models +from django.db import models, connection +from django.db.transaction import set_autocommit + class Migration(DataMigration): def forwards(self, orm): + if connection.vendor == 'sqlite': + set_autocommit(True) ct, created = orm['contenttypes.ContentType'].objects.get_or_create( model='asset', app_label='assetmgr') # model must be lowercase! perm, created = orm['auth.permission'].objects.get_or_create( diff --git a/mediathread/assetmgr/models.py b/mediathread/assetmgr/models.py index bf67e1d06..d544b0ca0 100755 --- a/mediathread/assetmgr/models.py +++ b/mediathread/assetmgr/models.py @@ -18,20 +18,21 @@ def default_url_processor(source, request, obj=None): class AssetManager(models.Manager): + def get_by_args(self, args, **constraints): "args typically is request.GET" criteria = Asset.good_args(args) if not criteria: - return False + return (False, None) qry = reduce(lambda x, y: x | y, # composable Q's [models.Q(label=k, url=args[k], primary=True) for k in criteria]) sources = Source.objects.filter(qry, **constraints) if sources: - return sources[0].asset + return (True, sources[0].asset) else: - return None + return (True, None) def archives(self): return self.filter(Q(source__primary=True) & @@ -66,26 +67,22 @@ def migrate(self, assets, course, user, faculty, object_map, new_asset = Asset.objects.migrate_one(old_asset, course, user) + object_map['assets'][old_asset.id] = new_asset notes = note_model.objects.get_related_notes( - [old_asset], None, faculty) + [old_asset], None, faculty, True) + + # remove all extraneous global annotations + notes = notes.filter(author__id__in=faculty) for old_note in notes: - if (not old_note.is_global_annotation() and - old_note.id not in object_map['notes']): + if (old_note.id not in object_map['notes']): new_note = note_model.objects.migrate_one( old_note, new_asset, user, include_tags, include_notes) object_map['notes'][old_note.id] = new_note - # migrate the requesting user's global annotation - # on this asset, if it exists - gann = old_asset.global_annotation(user, False) - if gann: - note_model.objects.migrate_one(gann, new_asset, user, - include_tags, include_notes) - return object_map def migrate_one(self, asset, course, user): @@ -165,7 +162,7 @@ class Asset(models.Model): 'image') # not good for uniqueness - fundamental_labels = ('archive', 'url',) + fundamental_labels = ('archive',) primary_labels = useful_labels + fundamental_labels class Meta: diff --git a/mediathread/assetmgr/tests/test_api.py b/mediathread/assetmgr/tests/test_api.py index f0b7cf28b..e998b1fac 100644 --- a/mediathread/assetmgr/tests/test_api.py +++ b/mediathread/assetmgr/tests/test_api.py @@ -1,67 +1,222 @@ # pylint: disable-msg=R0904 # pylint: disable-msg=E1103 +import json + from courseaffils.models import Course -from datetime import datetime, timedelta +from django.test.client import RequestFactory +from django.test.testcases import TestCase +from tagging.models import Tag + from mediathread.api import TagResource -from mediathread.assetmgr.models import Asset +from mediathread.djangosherd.models import SherdNote +from mediathread.factories import MediathreadTestMixin, UserFactory, \ + AssetFactory, SherdNoteFactory from mediathread.main import course_details -from tagging.models import Tag -from tastypie.test import ResourceTestCase -import json -class AssetApiTest(ResourceTestCase): - # Use ``fixtures`` & ``urls`` as normal. See Django's ``TestCase`` - # documentation for the gory details. - fixtures = ['unittest_sample_course.json'] +class AssetApiTest(MediathreadTestMixin, TestCase): + + def setUp(self): + self.setup_sample_course() + self.setup_alternate_course() + + # instructor that sees both Sample Course & Alternate Course + self.instructor_three = UserFactory(username='instructor_three') + self.add_as_faculty(self.sample_course, self.instructor_three) + self.add_as_faculty(self.alt_course, self.instructor_three) + + self.sample_course = Course.objects.get(title='Sample Course') + self.alt_course = Course.objects.get(title="Alternate Course") + + self.asset1 = AssetFactory.create(course=self.sample_course, + author=self.instructor_one, + primary_source='image') + + self.student_note = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',student_one_selection', + body='student one selection note', range1=0, range2=1) + self.student_ga = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',image, student_one_global,', + body='student one global note', + title=None, range1=None, range2=None) + self.instructor_note = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_selection,', + body='instructor one selection note', range1=0, range2=1) + self.instructor_ga = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_global,', + body='instructor one global note', + title=None, range1=None, range2=None) + + self.asset2 = AssetFactory.create(course=self.sample_course, + author=self.instructor_one, + primary_source='video') + self.asset2_instructor_note = SherdNoteFactory( + asset=self.asset2, author=self.instructor_one, + tags=',video, instructor_one_selection,', + body='instructor one selection note', range1=0, range2=1) + self.asset2_instructor_ga = SherdNoteFactory( + asset=self.asset2, author=self.instructor_one, + tags=',video, instructor_one_global,', + body='instructor one global note', + title=None, range1=None, range2=None) def get_credentials(self): return None def assertAssetEquals(self, asset, title, author, - primary_type, selection_ids, thumb_url): + primary_type, selection_ids): self.assertEquals(asset['title'], title) self.assertEquals(asset['author']['public_name'], author) self.assertEquals(asset['primary_type'], primary_type) - self.assertEquals(asset['thumb_url'], thumb_url) self.assertEquals(len(asset['annotations']), len(selection_ids)) for idx, selection in enumerate(asset['annotations']): self.assertEquals(int(selection['id']), selection_ids[idx]) - def test_student_get_my_collection(self): - username = "test_student_one" - password = "test" - self.assert_(self.client.login(username=username, password=password)) + def test_getall_as_student(self): + self.assertTrue( + self.client.login(username=self.student_two.username, + password="test")) - response = self.client.get( - "/api/asset/user/test_student_one/", - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/asset/?annotations=true' + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') the_json = json.loads(response.content) + objects = the_json['assets'] + self.assertEquals(len(objects), 2) + + selections = [self.student_note.id, self.instructor_note.id] + self.assertAssetEquals(objects[0], self.asset1.title, + 'Instructor One', 'image', selections) + self.assertFalse('global_annotation' in objects[0]) + + self.assertAssetEquals( + objects[1], self.asset2.title, + 'Instructor One', 'video', [self.asset2_instructor_note.id]) + self.assertFalse('global_annotation' in objects[1]) + + def test_restricted_getall_as_student(self): + # Set course details to restricted + self.sample_course.add_detail(course_details.SELECTION_VISIBILITY_KEY, + 0) + + self.assertTrue( + self.client.login(username=self.student_two.username, + password="test")) + + url = '/api/asset/?annotations=true' + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) + + the_json = json.loads(response.content) + objects = the_json['assets'] + self.assertEquals(len(objects), 2) + + selections = [self.instructor_note.id] + self.assertAssetEquals(objects[0], self.asset1.title, + 'Instructor One', 'image', selections) + self.assertFalse('global_annotation' in objects[0]) + + self.assertAssetEquals( + objects[1], self.asset2.title, + 'Instructor One', 'video', [self.asset2_instructor_note.id]) + self.assertFalse('global_annotation' in objects[1]) + + def test_getall_as_instructor(self): + self.assertTrue( + self.client.login(username=self.instructor_one.username, + password="test")) + + url = '/api/asset/?annotations=true' + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + + the_json = json.loads(response.content) + objects = the_json['assets'] + self.assertEquals(len(objects), 2) + + match = [x for x in objects if x['id'] == self.asset1.id] + self.assertEquals(len(match), 1) + selections = [self.student_note.id, self.instructor_note.id] + self.assertAssetEquals(match[0], self.asset1.title, + 'Instructor One', 'image', selections) + self.assertFalse('global_annotation' in objects[0]) + + match = [x for x in objects if x['id'] == self.asset2.id] + self.assertEquals(len(match), 1) + self.assertAssetEquals( + match[0], self.asset2.title, + 'Instructor One', 'video', [self.asset2_instructor_note.id]) + self.assertFalse('global_annotation' in objects[1]) + + def test_restricted_getall_as_instructor(self): + # Set course details to restricted + self.sample_course.add_detail(course_details.SELECTION_VISIBILITY_KEY, + 0) + self.test_getall_as_instructor() + + def test_getstudentlist_as_student_owner(self): + self.assert_(self.client.login(username=self.student_one.username, + password="test")) + record_owner = self.student_one.username + response = self.client.get( + "/api/asset/user/%s/?annotations=true" % record_owner, + {}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + + the_json = json.loads(response.content) + self.assertEquals(the_json['space_owner']['username'], + self.student_one.username) + self.assertEquals(the_json['space_viewer']['username'], + self.student_one.username) self.assertTrue(the_json['editable']) self.assertFalse(the_json['citable']) self.assertFalse(the_json['is_faculty']) self.assertEquals(len(the_json['assets']), 1) - self.assertEquals(len(the_json['assets'][0]['annotations']), 0) + self.assertEquals(len(the_json['assets'][0]['annotations']), 1) - def test_student_get_peer_collection(self): - username = "test_student_one" - password = "test" - self.assert_(self.client.login(username=username, password=password)) + annotations = the_json['assets'][0]['annotations'] + self.assertEquals(annotations[0]['title'], self.student_note.title) - record_owner = 'test_student_two' + # student one's tags + self.assertEquals(len(annotations[0]['metadata']['tags']), 1) + self.assertEquals(annotations[0]['metadata']['body'], + "student one selection note") + + self.assertTrue('global_annotation' in the_json['assets'][0]) + gla = the_json['assets'][0]['global_annotation'] + self.assertEquals(len(gla['metadata']['tags']), 2) + self.assertEquals(gla['metadata']['body'], + "student one global note") + + def test_restricted_getstudentlist_as_student_owner(self): + # Set course details to restricted + self.sample_course.add_detail(course_details.SELECTION_VISIBILITY_KEY, + 0) + self.test_getstudentlist_as_student_owner() + + def test_getstudentlist_as_student_viewer(self): + self.assert_(self.client.login(username=self.student_two.username, + password="test")) + + record_owner = self.student_one.username response = self.client.get( "/api/asset/user/%s/?annotations=true" % record_owner, - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + {}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') the_json = json.loads(response.content) - + self.assertEquals(the_json['space_owner']['username'], + self.student_one.username) + self.assertEquals(the_json['space_viewer']['username'], + self.student_two.username) self.assertFalse(the_json['editable']) self.assertFalse(the_json['citable']) self.assertFalse(the_json['is_faculty']) @@ -69,360 +224,359 @@ def test_student_get_peer_collection(self): self.assertEquals(len(the_json['assets'][0]['annotations']), 1) annotations = the_json['assets'][0]['annotations'] - self.assertEquals(annotations[0]['title'], 'Nice Tie') + self.assertEquals(annotations[0]['title'], self.student_note.title) # student two's tags self.assertEquals(len(annotations[0]['metadata']['tags']), 1) self.assertEquals(annotations[0]['metadata']['body'], - "student two selection note") + "student one selection note") self.assertTrue('global_annotation' in the_json['assets'][0]) gla = the_json['assets'][0]['global_annotation'] - self.assertEquals(len(gla['metadata']['tags']), 1) + self.assertEquals(len(gla['metadata']['tags']), 2) self.assertEquals(gla['metadata']['body'], - "student two item note") - - def test_student_getlist(self): - self.assertTrue( - self.api_client.client.login(username="test_student_one", - password="test")) + "student one global note") - url = '/api/asset/?annotations=true' - response = self.api_client.get(url, format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) - - the_json = self.deserialize(response) - objects = the_json['assets'] - self.assertEquals(len(objects), 4) + def test_restricted_getstudentlist_as_student_viewer(self): + self.sample_course.add_detail(course_details.SELECTION_VISIBILITY_KEY, + 0) - self.assertAssetEquals(objects[0], 'Mediathread: Introduction', - 'Instructor One', 'youtube', [2, 3, 17, 19], - 'http://localhost:8002/media/img/test/' - 'mediathread_introduction_thumb.jpg') + self.assert_(self.client.login(username=self.student_two.username, + password="test")) - self.assertAssetEquals( - objects[1], 'Project Portfolio', - 'test_instructor_two', 'image', [], None) + record_owner = self.student_one.username + response = self.client.get( + "/api/asset/user/%s/?annotations=true" % record_owner, + {}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertAssetEquals( - objects[2], 'MAAP Award Reception', - 'Instructor One', 'image', [5, 8, 10], - 'http://localhost:8002/media/img/test/maap_thumb.jpg') + the_json = json.loads(response.content) + self.assertEquals(the_json['space_owner']['username'], + self.student_one.username) + self.assertEquals(the_json['space_viewer']['username'], + self.student_two.username) + self.assertEquals(len(the_json['assets']), 1) + self.assertEquals(the_json['assets'][0]['annotation_count'], 0) - self.assertAssetEquals( - objects[3], - 'The Armory - Home to CCNMTL\'S CUMC Office', - 'Instructor One', 'image', [7], - 'http://localhost:8002/media/img/test/armory_thumb.jpg') + ga = the_json['assets'][0]['global_annotation'] + self.assertEquals(ga['author']['username'], 'student_one') - def test_student_getlist_sorted(self): - asset = Asset.objects.get(title='MAAP Award Reception') - asset.modified = datetime.now() + timedelta(days=1) - asset.save() + def test_getstudentlist_as_instructor(self): + self.assert_(self.client.login(username=self.instructor_one.username, + password="test")) - self.assertTrue( - self.api_client.client.login(username="test_student_one", - password="test")) + record_owner = self.student_one.username + response = self.client.get( + "/api/asset/user/%s/?annotations=true" % record_owner, + {}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') - url = '/api/asset/?annotations=true' - response = self.api_client.get(url, format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) + the_json = json.loads(response.content) + self.assertEquals(the_json['space_owner']['username'], + self.student_one.username) + self.assertEquals(the_json['space_viewer']['username'], + self.instructor_one.username) + self.assertFalse(the_json['editable']) + self.assertFalse(the_json['citable']) + self.assertTrue(the_json['is_faculty']) + self.assertEquals(len(the_json['assets']), 1) + self.assertEquals(len(the_json['assets'][0]['annotations']), 1) - the_json = self.deserialize(response) - objects = the_json['assets'] - self.assertEquals(len(objects), 4) + annotations = the_json['assets'][0]['annotations'] + self.assertEquals(annotations[0]['title'], self.student_note.title) - self.assertAssetEquals( - objects[0], 'MAAP Award Reception', - 'Instructor One', 'image', [5, 8, 10], - 'http://localhost:8002/media/img/test/maap_thumb.jpg') + self.assertEquals(len(annotations[0]['metadata']['tags']), 1) + self.assertEquals(annotations[0]['metadata']['body'], + "student one selection note") - self.assertAssetEquals(objects[1], 'Mediathread: Introduction', - 'Instructor One', 'youtube', [2, 3, 17, 19], - 'http://localhost:8002/media/img/test/' - 'mediathread_introduction_thumb.jpg') + self.assertTrue('global_annotation' in the_json['assets'][0]) + gla = the_json['assets'][0]['global_annotation'] + self.assertEquals(len(gla['metadata']['tags']), 2) + self.assertEquals(gla['metadata']['body'], + "student one global note") - def test_student_getlist_restricted(self): + def test_restricted_getstudentlist_as_instructor(self): # Set course details to restricted - sample_course = Course.objects.get(title="Sample Course") - sample_course.add_detail(course_details.SELECTION_VISIBILITY_KEY, 0) + self.sample_course.add_detail(course_details.SELECTION_VISIBILITY_KEY, + 0) + self.test_getstudentlist_as_instructor() + def test_getobject_as_student_owner(self): self.assertTrue( - self.api_client.client.login(username="test_student_one", - password="test")) - - url = '/api/asset/?annotations=true' - response = self.api_client.get(url, format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) + self.client.login(username=self.student_one.username, + password="test")) - the_json = self.deserialize(response) - objects = the_json['assets'] - self.assertEquals(len(objects), 4) - - self.assertAssetEquals(objects[0], 'Mediathread: Introduction', - 'Instructor One', 'youtube', [2, 3, 17, 19], - 'http://localhost:8002/media/img/test/' - 'mediathread_introduction_thumb.jpg') + response = self.client.get('/api/asset/%s/' % self.asset1.id, + {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) + the_json = json.loads(response.content) + selections = [self.student_note.id, self.instructor_note.id] + asset = the_json['assets'][str(self.asset1.id)] self.assertAssetEquals( - objects[1], - 'Project Portfolio', - 'test_instructor_two', 'image', [], - None) + asset, + self.asset1.title, + 'Instructor One', 'image', selections) - self.assertAssetEquals( - objects[2], 'MAAP Award Reception', - 'Instructor One', 'image', [5, 8], - 'http://localhost:8002/media/img/test/maap_thumb.jpg') + self.assertTrue('global_annotation' in asset) + self.assertEquals(asset['global_annotation']['id'], + self.student_ga.id) - self.assertAssetEquals( - objects[3], - 'The Armory - Home to CCNMTL\'S CUMC Office', - 'Instructor One', 'image', [7], - 'http://localhost:8002/media/img/test/armory_thumb.jpg') + def test_restricted_getobject_as_student_owner(self): + self.sample_course.add_detail(course_details.SELECTION_VISIBILITY_KEY, + 0) + self.test_getobject_as_student_owner() - def test_student_getobject(self): + def test_getobject_as_instructor_viewer(self): self.assertTrue( - self.api_client.client.login(username="test_student_one", - password="test")) + self.client.login(username=self.instructor_one.username, + password="test")) - response = self.api_client.get('/api/asset/2/', format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) - the_json = self.deserialize(response) + response = self.client.get('/api/asset/%s/' % self.asset1.id, + {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) + the_json = json.loads(response.content) + selections = [self.student_note.id, self.instructor_note.id] + asset = the_json['assets'][str(self.asset1.id)] self.assertAssetEquals( - the_json['assets']['2'], 'MAAP Award Reception', - 'Instructor One', 'image', [5, 8, 10], - 'http://localhost:8002/media/img/test/maap_thumb.jpg') - - def test_student_getobject_restricted(self): - # Set course details to restricted - sample_course = Course.objects.get(title="Sample Course") - sample_course.add_detail(course_details.SELECTION_VISIBILITY_KEY, 0) + asset, + self.asset1.title, + 'Instructor One', 'image', selections) - self.assertTrue( - self.api_client.client.login(username="test_student_one", - password="test")) + self.assertTrue('global_annotation' in asset) + self.assertEquals(asset['global_annotation']['id'], + self.instructor_ga.id) - response = self.api_client.get('/api/asset/2/', format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) - the_json = self.deserialize(response) + def test_restricted_getobject_as_instructor_viewer(self): + self.sample_course.add_detail(course_details.SELECTION_VISIBILITY_KEY, + 0) + self.test_getobject_as_instructor_viewer() - self.assertAssetEquals( - the_json['assets']['2'], 'MAAP Award Reception', - 'Instructor One', 'image', [5, 8], - 'http://localhost:8002/media/img/test/maap_thumb.jpg') - - def test_instructor_getlist(self): + def test_getobject_as_student_viewer(self): self.assertTrue( - self.api_client.client.login(username="test_instructor", - password="test")) - - response = self.api_client.get('/api/asset/?annotations=true', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) + self.client.login(username=self.student_two.username, + password="test")) - the_json = self.deserialize(response) - objects = the_json['assets'] - self.assertEquals(len(objects), 4) - - self.assertAssetEquals(objects[0], 'Mediathread: Introduction', - 'Instructor One', 'youtube', [2, 3, 17, 19], - 'http://localhost:8002/media/img/test/' - 'mediathread_introduction_thumb.jpg') - - self.assertAssetEquals( - objects[1], - 'Project Portfolio', - 'test_instructor_two', 'image', [], - None) + response = self.client.get('/api/asset/%s/' % self.asset1.id, + {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) + the_json = json.loads(response.content) + selections = [self.student_note.id, self.instructor_note.id] + asset = the_json['assets'][str(self.asset1.id)] self.assertAssetEquals( - objects[2], 'MAAP Award Reception', - 'Instructor One', 'image', [5, 8, 10], - 'http://localhost:8002/media/img/test/maap_thumb.jpg') + asset, + self.asset1.title, + 'Instructor One', 'image', selections) - self.assertAssetEquals( - objects[3], - 'The Armory - Home to CCNMTL\'S CUMC Office', - 'Instructor One', 'image', [7], - 'http://localhost:8002/media/img/test/armory_thumb.jpg') + self.assertFalse('global_annotation' in asset) - def test_instructor_getlist_restricted(self): + def test_restricted_getobject_as_student_viewer(self): # Set course details to restricted - sample_course = Course.objects.get(title="Sample Course") - sample_course.add_detail(course_details.SELECTION_VISIBILITY_KEY, 0) - - self.test_instructor_getlist() + self.sample_course.add_detail(course_details.SELECTION_VISIBILITY_KEY, + 0) - def test_instructor_getobject(self): self.assertTrue( - self.api_client.client.login(username="test_instructor", - password="test")) + self.client.login(username=self.student_two.username, + password="test")) - response = self.api_client.get('/api/asset/1/', format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) - the_json = self.deserialize(response) - - self.assertAssetEquals(the_json['assets']['1'], - 'Mediathread: Introduction', - 'Instructor One', 'youtube', [2, 3, 17, 19], - 'http://localhost:8002/media/img/test/' - 'mediathread_introduction_thumb.jpg') + response = self.client.get('/api/asset/%s/' % self.asset1.id, + {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) + the_json = json.loads(response.content) - def test_instructor_getobject_restricted(self): - # Set course details to restricted - sample_course = Course.objects.get(title="Sample Course") - sample_course.add_detail(course_details.SELECTION_VISIBILITY_KEY, 0) + selections = [self.instructor_note.id] + asset = the_json['assets'][str(self.asset1.id)] + self.assertAssetEquals( + asset, + self.asset1.title, + 'Instructor One', 'image', selections) - self.test_instructor_getobject() + self.assertFalse('global_annotation' in asset) - def test_nonclassmember_getobject(self): + def test_getobject_as_nonclassmember(self): # Student in Alternate Course attempts # to retrieve selections from Sample Course self.assertTrue( - self.api_client.client.login(username="test_student_alt", - password="test")) + self.client.login(username=self.alt_student.username, + password="test")) # Student One Selection - response = self.api_client.get('/api/asset/1/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = self.client.get('/api/asset/%s/' % self.asset1, + {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 404) def test_post_list(self): self.assertTrue( - self.api_client.client.login(username="test_instructor", - password="test")) - - self.assertHttpMethodNotAllowed(self.api_client.post( - '/api/asset/', format='json', data={})) + self.client.login(username=self.instructor_one.username, + password="test")) + response = self.client.post('/api/asset/', {}) + self.assertEquals(response.status_code, 405) def test_put_detail(self): self.assertTrue( - self.api_client.client.login(username="test_instructor", - password="test")) + self.client.login(username=self.instructor_one.username, + password="test")) - self.assertHttpMethodNotAllowed(self.api_client.put( - '/api/asset/2/', format='json', data={}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest')) + response = self.client.put('/api/asset/2/', {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 405) def test_delete(self): self.assertTrue( - self.api_client.client.login(username="test_instructor", - password="test")) + self.client.login(username=self.instructor_one.username, + password="test")) - self.assertHttpMethodNotAllowed(self.api_client.delete( - '/api/asset/2/', format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest')) + response = self.client.delete('/api/asset/2/', {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 405) def test_getobject_multiple_class_member_nocourse(self): self.assertTrue( - self.api_client.client.login(username="test_student_three", - password="test")) + self.client.login(username=self.instructor_three.username, + password="test")) # No course selection yet - response = self.api_client.get('/api/asset/1/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertHttpOK(response) + response = self.client.get('/api/asset/%s/' % self.asset1.id, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) self.assertEquals(response.templates[0].name, "courseaffils/select_course.html") def test_getobject_multiple_class_member_wrongcourse(self): self.assertTrue( - self.api_client.client.login(username="test_student_three", - password="test")) - - response = self.api_client.client.get( - '/?set_course=Alternate%20Course%20Members&next=/', follow=True) - self.assertHttpOK(response) - self.assertEquals(response.templates[0].name, "homepage.html") + self.client.login(username=self.instructor_three.username, + password="test")) + self.switch_course(self.client, self.alt_course) - response = self.api_client.get('/api/asset/1/', - format='json', follow=True, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = self.client.get('/api/asset/%s/' % self.asset1.id, + {}, follow=True, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 200) self.assertEquals(response.templates[0].name, "assetmgr/asset_not_found.html") def test_getobject_multiple_class_member_rightcourse(self): self.assertTrue( - self.api_client.client.login(username="test_student_three", - password="test")) - - response = self.api_client.client.get( - '/?set_course=Sample_Course_Students', follow=True) - self.assertHttpOK(response) - self.assertEquals(response.templates[0].name, "homepage.html") - response = self.api_client.get('/api/asset/1/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertHttpOK(response) - self.assertValidJSONResponse(response) - the_json = self.deserialize(response) - self.assertAssetEquals(the_json['assets']['1'], - 'Mediathread: Introduction', - 'Instructor One', 'youtube', [2, 3, 17, 19], - 'http://localhost:8002/media/img/test/' - 'mediathread_introduction_thumb.jpg') + self.client.login(username=self.instructor_three.username, + password="test")) + + self.switch_course(self.client, self.sample_course) + + response = self.client.get('/api/asset/%s/' % self.asset1.id, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) + the_json = json.loads(response.content) + + selections = [self.student_note.id, self.instructor_note.id] + asset = the_json['assets'][str(self.asset1.id)] + self.assertAssetEquals(asset, self.asset1.title, + 'Instructor One', 'image', selections) + + self.assertFalse('global_annotation' in asset) def test_getlist_multiple_class_member(self): self.assertTrue( - self.api_client.client.login(username="test_student_three", - password="test")) + self.client.login(username=self.instructor_three.username, + password="test")) - # Student One Selection from Sample Course - response = self.api_client.get('/api/asset/', format='json') - self.assertHttpOK(response) + # No course selected + response = self.client.get('/api/asset/', {}) + self.assertEquals(response.status_code, 200) self.assertEquals(response.templates[0].name, "courseaffils/select_course.html") # No dice, login to Alternate Course - response = self.api_client.client.get( - '/?set_course=Alternate%20Course%20Members&next=/', follow=True) - self.assertHttpOK(response) - self.assertEquals(response.templates[0].name, "homepage.html") + self.switch_course(self.client, self.alt_course) # Let's try this again -- asset list - response = self.api_client.get('/api/asset/?annotations=true', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = self.client.get('/api/asset/?annotations=true', + {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') - the_json = self.deserialize(response) + the_json = json.loads(response.content) objects = the_json['assets'] - self.assertEquals(len(objects), 1) + self.assertEquals(len(objects), 0) - self.assertAssetEquals(objects[0], 'Design Research', - 'test_instructor_alt', 'image', - [13, 14, 15], None) - - -class TagApiTest(ResourceTestCase): - fixtures = ['unittest_sample_course.json'] - - def get_credentials(self): - return None - - def test_render_list(self): - asset = Asset.objects.get(id=1) - tags = Tag.objects.usage_for_queryset(asset.sherdnote_set.all(), + def test_render_tag_list(self): + tags = Tag.objects.usage_for_queryset(self.asset1.sherdnote_set.all(), counts=True) resource = TagResource() lst = resource.render_list(None, tags) self.assertEquals(len(lst), 5) - self.assertEquals(lst[0]['count'], 1) + self.assertEquals(lst[0]['count'], 3) self.assertEquals(lst[0]['last'], False) - self.assertEquals(lst[0]['name'], 'test_instructor_item') + self.assertEquals(lst[0]['name'], 'image') + + self.assertEquals(lst[1]['count'], 1) + self.assertEquals(lst[1]['last'], False) + self.assertEquals(lst[1]['name'], 'instructor_one_global') + + self.assertEquals(lst[2]['count'], 1) + self.assertEquals(lst[2]['last'], False) + self.assertEquals(lst[2]['name'], 'instructor_one_selection') + + self.assertEquals(lst[3]['count'], 1) + self.assertEquals(lst[3]['last'], False) + self.assertEquals(lst[3]['name'], 'student_one_global') self.assertEquals(lst[4]['count'], 1) self.assertEquals(lst[4]['last'], True) - self.assertEquals(lst[4]['name'], 'youtube') + self.assertEquals(lst[4]['name'], 'student_one_selection') + + def test_render_related_tag_list(self): + request = RequestFactory().get('') + request.course = self.sample_course + + notes = SherdNote.objects.filter(author=self.student_one) + lst = TagResource().render_related(request, notes) + self.assertEquals(len(lst), 3) + + self.assertEquals(lst[0]['count'], 1) + self.assertEquals(lst[0]['last'], False) + self.assertEquals(lst[0]['name'], 'image') + + self.assertEquals(lst[1]['count'], 1) + self.assertEquals(lst[1]['last'], False) + self.assertEquals(lst[1]['name'], 'student_one_global') + + self.assertEquals(lst[2]['count'], 1) + self.assertEquals(lst[2]['last'], True) + self.assertEquals(lst[2]['name'], 'student_one_selection') + + def test_render_tag_list_for_course(self): + request = RequestFactory().get('') + request.course = self.sample_course + + notes = SherdNote.objects.filter(author=self.student_one) + lst = TagResource().render_for_course(request, notes) + self.assertEquals(len(lst), 6) + + self.assertEquals(lst[0]['count'], 1) + self.assertEquals(lst[0]['last'], False) + self.assertEquals(lst[0]['name'], 'image') + + self.assertEquals(lst[1]['count'], 0) + self.assertEquals(lst[1]['last'], False) + self.assertEquals(lst[1]['name'], 'instructor_one_global') + + self.assertEquals(lst[2]['count'], 0) + self.assertEquals(lst[2]['last'], False) + self.assertEquals(lst[2]['name'], 'instructor_one_selection') + + self.assertEquals(lst[3]['count'], 1) + self.assertEquals(lst[3]['last'], False) + self.assertEquals(lst[3]['name'], 'student_one_global') + + self.assertEquals(lst[4]['count'], 1) + self.assertEquals(lst[4]['last'], False) + self.assertEquals(lst[4]['name'], 'student_one_selection') + + self.assertEquals(lst[5]['count'], 0) + self.assertEquals(lst[5]['last'], True) + self.assertEquals(lst[5]['name'], 'video') diff --git a/mediathread/assetmgr/tests/test_models.py b/mediathread/assetmgr/tests/test_models.py index f9f4dc92d..24a6246ee 100644 --- a/mediathread/assetmgr/tests/test_models.py +++ b/mediathread/assetmgr/tests/test_models.py @@ -1,40 +1,108 @@ -#pylint: disable-msg=R0904 -from courseaffils.models import Course -from django.contrib.auth.models import User +# pylint: disable-msg=R0904 from django.test import TestCase + from mediathread.assetmgr.models import Asset, Source from mediathread.djangosherd.models import SherdNote - - -class AssetTest(TestCase): - fixtures = ['unittest_sample_course.json'] +from mediathread.factories import MediathreadTestMixin, AssetFactory, \ + UserFactory, SherdNoteFactory, ProjectFactory + + +class AssetTest(MediathreadTestMixin, TestCase): + def setUp(self): + self.setup_sample_course() + self.setup_alternate_course() + + # instructor that sees both Sample Course & Alternate Course + self.instructor_three = UserFactory(username='instructor_three') + self.add_as_faculty(self.sample_course, self.instructor_three) + self.add_as_faculty(self.alt_course, self.instructor_three) + + # Sample Course Image Asset + self.asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image') + + self.student_note = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',student_one_selection', + body='student one selection note', range1=0, range2=1) + self.student_ga = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',student_one_item', + body='student one item note', + title=None, range1=None, range2=None) + self.instructor_note = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_selection,', + body='instructor one selection note', range1=0, range2=1) + self.instructor_ga = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_item,', + body='instructor one item note', + title=None, range1=None, range2=None) def test_unicode(self): - asset = Asset.objects.get(title="Mediathread: Introduction") - self.assertEquals(asset.__unicode__(), - 'Mediathread: Introduction <1> (Sample Course)') + asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image', + author=self.instructor_one, + title="Item Title") + self.assertEquals(asset1.__unicode__(), + 'Item Title <%s> (Sample Course)' % asset1.id) + + def test_get_by_args(self): + success, asset = Asset.objects.get_by_args( + {'foo': 'bar'}, asset__course=self.sample_course) + self.assertFalse(success) + self.assertIsNone(asset) + + data = {'title': 'Z', + 'url': 'https://www.google.com/search=X', + 'metadata-image': '', + 'image': 'data:image/jpeg;base64,/9j/'} + success, asset = Asset.objects.get_by_args( + data, asset__course=self.sample_course) + self.assertTrue(success) + self.assertIsNone(asset) + + asset1 = AssetFactory.create(course=self.sample_course, + primary_source='mp4_pseudo', + author=self.instructor_one) + + data = {'title': asset1.title, + 'mp4_pseudo': asset1.primary.url} + + success, asset = Asset.objects.get_by_args( + data, asset__course=self.sample_course) + self.assertTrue(success) + self.assertEquals(asset1, asset) def test_metadata(self): - asset = Asset.objects.get(title="Mediathread: Introduction") - ctx = asset.metadata() + asset1 = AssetFactory.create( + course=self.sample_course, primary_source='image', + author=self.instructor_one, + metadata_blob='{"category": ["Education"], "author": ["CCNMTL"]}', + title="Item Title") + + ctx = asset1.metadata() self.assertEquals(ctx['author'], [u'CCNMTL']) self.assertEquals(ctx['category'], [u'Education']) - asset = Asset.objects.get(title="MAAP Award Reception") - ctx = asset.metadata() + asset2 = AssetFactory.create(course=self.sample_course) + ctx = asset2.metadata() self.assertEquals(len(ctx.keys()), 0) def test_video(self): + asset = AssetFactory.create( + course=self.sample_course, primary_source='youtube') + # youtube -- asset #1 - asset = Asset.objects.get(id=1) self.assertEquals(asset.media_type(), 'video') self.assertFalse(asset.primary.is_image()) self.assertFalse(asset.primary.is_archive()) self.assertFalse(asset.primary.is_audio()) def test_image(self): - # image -- asset #2 - asset = Asset.objects.get(id=2) + asset = AssetFactory.create( + course=self.sample_course, primary_source='image') self.assertEquals(asset.media_type(), 'image') self.assertTrue(asset.primary.is_image()) @@ -42,250 +110,197 @@ def test_image(self): self.assertFalse(asset.primary.is_audio()) def test_migrate_many(self): - from_course = Course.objects.get(title="Sample Course") + from_course = self.sample_course faculty = [user.id for user in from_course.faculty.all()] - course = Course.objects.get(title="Alternate Course") - self.assertEquals(len(course.asset_set.all()), 1) + to_course = self.alt_course + self.assertEquals(to_course.asset_set.count(), 0) - user = User.objects.get(username='test_instructor_two') - titles = ['Mediathread: Introduction', 'MAAP Award Reception'] - assets = Asset.objects.filter(title__in=titles) + assets = Asset.objects.filter(course=self.sample_course) object_map = {'assets': {}, 'notes': {}} object_map = Asset.objects.migrate( - assets, course, user, faculty, object_map, + assets, to_course, self.instructor_three, faculty, object_map, True, True) - self.assertEquals(len(course.asset_set.all()), 3) - asset = object_map['assets'][1] - self.assertNotEquals(asset.id, 1) - self.assertEquals(asset.title, "Mediathread: Introduction") - self.assertEquals(asset.course, course) - self.assertEquals(asset.author, user) - self.assertEquals(len(asset.sherdnote_set.all()), 5) - - try: - asset.sherdnote_set.get(title='Whole Item Selection') - asset.sherdnote_set.get(title='Annotations') - asset.sherdnote_set.get(title='Manage Sources') - asset.sherdnote_set.get(title='Video Selection Is Time-based') - except SherdNote.DoesNotExist: - self.assertTrue(False) - - gann = asset.global_annotation(user, False) - self.assertTrue(gann is not None) - self.assertEquals(gann.tags, ',test_instructor_two') - self.assertEquals(gann.body, 'test_instructor_two notes') - - asset = object_map['assets'][2] - self.assertNotEquals(asset.id, 2) - self.assertEquals(asset.title, "MAAP Award Reception") - self.assertEquals(asset.course, course) - self.assertEquals(asset.author, user) + self.assertEquals(to_course.asset_set.count(), 1) + asset = object_map['assets'][self.asset1.id] + self.assertEquals(asset.title, self.asset1.title) + self.assertEquals(asset.course, to_course) + self.assertEquals(asset.author, self.instructor_three) self.assertEquals(len(asset.sherdnote_set.all()), 2) - try: - asset.sherdnote_set.get(title='Our esteemed leaders') - except SherdNote.DoesNotExist: - self.assertTrue(False) + # instructor note exists with tags + note = asset.sherdnote_set.get(title=self.instructor_note.title) + self.assertEquals(note.tags, ',image, instructor_one_selection,') + self.assertEquals(note.body, 'instructor one selection note') - gann = asset.global_annotation(user, False) + gann = asset.global_annotation(self.instructor_three, False) self.assertTrue(gann is not None) - self.assertEquals(gann.tags, '') - self.assertEquals(gann.body, None) + self.assertEquals(gann.tags, ',image, instructor_one_item,') + self.assertEquals(gann.body, 'instructor one item note') def test_migrate_one(self): - asset = Asset.objects.get(id=1) - self.assertEquals(asset.title, "Mediathread: Introduction") + new_course = self.alt_course + new_user = self.alt_instructor - new_course = Course.objects.get(title="Alternate Course") + asset = Asset.objects.migrate_one( + self.asset1, new_course, new_user) + self.assertEquals(asset.author, new_user) + self.assertEquals(asset.course, new_course) - new_user = User.objects.get(username='test_instructor_alt') + self.assertEquals(asset.sherdnote_set.count(), 1) - new_asset = Asset.objects.migrate_one(asset, new_course, new_user) - self.assertEquals(new_asset.author, new_user) - self.assertEquals(new_asset.course, new_course) + gann = asset.sherdnote_set.all()[0] + self.assertTrue(gann.is_global_annotation()) + self.assertEquals(gann.tags, '') + self.assertEquals(gann.body, None) - self.assertEquals(new_asset.media_type(), 'video') - self.assertFalse(new_asset.primary.is_image()) - self.assertFalse(new_asset.primary.is_archive()) - self.assertFalse(new_asset.primary.is_audio()) + def test_migrate_note_global_annotations(self): + alt_asset = AssetFactory.create(course=self.alt_course, + primary_source='image') # migrate a global annotation - global_annotation = SherdNote.objects.get(id=1) - global_note = SherdNote.objects.migrate_one(global_annotation, - new_asset, - new_user, True, True) + global_note = SherdNote.objects.migrate_one( + self.instructor_ga, alt_asset, self.instructor_three, True, True) self.assertTrue(global_note.is_global_annotation()) - self.assertEquals(global_note.author, new_user) + self.assertEquals(global_note.author, self.instructor_three) self.assertEquals(global_note.title, None) - self.assertEquals(global_note.tags, ',youtube, test_instructor_item') - self.assertEquals(global_note.body, u'All credit to Mark and Casey') + self.assertEquals(global_note.tags, self.instructor_ga.tags) + self.assertEquals(global_note.body, self.instructor_ga.body) # try to migrate another global annotation as well # the global annotation that was already created will come back - another_global_annotation = SherdNote.objects.get(id=20) - another_note = SherdNote.objects.migrate_one(another_global_annotation, - new_asset, - new_user, - True, - True) + another_note = SherdNote.objects.migrate_one( + self.student_ga, alt_asset, self.instructor_three, True, True) self.assertEquals(another_note, global_note) - selected_annotation = SherdNote.objects.get(id=2) - new_note = SherdNote.objects.migrate_one(selected_annotation, - new_asset, - new_user, - True, - True) + def test_migrate_note_regular_annotations(self): + alt_asset = AssetFactory.create(course=self.alt_course, + primary_source='image') + + # migrate a regular annotation + new_note = SherdNote.objects.migrate_one( + self.instructor_note, alt_asset, self.instructor_three, True, True) self.assertFalse(new_note.is_global_annotation()) - self.assertEquals(new_note.author, new_user) - self.assertEquals(new_note.title, 'Manage Sources') - self.assertEquals(new_note.tags, ',video') - self.assertEquals(new_note.body, '') + self.assertEquals(new_note.author, self.instructor_three) + self.assertEquals(new_note.title, self.instructor_note.title) + self.assertEquals(new_note.tags, self.instructor_note.tags) + self.assertEquals(new_note.body, self.instructor_note.body) def test_migrate_one_duplicates(self): - asset = Asset.objects.get(id=1) - self.assertEquals(asset.title, "Mediathread: Introduction") + new_asset = Asset.objects.migrate_one( + self.asset1, self.alt_course, self.alt_instructor) + self.assertEquals(new_asset.author, self.alt_instructor) + self.assertEquals(new_asset.course, self.alt_course) - new_course = Course.objects.get(title="Alternate Course") - - new_user = User.objects.get(username='test_instructor_alt') - - new_asset = Asset.objects.migrate_one(asset, new_course, new_user) - self.assertEquals(new_asset.author, new_user) - self.assertEquals(new_asset.course, new_course) - - duplicate_asset = Asset.objects.migrate_one(asset, - new_course, - new_user) + duplicate_asset = Asset.objects.migrate_one( + self.asset1, self.alt_course, self.alt_instructor) self.assertEquals(new_asset, duplicate_asset) - selected_annotation = SherdNote.objects.get(id=2) - new_note = SherdNote.objects.migrate_one(selected_annotation, - new_asset, - new_user, - True, - True) + new_note = SherdNote.objects.migrate_one( + self.instructor_note, new_asset, self.alt_instructor, True, True) self.assertFalse(new_note.is_global_annotation()) - self.assertEquals(new_note.author, new_user) - self.assertEquals(new_note.title, 'Manage Sources') + self.assertEquals(new_note.author, self.alt_instructor) + self.assertEquals(new_note.title, self.instructor_note.title) - duplicate_note = SherdNote.objects.migrate_one(selected_annotation, - new_asset, - new_user, - True, True) + duplicate_note = SherdNote.objects.migrate_one( + self.instructor_note, new_asset, self.alt_instructor, True, True) self.assertEquals(new_note, duplicate_note) def test_update_reference_in_string(self): - text = ('

    Nice Tie' - '

    Nice Tie' - '

    Nice Tie' - '

    Whole Item

    ' - '

    This should still be there

    ' - '

    This should still be there

    ' - ) + extra_asset = AssetFactory.create(course=self.sample_course, + primary_source='image') + extra_note = SherdNoteFactory( + asset=extra_asset, author=self.student_one) - old_asset = Asset.objects.get(id=2) - new_asset = Asset.objects.get(id=1) + new_asset = AssetFactory.create(course=self.sample_course, + primary_source='image') - new_text = new_asset.update_references_in_string(text, old_asset) + project = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='PrivateEditorsAreOwners') + self.add_citation(project, self.instructor_note) + self.add_citation(project, extra_note) + + # old-style whole-item annotation + project.body = '%s Whole Item

    ' % \ + (project.body, self.asset1.id) + + new_text = new_asset.update_references_in_string(project.body, + self.asset1) new_asset_href = "/asset/%s/" % (new_asset.id) self.assertTrue(new_text.find(new_asset_href) > 0) - old_asset_href = "/asset/24/" + old_asset_href = "/asset/%s/" % self.asset1.id self.assertTrue(new_text.find(old_asset_href) > 0) citations = SherdNote.objects.references_in_string(new_text, new_asset.author) - self.assertEquals(len(citations), 6) - self.assertEquals(citations[0].id, 10) - self.assertEquals(citations[0].asset.id, 2) - - self.assertEquals(citations[1].id, 10) - self.assertEquals(citations[1].asset.id, 2) + self.assertEquals(len(citations), 3) + self.assertEquals(citations[0].id, self.instructor_note.id) + self.assertEquals(citations[0].asset.id, self.asset1.id) - self.assertEquals(citations[2].id, 8) - self.assertEquals(citations[2].asset.id, 2) + self.assertEquals(citations[1].id, extra_note.id) + self.assertEquals(citations[1].asset.id, extra_asset.id) - self.assertEquals(citations[3].id, 1) - self.assertEquals(citations[3].asset.id, 1) - - self.assertEquals(citations[4].id, 0) - self.assertEquals(citations[5].id, 0) + gann = new_asset.global_annotation(new_asset.author, False) + self.assertEquals(citations[2].id, gann.id) + self.assertEquals(citations[2].asset.id, new_asset.id) def test_user_analysis_count(self): - asset1 = Asset.objects.get(id=1) - asset2 = Asset.objects.get(id=2) - asset3 = Asset.objects.get(id=3) - asset5 = Asset.objects.get(id=5) - - test_instructor = User.objects.get(username='test_instructor') - self.assertEquals(asset1.user_analysis_count(test_instructor), 6) - self.assertEquals(asset2.user_analysis_count(test_instructor), 4) - self.assertEquals(asset3.user_analysis_count(test_instructor), 3) - self.assertEquals(asset5.user_analysis_count(test_instructor), 0) - - test_instructor_two = User.objects.get(username='test_instructor_two') - self.assertEquals(asset1.user_analysis_count(test_instructor_two), 3) - self.assertEquals(asset2.user_analysis_count(test_instructor_two), 0) - self.assertEquals(asset5.user_analysis_count(test_instructor_two), 0) - - test_student_one = User.objects.get(username='test_student_one') - self.assertEquals(asset1.user_analysis_count(test_student_one), 0) - self.assertEquals(asset2.user_analysis_count(test_student_one), 3) - self.assertEquals(asset3.user_analysis_count(test_student_one), 0) + SherdNoteFactory( + asset=self.asset1, author=self.student_two, + tags=',student_two_item', + title=None, range1=None, range2=None) + + # global notes y/n + global tag count + annotation count + self.assertEquals(0, + self.asset1.user_analysis_count(self.instructor_two)) + self.assertEquals(1, + self.asset1.user_analysis_count(self.student_two)) + self.assertEquals(4, + self.asset1.user_analysis_count(self.instructor_one)) def test_assets_by_course(self): - course = Course.objects.get(title='Sample Course') - user = User.objects.get(username='test_instructor') - archive = Asset.objects.create(title="Sample Archive", - course=course, author=user) - primary = Source.objects.create(asset=archive, label='archive', - primary=True, - url="http://ccnmtl.columbia.edu") - archive.source_set.add(primary) + AssetFactory.create(course=self.sample_course, + primary_source='archive') - assets = Asset.objects.filter(course=course) - self.assertEquals(assets.count(), 5) + assets = Asset.objects.filter(course=self.sample_course) + self.assertEquals(assets.count(), 2) - assets = Asset.objects.by_course(course=course) - self.assertEquals(assets.count(), 4) + assets = Asset.objects.by_course(course=self.sample_course) + self.assertEquals(assets.count(), 1) # make sure the archive isn't in there - self.assertEquals(assets.filter(title="Sample Archive").count(), 0) + self.assertEquals(assets[0].title, self.asset1.title) def test_assets_by_course_and_user(self): - course = Course.objects.get(title='Sample Course') - user = User.objects.get(username='test_instructor_two') - archive = Asset.objects.create(title="Sample Archive", - course=course, author=user) - primary = Source.objects.create(asset=archive, label='archive', - primary=True, - url="http://ccnmtl.columbia.edu") - archive.source_set.add(primary) - - # tweak project portfolio to have a non-primary archive label - asset = Asset.objects.get(title='Project Portfolio') - metadata = Source.objects.create(asset=asset, label='archive', + AssetFactory.create(course=self.sample_course, + author=self.instructor_one, + primary_source='archive') + + # tweak an asset to have a non-primary archive label + asset2 = AssetFactory.create(course=self.sample_course, + author=self.instructor_one, + primary_source='image') + metadata = Source.objects.create(asset=asset2, label='archive', primary=False, url="http://ccnmtl.columbia.edu") - asset.source_set.add(metadata) + asset2.source_set.add(metadata) - assets = Asset.objects.by_course_and_user(course, user) - self.assertEquals(assets.count(), 2) - self.assertIsNotNone(assets.get(title='Project Portfolio')) - self.assertIsNotNone(assets.get(title='Mediathread: Introduction')) + assets = Asset.objects.by_course_and_user(self.sample_course, + self.instructor_one) + self.assertEquals(assets.count(), 1) + self.assertIsNotNone(assets[0], asset2) - # make sure the archive isn't in there - self.assertEquals(assets.filter(title="Sample Archive").count(), 0) - - user = User.objects.get(username='test_instructor') - assets = Asset.objects.by_course_and_user(course, user) - self.assertEquals(assets.count(), 3) - self.assertIsNotNone(assets.get(title='Mediathread: Introduction')) - self.assertIsNotNone(assets.get(title='MAAP Award Reception')) - self.assertIsNotNone(assets.get( - title="The Armory - Home to CCNMTL'S CUMC Office")) + assets = Asset.objects.by_course_and_user(self.sample_course, + self.student_one) + self.assertEquals(assets.count(), 1) + self.assertIsNotNone(assets[0], asset2) + + assets = Asset.objects.by_course_and_user(self.sample_course, + self.student_two) + self.assertEquals(assets.count(), 0) diff --git a/mediathread/assetmgr/tests/test_templatetags.py b/mediathread/assetmgr/tests/test_templatetags.py index 596214cd1..f33818172 100644 --- a/mediathread/assetmgr/tests/test_templatetags.py +++ b/mediathread/assetmgr/tests/test_templatetags.py @@ -1,8 +1,8 @@ -from courseaffils.models import Course -from django.contrib.auth.models import User from django.test.testcases import TestCase + from mediathread.assetmgr.models import Asset, Source from mediathread.assetmgr.templatetags.assetlinks import InCourseNode +from mediathread.factories import MediathreadTestMixin class MockNodeList(object): @@ -13,22 +13,21 @@ def render(self, c): self.rendered = True -class TestInCourse(TestCase): - fixtures = ['unittest_sample_course.json'] +class TestInCourse(MediathreadTestMixin, TestCase): def setUp(self): - self.user = User.objects.get(username='test_instructor') - self.course = Course.objects.get(title='Sample Course') + self.setup_sample_course() + self.setup_alternate_course() + self.archive = Asset.objects.create(title="Sample Archive", - course=self.course, - author=self.user) - primary = Source.objects.create(asset=self.archive, label='archive', + course=self.sample_course, + author=self.instructor_one) + primary = Source.objects.create(asset=self.archive, + label='archive', primary=True, url="http://ccnmtl.columbia.edu") self.archive.source_set.add(primary) - self.alt_course = Course.objects.get(title='Alternate Course') - def test_not_in_course(self): nlTrue = MockNodeList() nlFalse = MockNodeList() @@ -45,7 +44,7 @@ def test_is_in_course(self): nlFalse = MockNodeList() node = InCourseNode('archive', 'course', nlTrue, nlFalse) - context = dict(archive=self.archive, course=self.course) + context = dict(archive=self.archive, course=self.sample_course) out = node.render(context) self.assertEqual(out, None) self.assertTrue(nlTrue.rendered) diff --git a/mediathread/assetmgr/tests/test_views.py b/mediathread/assetmgr/tests/test_views.py index 0d826ae3c..7096f2554 100644 --- a/mediathread/assetmgr/tests/test_views.py +++ b/mediathread/assetmgr/tests/test_views.py @@ -1,22 +1,51 @@ -#pylint: disable-msg=R0904 -from courseaffils.models import Course -from django.conf import settings -from django.contrib.auth.models import User +# pylint: disable-msg=R0904 +import json + from django.test import TestCase from django.test.client import RequestFactory -from mediathread.assetmgr.models import Asset, Source + +from mediathread.assetmgr.models import Asset from mediathread.assetmgr.views import asset_workspace_courselookup, \ - asset_create + asset_create, sources_from_args from mediathread.djangosherd.models import SherdNote -import json +from mediathread.factories import MediathreadTestMixin, AssetFactory, \ + SherdNoteFactory, UserFactory + + +class AssetViewTest(MediathreadTestMixin, TestCase): + + def setUp(self): + self.setup_sample_course() + self.setup_alternate_course() + + # instructor that sees both Sample Course & Alternate Course + self.instructor_three = UserFactory(username='instructor_three') + self.add_as_faculty(self.sample_course, self.instructor_three) + self.add_as_faculty(self.alt_course, self.instructor_three) + + def test_sources_from_args(self): + data = {'title': 'Bad Asset', + 'asset-source': 'bookmarklet', + 'image': "x" * 5000, # too long + 'url': 'http://www.youtube.com/abcdefghi'} + request = RequestFactory().post('/save/', data) + sources = sources_from_args(request) + self.assertEquals(len(sources.keys()), 0) -class AssetViewTest(TestCase): - fixtures = ['unittest_sample_course.json'] + data = {'title': 'Good Asset', + 'asset-source': 'bookmarklet', + 'image': "http://www.flickr.com/"} + request = RequestFactory().post('/save/', data) + sources = sources_from_args(request) + self.assertEquals(len(sources.keys()), 2) + self.assertEquals(sources['image'].url, "http://www.flickr.com/") + self.assertTrue(sources['image'].primary) def test_archive_add_or_remove_get(self): self.assertTrue( - self.client.login(username='test_instructor', password='test')) + self.client.login(username=self.instructor_one.username, + password='test')) response = self.client.get('/asset/archive/') self.assertEquals(response.status_code, 405) @@ -26,22 +55,16 @@ def test_archive_add_or_remove_notloggedin(self): self.assertEquals(response.status_code, 302) def test_archive_remove(self): - user = User.objects.get(username='test_instructor') - course = Course.objects.get(title='Sample Course') - archive = Asset.objects.create(title="Sample Archive", - course=course, author=user) - primary = Source.objects.create(asset=archive, label='archive', - primary=True, - url="http://ccnmtl.columbia.edu") - archive.source_set.add(primary) - - self.assertIsNotNone(Asset.objects.get(title="Sample Archive")) + archive = AssetFactory.create(course=self.sample_course, + author=self.instructor_one, + primary_source='archive') self.assertTrue( - self.client.login(username='test_instructor', password='test')) + self.client.login(username=self.instructor_one.username, + password='test')) response = self.client.post('/asset/archive/', {'remove': True, - 'title': 'Sample Archive'}) + 'title': archive.title}) self.assertEquals(response.status_code, 302) try: @@ -58,10 +81,11 @@ def test_archive_add(self): 'archive': 'http://www.youtube.com/'} self.assertTrue( - self.client.login(username='test_instructor', password='test')) + self.client.login(username=self.instructor_one.username, + password='test')) self.client.post('/asset/archive/', data) - self.assertIsNotNone(Asset.objects.get(course__title='Sample Course', + self.assertIsNotNone(Asset.objects.get(course=self.sample_course, title='YouTube')) def test_asset_create_noasset(self): @@ -69,8 +93,8 @@ def test_asset_create_noasset(self): 'foobar': 'http://www.youtube.com/abcdefghi'} request = RequestFactory().post('/save/', data) - request.user = User.objects.get(username='test_instructor') - request.course = Course.objects.get(title='Sample Course') + request.user = self.instructor_one + request.course = self.sample_course try: asset_create(request) @@ -84,8 +108,8 @@ def test_asset_create_via_bookmarklet(self): 'asset-source': 'bookmarklet'} request = RequestFactory().post('/save/', data) - request.user = User.objects.get(username='test_instructor') - request.course = Course.objects.get(title='Sample Course') + request.user = self.instructor_one + request.course = self.sample_course response = asset_create(request) self.assertEquals(response.status_code, 200) @@ -95,42 +119,59 @@ def test_asset_create_via_bookmarklet(self): def test_asset_workspace_course_lookup(self): self.assertIsNone(asset_workspace_courselookup()) - asset = Asset.objects.get(title='MAAP Award Reception') - self.assertEquals(asset_workspace_courselookup(asset_id=asset.id), - asset.course) + asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image') + + self.assertEquals(asset_workspace_courselookup(asset_id=asset1.id), + asset1.course) def test_most_recent(self): self.assertTrue( - self.client.login(username='test_instructor', password='test')) + self.client.login(username=self.instructor_one.username, + password='test')) + + asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image', + author=self.instructor_one) response = self.client.get('/asset/most_recent/', {}, follow=True) self.assertEquals(response.status_code, 200) - self.assertEquals(response.redirect_chain, - [('http://testserver/asset/3/', 302)]) + + url = 'http://testserver/asset/%s/' % asset1.id + self.assertEquals(response.redirect_chain, [(url, 302)]) def test_asset_delete(self): self.assertTrue( - self.client.login(username='test_instructor', password='test')) - - asset = Asset.objects.get(title='MAAP Award Reception') - user = asset.author - self.assertEquals(asset.sherdnote_set.filter(author=user).count(), 2) - response = self.client.get('/asset/delete/%s/' % asset.id, - {}, + self.client.login(username=self.instructor_one.username, + password='test')) + + asset1 = AssetFactory.create(course=self.sample_course, + author=self.instructor_one, + primary_source='image') + self.student_note = SherdNoteFactory( + asset=asset1, author=self.student_one) + self.instructor_note = SherdNoteFactory( + asset=asset1, author=self.instructor_one) + + response = self.client.get('/asset/delete/%s/' % asset1.id, {}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 200) - self.assertEquals(asset.sherdnote_set.filter(author=user).count(), 0) + notes = asset1.sherdnote_set.filter(author=self.instructor_one) + self.assertEquals(notes.count(), 0) + notes = asset1.sherdnote_set.filter(author=self.student_one) + self.assertEquals(notes.count(), 1) def test_asset_detail(self): self.assertTrue( - self.client.login(username='test_instructor_two', password='test')) + self.client.login(username=self.instructor_one.username, + password='test')) - response = self.client.get('/?set_course=Sample_Course_Students') - self.assertEquals(response.status_code, 200) + asset1 = AssetFactory.create(course=self.sample_course, + author=self.instructor_one, + primary_source='image') - response = self.client.get('/asset/1/', - {}, + response = self.client.get('/asset/%s/' % asset1.id, {}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') the_json = json.loads(response.content) @@ -139,7 +180,7 @@ def test_asset_detail(self): panel = the_json["panels"][0] self.assertIsNone(panel["current_annotation"]) - self.assertEquals(panel["current_asset"], "1") + self.assertEquals(panel["current_asset"], str(asset1.id)) self.assertEquals(panel["panel_state"], "open") self.assertEquals(panel["panel_state_label"], "Annotate Media") self.assertTrue(panel["show_collection"]) @@ -152,13 +193,18 @@ def test_asset_detail(self): def test_asset_detail_alternate(self): self.assertTrue( - self.client.login(username='test_instructor_two', password='test')) + self.client.login(username=self.instructor_three.username, + password='test')) - response = self.client.get('/?set_course=Sample_Course_Students') + response = self.switch_course(self.client, self.sample_course) self.assertEquals(response.status_code, 200) + asset1 = AssetFactory.create(course=self.alt_course, + author=self.alt_instructor, + primary_source='image') + # Alternate Course Asset - response = self.client.get('/asset/4/') + response = self.client.get('/asset/%s/' % asset1.id) self.assertEquals(response.status_code, 200) self.assertTemplateUsed(response, "assetmgr/asset_not_found.html") @@ -168,108 +214,149 @@ def test_asset_detail_alternate(self): def test_asset_detail_does_not_exist(self): self.assertTrue( - self.client.login(username='test_instructor_two', password='test')) - - response = self.client.get('/?set_course=Sample_Course_Students') - self.assertEquals(response.status_code, 200) + self.client.login(username=self.instructor_one.username, + password='test')) # Item Does Not Exist - response = self.client.get('/asset/56/') + response = self.client.get('/asset/5616/') self.assertEquals(response.status_code, 404) - def test_annotation_save(self): - asset = Asset.objects.get(id=2) - - # Update as the asset's non-original author. This should fail - username = "test_student_one" - self.assert_(self.client.login(username=username, password="test")) - post_data = {'asset-title': "My MAAP Award Reception"} - gann = SherdNote.objects.get(id=9) # student one global ann - url = "/asset/save/%s/annotations/%s/" % (asset.id, gann.id) - response = self.client.post(url, - post_data, + def test_asset_title_save_as_non_author(self): + asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image', + title="Item Title") + + student_ga = SherdNoteFactory( + asset=asset1, author=self.student_one, + title=None, range1=None, range2=None) + + # Update as the asset's non-original author with ga. This should fail + self.assert_(self.client.login(username=self.student_one.username, + password="test")) + post_data = {'asset-title': "Student Item"} + url = "/asset/save/%s/annotations/%s/" % (asset1.id, student_ga.id) + response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 200) - asset = Asset.objects.get(id=2) - self.assertEquals(asset.title, "MAAP Award Reception") - - # Switch to the asset's original author - username = "test_instructor" - self.client.logout() - self.assert_(self.client.login(username=username, password="test")) - - # Update as the asset's original author, this should succeed - gann = SherdNote.objects.get(id=4) - url = "/asset/save/%s/annotations/%s/" % (asset.id, gann.id) - post_data = {'asset-title': "Updated MAAP Award Reception"} - response = self.client.post(url, - post_data, + updated_asset = Asset.objects.get(id=asset1.id) + self.assertEquals(updated_asset.title, "Item Title") + + def test_asset_title_save_as_author_not_global_annotation(self): + asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image', + title="Item Title") + + note = SherdNoteFactory( + asset=asset1, author=self.instructor_one) + + self.assert_(self.client.login(username=self.instructor_one.username, + password="test")) + + # Update passing in a non-global annotation. This should fail + post_data = {'asset-title': 'Updated Title', + 'annotation-range1': -4.5, 'annotation-range2': 23.0} + url = "/asset/save/%s/annotations/%s/" % (asset1.id, note.id) + response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 200) - asset = Asset.objects.get(id=2) - self.assertEquals(asset.title, "Updated MAAP Award Reception") + asset = Asset.objects.get(id=asset1.id) + self.assertEquals(asset.title, "Item Title") + + def test_asset_title_save_as_author_global_annotation(self): + asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image', + title="Item Title", + author=self.instructor_one) + + gann = SherdNoteFactory( + asset=asset1, author=self.instructor_one, + title=None, range1=None, range2=None) + + self.assert_(self.client.login(username=self.instructor_one.username, + password="test")) # Update passing in a non-global annotation. This should fail - ann = SherdNote.objects.get(id=5) - url = "/asset/save/%s/annotations/%s/" % (asset.id, ann.id) - post_data = {'asset-title': "The New and Improved MAAP", - 'annotation-range1': -4.5, - 'annotation-range2': 23.0} - response = self.client.post(url, - post_data, + post_data = {'asset-title': "Updated Item Title"} + url = "/asset/save/%s/annotations/%s/" % (asset1.id, gann.id) + response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 200) - asset = Asset.objects.get(id=2) - self.assertEquals(asset.title, "Updated MAAP Award Reception") - - # Test "no annotation" exception path - url = "/asset/save/%s/annotations/%s/" % (asset.id, 42) - post_data = {'asset-title': "The New and Improved Armory"} - response = self.client.post(url, - post_data, + asset = Asset.objects.get(id=asset1.id) + self.assertEquals(asset.title, "Updated Item Title") + + def test_annotation_save(self): + asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image', + title="Item Title") + + note = SherdNoteFactory( + asset=asset1, author=self.instructor_one) + + self.assert_(self.client.login(username=self.instructor_one.username, + password="test")) + + # Update passing in a non-global annotation. This should fail + url = "/asset/save/%s/annotations/%s/" % (asset1.id, note.id) + post_data = {'annotation-range1': -4.5, 'annotation-range2': 23.0} + response = self.client.post(url, post_data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertEquals(response.status_code, 403) + self.assertEquals(response.status_code, 200) - # The remainder of the annotation update is tested in djangosherd + updated_note = SherdNote.objects.get(id=note.id) + self.assertEquals(updated_note.range1, -4.5) + self.assertEquals(updated_note.range2, 23.0) + + def test_annotation_save_no_annotation_exists(self): + asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image', + title="Item Title") + + self.assert_(self.client.login(username=self.instructor_one.username, + password="test")) + + url = "/asset/save/%s/annotations/%s/" % (asset1.id, 42) + post_data = {'annotation-range1': -4.5} + response = self.client.post(url, post_data, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 403) def test_save_server2server(self): - setattr(settings, - 'SERVER_ADMIN_SECRETKEYS', {'http://localhost': 'testing'}) - - post_data = {'set_course': "Sample_Course_Students", - 'secret': 'testing', - 'as': 'test_student_one', - 'title': "Test Video", - "metadata-creator": "test_student_one", - "metadata-description": "a description", - "metadata-subject": "a subject", - "metadata-license": "video.license", - "metadata-language": "english", - "metadata-uuid": "26d62ca0-844f-11e3-a7ed-0002a5d5c51b", - "metadata-wardenclyffe-id": str(1234), - "metadata-tag": "upload", - "mp4-metadata": "w%dh%d" % (256, 256), - "mp4_pseudo": "http://stream.ccnmtl.columbia.edu/" - "secvideos/SECURE/d75ebcfa-8444-11e3-a075-00163e3b1544-" - "No_training_wheels-mp4-aac-480w-850kbps-ffmpeg.mp4"} - - response = self.client.post("/save/", post_data, follow=True) - asset = Asset.objects.get(title="Test Video") - self.assertRedirects(response, - "http://testserver/accounts/login/?next=/asset/" + - str(asset.id) + "/", - status_code=302, - target_status_code=200) - self.assertEquals(asset.author.username, "test_student_one") - self.assertEquals(asset.course.title, "Sample Course") - user = User.objects.get(username='test_student_one') - self.assertIsNotNone(asset.global_annotation(user, auto_create=False)) - - # Repeat the post with a different user, verify there's no duplication - post_data['as'] = 'test_instructor' - response = self.client.post("/save/", post_data, follow=True) - - # There should only be one asset. This will raise if there are multiple - asset = Asset.objects.get(title="Test Video") - user = User.objects.get(username="test_instructor") - self.assertIsNotNone(asset.global_annotation(user, auto_create=False)) + secrets = {'http://testserver/': 'testing'} + with self.settings(SERVER_ADMIN_SECRETKEYS=secrets): + post_data = {'set_course': self.sample_course.group.name, + 'secret': 'testing', + 'as': self.student_one.username, + 'title': "Test Video", + "metadata-creator": self.student_one.username, + "metadata-description": "a description", + "metadata-subject": "a subject", + "metadata-license": "video.license", + "metadata-language": "english", + "metadata-uuid": "26d62ca0-844f", + "metadata-wardenclyffe-id": str(1234), + "metadata-tag": "upload", + "mp4-metadata": "w%dh%d" % (256, 256), + "mp4_pseudo": "http://stream.ccnmtl.columbia.edu/" + "secvideos/SECURE/d75ebcfa-8444" + "No_training_wheels-mp4-aac-480w-850kbps-ffmpeg.mp4"} + + response = self.client.post("/save/", post_data, follow=True) + asset = Asset.objects.get(title="Test Video") + self.assertRedirects( + response, + "http://testserver/accounts/login/?next=/asset/%s/" % asset.id, + status_code=302, + target_status_code=200) + self.assertEquals(asset.author.username, self.student_one.username) + self.assertEquals(asset.course.title, "Sample Course") + self.assertIsNotNone(asset.global_annotation(self.student_one, + auto_create=False)) + + # Repeat the post with a different user, no duplication expected + post_data['as'] = self.instructor_one + response = self.client.post("/save/", post_data, follow=True) + + # There should only be one asset. + asset = Asset.objects.get(title="Test Video") + self.assertIsNotNone(asset.global_annotation(self.instructor_one, + auto_create=False)) diff --git a/mediathread/discussions/models.py b/mediathread/discussions/models.py index 691dbbc9d..a889ff05a 100644 --- a/mediathread/discussions/models.py +++ b/mediathread/discussions/models.py @@ -1,92 +1 @@ -from structuredcollaboration.models import Collaboration -from django.contrib.contenttypes.models import ContentType -from threadedcomments import ThreadedComment - - -class CollaborationThreadedComment_Migration1: - """ - The first implementation connected things like this: - threadedcomment -> collaboration -> _parent -> object - The new way will be: - threadedcomment -> collaboration -> _parent -> object - -> threadedcomment - """ - harmless = True - - def run(self): - collab_type = ContentType.objects.get_for_model(Collaboration) - root_comments = \ - ThreadedComment.objects.filter(parent=None, - content_type=collab_type) - for root in root_comments: - disc_sc = root.content_object - disc_sc.content_object = root - disc_sc.save() - - def reverse(self): - collab_type = ContentType.objects.get_for_model(Collaboration) - root_comments = \ - ThreadedComment.objects.filter(parent=None, - content_type=collab_type) - for root in root_comments: - disc_sc = root.content_object - disc_sc.content_object = None - disc_sc.save() - - -class CollaborationThreadedComment_Migration2: - """NEVER USED YET: CONSIDERING.... - First implementation connected things like this: - threadedcomment -> collaboration -> _parent -> object - The new way will be: - collaboration -> threadedcomment -> object - -> _parent -> object - The advantage is mostly that the collaboration - knows what it's protecting - - This script moves the pointers around - """ - harmless = False - - def run(self): - collab_type = ContentType.objects.get_for_model(Collaboration) - root_comments = \ - ThreadedComment.objects.filter(parent=None, - content_type=collab_type) - for root in root_comments: - disc_sc = root.content_object - target_object = disc_sc._parent.content_object - child_comments = \ - ThreadedComment.objects.filter(content_type=collab_type, - object_pk=disc_sc.pk) - for child in child_comments: - child.content_object = target_object - child.save() - - disc_sc.content_object = root - disc_sc.save() - root.content_object = target_object - root.save() - - def reverse(self): - comm_type = ContentType.objects.get_for_model(ThreadedComment) - comment_collabs = Collaboration.objects.filter(content_type=comm_type) - for disc_sc in comment_collabs: - target_object = disc_sc._parent.content_object - root = disc_sc.content_object - target_type = \ - ContentType.objects.get_for_model(target_object.__class__) - child_comments = \ - ThreadedComment.objects.filter(content_type=target_type, - object_pk=target_object.pk) - for child in child_comments: - child.content_object = disc_sc - child.save() - disc_sc.content_object = None - disc_sc.save() - root.content_object = disc_sc - root.save() - - -class Discussion: - migrations = (CollaborationThreadedComment_Migration1(),) +# models for discussions diff --git a/mediathread/discussions/urls.py b/mediathread/discussions/urls.py index f929d1b0c..985100833 100644 --- a/mediathread/discussions/urls.py +++ b/mediathread/discussions/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import patterns, url +from django.conf.urls import patterns, url urlpatterns = patterns( 'mediathread.discussions.views', diff --git a/mediathread/discussions/utils.py b/mediathread/discussions/utils.py index 152426446..d7c03bc0a 100644 --- a/mediathread/discussions/utils.py +++ b/mediathread/discussions/utils.py @@ -4,20 +4,6 @@ from threadedcomments.models import ThreadedComment -def get_discussions(arbitrary_object): - coll = ContentType.objects.get_for_model(Collaboration) - discussions = [] - comments = ThreadedComment.objects.filter(parent=None, content_type=coll) - - for comment in comments: - parent_content_object = comment.content_object._parent.content_object - if (comment.content_object and - comment.content_object._parent_id and - arbitrary_object == parent_content_object): - discussions.append(comment) - return discussions - - def get_course_discussions(course): content_type = ContentType.objects.get_for_model(Course) diff --git a/mediathread/djangosherd/models.py b/mediathread/djangosherd/models.py index 8e5557aeb..6371fa92a 100755 --- a/mediathread/djangosherd/models.py +++ b/mediathread/djangosherd/models.py @@ -117,6 +117,7 @@ def filter_by_date(self, date_range): Q(modified__range=[startdate, enddate])) def get_related_notes(self, assets, record_owner, visible_authors, + all_items_are_visible, tag_string=None, modified=None, vocabulary=None): # For efficiency purposes, retrieve all related notes @@ -128,10 +129,21 @@ def get_related_notes(self, assets, record_owner, visible_authors, self = self.exclude(~Q(author=record_owner)) if len(visible_authors) > 0: +<<<<<<< HEAD # return global annotations & # regular selections authored by certain people self = self.filter(Q(author__id__in=visible_authors) | Q(range1__isnull=True)) +======= + if not all_items_are_visible: + # only return notes that are authored by certain people + self = self.filter(author__id__in=visible_authors) + else: + # return everyone's global annotations (item-level) & + # regular selections authored by certain people + self = self.filter(Q(author__id__in=visible_authors) | + Q(range1__isnull=True)) +>>>>>>> added spring 2015 files # filter by tag string, date, vocabulary self = self.filter_by_tags(tag_string) @@ -163,11 +175,11 @@ def filter_by_vocabulary(self, vocabulary): return self.get_query_set().filter_by_vocabulary(vocabulary) def get_related_notes(self, assets, record_owner, visible_authors, + all_items_are_visible, tag_string=None, modified=None, vocabulary=None): - return self.get_query_set().get_related_notes(assets, record_owner, - visible_authors, - tag_string, modified, - vocabulary) + return self.get_query_set().get_related_notes( + assets, record_owner, visible_authors, + all_items_are_visible, tag_string, modified, vocabulary) def global_annotation(self, asset, author, auto_create=True): """ diff --git a/mediathread/djangosherd/tests/__init__.py b/mediathread/djangosherd/tests/__init__.py index 4090c729b..e94598e5c 100755 --- a/mediathread/djangosherd/tests/__init__.py +++ b/mediathread/djangosherd/tests/__init__.py @@ -1,3 +1,3 @@ -#pylint: disable-msg=W0401 +# pylint: disable-msg=W0401 # flake8: noqa -from .test_model import * +from mediathread.djangosherd.tests.test_model import * diff --git a/mediathread/djangosherd/tests/test_model.py b/mediathread/djangosherd/tests/test_model.py index 3cafbbb31..353ff619a 100644 --- a/mediathread/djangosherd/tests/test_model.py +++ b/mediathread/djangosherd/tests/test_model.py @@ -1,14 +1,18 @@ # pylint: disable-msg=R0904 from django.contrib.auth.models import User +from django.test import TestCase from mediathread.assetmgr.models import Asset from mediathread.djangosherd.models import SherdNote -from mediathread.factories import MediathreadTestCase, AssetFactory, \ - SherdNoteFactory +from mediathread.factories import AssetFactory, \ + SherdNoteFactory, MediathreadTestMixin from mediathread.taxonomy.models import Vocabulary, Term -class SherdNoteTest(MediathreadTestCase): +class SherdNoteTest(MediathreadTestMixin, TestCase): + + def setUp(self): + self.setup_sample_course() def test_is_global_annotation(self): asset = AssetFactory(course=self.sample_course) @@ -23,7 +27,7 @@ def test_is_global_annotation(self): real_annotation = SherdNoteFactory( asset=asset, author=self.student_three, - title="Whole Item Selection", range1=116.25, range2=6.75) + title="Selection", range1=116.25, range2=6.75) self.assertFalse(real_annotation.is_global_annotation()) def test_seconds_to_code(self): @@ -264,33 +268,33 @@ def test_filter_by_today(self): self.assertEquals(notes[0].title, note.title) -class SherdNoteFilterTest(MediathreadTestCase): +class SherdNoteFilterTest(MediathreadTestMixin, TestCase): def setUp(self): - super(self.__class__, self).setUp() + self.setup_sample_course() self.asset = AssetFactory(course=self.sample_course, author=self.student_one) - self.ga1 = SherdNoteFactory( + self.student_one_ga = SherdNoteFactory( asset=self.asset, author=self.student_one, tags=',student_one_item', title=None, range1=None, range2=None) - self.assertTrue(self.ga1.is_global_annotation()) - self.note1 = SherdNoteFactory( + self.assertTrue(self.student_one_ga.is_global_annotation()) + self.student_one_note = SherdNoteFactory( asset=self.asset, author=self.student_one, tags=',student_one_selection', range1=0, range2=1) - self.ga2 = SherdNoteFactory( + self.student_two_ga = SherdNoteFactory( asset=self.asset, author=self.student_two, tags=',student_two_item', title=None, range1=None, range2=None) - self.note2 = SherdNoteFactory( + self.student_two_note = SherdNoteFactory( asset=self.asset, author=self.student_two, tags=',student_two_selection', range1=0, range2=1) - self.ga3 = SherdNoteFactory( + self.instructor_one_ga = SherdNoteFactory( asset=self.asset, author=self.instructor_one, tags=',instructor_one_item', title=None, range1=None, range2=None) - self.note3 = SherdNoteFactory( + self.instructor_one_note = SherdNoteFactory( asset=self.asset, author=self.instructor_one, tags=',image,instructor_one_selection,', range1=0, range2=1) @@ -304,24 +308,25 @@ def test_filter_by_record_owner(self): notes = SherdNote.objects.get_related_notes(qs, author, - visible_authors) + visible_authors, + True) self.assertEquals(notes.count(), 2) - self.assertEquals(notes[0], self.ga1) - self.assertEquals(notes[1], self.note1) + self.assertEquals(notes[0], self.student_one_ga) + self.assertEquals(notes[1], self.student_one_note) def test_filter_no_authors(self): qs = Asset.objects.filter(id=self.asset.id) - notes = SherdNote.objects.get_related_notes(qs, None, []) + notes = SherdNote.objects.get_related_notes(qs, None, [], True) self.assertEquals(notes.count(), 6) - self.assertEquals(notes[0], self.ga1) - self.assertEquals(notes[1], self.note1) - self.assertEquals(notes[2], self.ga2) - self.assertEquals(notes[3], self.note2) - self.assertEquals(notes[4], self.ga3) - self.assertEquals(notes[5], self.note3) + self.assertEquals(notes[0], self.student_one_ga) + self.assertEquals(notes[1], self.student_one_note) + self.assertEquals(notes[2], self.student_two_ga) + self.assertEquals(notes[3], self.student_two_note) + self.assertEquals(notes[4], self.instructor_one_ga) + self.assertEquals(notes[5], self.instructor_one_note) def test_filter_by_visible_authors(self): qs = Asset.objects.filter(id=self.asset.id) @@ -332,6 +337,7 @@ def test_filter_by_visible_authors(self): notes = SherdNote.objects.get_related_notes(qs, None, +<<<<<<< HEAD visible_authors) self.assertEquals(notes.count(), 5) @@ -340,11 +346,40 @@ def test_filter_by_visible_authors(self): self.assertEquals(notes[2], self.ga2) self.assertEquals(notes[3], self.ga3) self.assertEquals(notes[4], self.note3) +======= + visible_authors, + True) + self.assertEquals(notes.count(), 5) + + self.assertEquals(notes[0], self.student_one_ga) + self.assertEquals(notes[1], self.student_one_note) + self.assertEquals(notes[2], self.student_two_ga) + self.assertEquals(notes[3], self.instructor_one_ga) + self.assertEquals(notes[4], self.instructor_one_note) + + def test_filter_by_all_items_are_visible(self): + qs = Asset.objects.filter(id=self.asset.id) + + names = ['instructor_one', 'instructor_two', 'student_one'] + users = User.objects.filter(username__in=names).values_list('id') + visible_authors = users.values_list('id', flat=True) + + notes = SherdNote.objects.get_related_notes(qs, + None, + visible_authors, + False) + self.assertEquals(notes.count(), 4) + + self.assertEquals(notes[0], self.student_one_ga) + self.assertEquals(notes[1], self.student_one_note) + self.assertEquals(notes[2], self.instructor_one_ga) + self.assertEquals(notes[3], self.instructor_one_note) +>>>>>>> added spring 2015 files def test_filter_by_tags(self): notes = SherdNote.objects.filter_by_tags('student_one_selection') self.assertEquals(notes.count(), 1) - self.assertEquals(notes[0], self.note1) + self.assertEquals(notes[0], self.student_one_note) notes = SherdNote.objects.filter(asset=self.asset) self.assertEquals(notes.count(), 6) @@ -352,5 +387,5 @@ def test_filter_by_tags(self): notes = notes.filter_by_tags( 'student_two_selection,image').order_by('id') self.assertEquals(notes.count(), 2) - self.assertEquals(notes[0], self.note2) - self.assertEquals(notes[1], self.note3) + self.assertEquals(notes[0], self.student_two_note) + self.assertEquals(notes[1], self.instructor_one_note) diff --git a/mediathread/djangosherd/views.py b/mediathread/djangosherd/views.py index 6c52e378d..9a760f9b9 100755 --- a/mediathread/djangosherd/views.py +++ b/mediathread/djangosherd/views.py @@ -43,8 +43,8 @@ def create_annotation(request): update_vocabulary_terms(request, annotation) - #need to create global annotation if it doesn't exist already - #so it appears in the user's list + # need to create global annotation if it doesn't exist already + # so it appears in the user's list asset.global_annotation(annotation.author, auto_create=True) if request.is_ajax(): @@ -53,7 +53,7 @@ def create_annotation(request): return HttpResponse(json.dumps(response), mimetype="application/json") else: - #new annotations should redirect 'back' to the asset + # new annotations should redirect 'back' to the asset # at the endpoint of the last annotation # so someone can create a new annotation ~lizday url_fragment = '' @@ -112,7 +112,7 @@ def update_annotation(request, annotation): for key, val in request.POST.items() if key.startswith('annotation-')) - ## @todo -- figure out how the clipform gets into the + # @todo -- figure out how the clipform gets into the # annotations.mustache form # don't let a global annotation turn into a clip, or v.v. if form.get('range1') or form.get('range2'): diff --git a/mediathread/factories.py b/mediathread/factories.py index 947d62b7f..544c5ef51 100644 --- a/mediathread/factories.py +++ b/mediathread/factories.py @@ -1,23 +1,26 @@ from courseaffils.models import Course from django.contrib.auth.models import User, Group from django.contrib.contenttypes.models import ContentType -from django.test.testcases import TestCase +from django.test.client import RequestFactory import factory -from mediathread.assetmgr.models import Asset +from mediathread.assetmgr.models import Asset, Source +from mediathread.discussions.views import discussion_create from mediathread.djangosherd.models import SherdNote +from mediathread.projects.models import Project from mediathread.taxonomy.models import Vocabulary, Term, TermRelationship +from structuredcollaboration.models import Collaboration class UserFactory(factory.DjangoModelFactory): FACTORY_FOR = User username = factory.Sequence(lambda n: 'user%d' % n) - password = 'test' + password = factory.PostGenerationMethodCall('set_password', 'test') class GroupFactory(factory.DjangoModelFactory): FACTORY_FOR = Group - name = factory.Sequence(lambda n: 'group %d' % n) + name = factory.Sequence(lambda n: 'group%d' % n) class CourseFactory(factory.DjangoModelFactory): @@ -26,6 +29,19 @@ class CourseFactory(factory.DjangoModelFactory): faculty_group = factory.SubFactory(GroupFactory) group = factory.SubFactory(GroupFactory) + @factory.post_generation + def create_collaboration(self, create, extracted, **kwargs): + if create: + Collaboration.objects.get_or_create( + content_type=ContentType.objects.get_for_model(Course), + object_pk=str(self.pk)) + + +class SourceFactory(factory.DjangoModelFactory): + FACTORY_FOR = Source + url = 'sample url' + media_type = 'ext' + class AssetFactory(factory.DjangoModelFactory): FACTORY_FOR = Asset @@ -33,6 +49,15 @@ class AssetFactory(factory.DjangoModelFactory): author = factory.SubFactory(UserFactory) course = factory.SubFactory(CourseFactory) + @factory.post_generation + def primary_source(self, create, extracted, **kwargs): + if create and extracted: + # A list of groups were passed in, use them + source = SourceFactory(primary=True, + label=extracted, + asset=self) + self.source_set.add(source) + class SherdNoteFactory(factory.DjangoModelFactory): FACTORY_FOR = SherdNote @@ -43,7 +68,49 @@ class SherdNoteFactory(factory.DjangoModelFactory): author = factory.SubFactory(UserFactory) -class MediathreadTestCase(TestCase): +class ProjectFactory(factory.DjangoModelFactory): + FACTORY_FOR = Project + course = factory.SubFactory(CourseFactory) + title = factory.Sequence(lambda n: 'Project %d' % n) + author = factory.SubFactory(UserFactory) + + @factory.post_generation + def policy(self, create, extracted, **kwargs): + if create: + data = {'publish': 'PrivateEditorsAreOwners'} + if extracted: + data = {'publish': extracted} + + request = RequestFactory().post('/', data) + request.collaboration_context = \ + Collaboration.objects.get( + content_type=ContentType.objects.get_for_model(Course), + object_pk=str(self.course.pk)) + + self.collaboration(request, sync_group=True) + + @factory.post_generation + def parent(self, create, extracted, **kwargs): + if create and extracted: + parent_collab = extracted.collaboration() + if parent_collab._policy.policy_name == 'Assignment': + parent_collab.append_child(self) + + +class MediathreadTestMixin(object): + + def create_discussion(self, course, instructor): + data = {'comment_html': '%s Discussion' % course.title, + 'obj_pk': course.id, + 'model': 'course', 'app_label': 'courseaffils'} + request = RequestFactory().post('/discussion/create/', data) + request.user = instructor + request.course = course + request.collaboration_context, created = \ + Collaboration.objects.get_or_create( + content_type=ContentType.objects.get_for_model(Course), + object_pk=str(course.pk)) + discussion_create(request) def create_vocabularies(self, course, taxonomy): course_type = ContentType.objects.get_for_model(course) @@ -66,29 +133,67 @@ def create_term_relationship(self, content_object, term): content_type=content_type, object_id=content_object.id) - def setUp(self): + def add_citation(self, project, note): + # add this note into the project's body + fmt = '%s %s' + project.body = fmt % (project.body, + 'asset', note.asset.id, + 'annotations', note.id, note.title) + project.save() + + def add_as_student(self, course, user): + user.groups.add(course.group) + + def add_as_faculty(self, course, user): + user.groups.add(course.group) + user.groups.add(course.faculty_group) + + def setup_sample_course(self): self.instructor_one = UserFactory(username='instructor_one', - first_name='Instructor', - last_name='One') - self.instructor_two = UserFactory(username='test_instructor_two') + first_name="Instructor", + last_name="One") + self.instructor_two = UserFactory(username='instructor_two', + first_name="Instructor", + last_name="Two") self.student_one = UserFactory(username='student_one', - first_name='Student', - last_name='One') + first_name="Student", + last_name="One") self.student_two = UserFactory(username='student_two', - first_name='Student', - last_name='Two') - self.student_three = UserFactory(username='test_student_three') - self.ta = UserFactory(username='teachers_assistant', - first_name="Teacher's", - last_name=" Assistant") + first_name="Student", + last_name="Two") + self.student_three = UserFactory(username='student_three', + first_name="Student", + last_name="Three") self.sample_course = CourseFactory(title="Sample Course") - self.instructor_one.groups.add(self.sample_course.group) - self.instructor_two.groups.add(self.sample_course.group) - self.student_one.groups.add(self.sample_course.group) - self.student_two.groups.add(self.sample_course.group) - self.student_three.groups.add(self.sample_course.group) - - self.instructor_one.groups.add(self.sample_course.faculty_group) - self.instructor_two.groups.add(self.sample_course.faculty_group) + + self.add_as_student(self.sample_course, self.student_one) + self.add_as_student(self.sample_course, self.student_two) + self.add_as_student(self.sample_course, self.student_three) + + self.add_as_faculty(self.sample_course, self.instructor_one) + self.add_as_faculty(self.sample_course, self.instructor_two) + + def setup_alternate_course(self): + self.alt_instructor = UserFactory(username='alt_instructor', + first_name='Instructor', + last_name='Alternate') + self.alt_student = UserFactory(username='alt_student', + first_name='Student', + last_name='Alternate') + + self.alt_course = CourseFactory(title="Alternate Course") + + self.add_as_student(self.alt_course, self.alt_student) + self.add_as_faculty(self.alt_course, self.alt_instructor) + + def switch_course(self, client, course): + # assumes there is a logged in user + set_course_url = '/?set_course=%s' % course.group.name + return client.get(set_course_url) + + def enable_upload(self, course): + AssetFactory.create(course=course, + primary_source='archive', + metadata_blob='{"upload": ["1"]}') diff --git a/mediathread/main/admin.py b/mediathread/main/admin.py index 72d19399d..d944f8904 100644 --- a/mediathread/main/admin.py +++ b/mediathread/main/admin.py @@ -1,5 +1,8 @@ from django.contrib import admin -from mediathread.main.models import UserSetting +from django.contrib.auth.admin import UserAdmin +from django.contrib.auth.models import User + +from mediathread.main.models import UserSetting, UserProfile class UserSettingAdmin(admin.ModelAdmin): @@ -11,3 +14,15 @@ class Meta: admin.site.register(UserSetting, UserSettingAdmin) + +admin.site.unregister(User) + + +class UserProfileInline(admin.StackedInline): + model = UserProfile + + +class UserProfileAdmin(UserAdmin): + inlines = [UserProfileInline, ] + +admin.site.register(User, UserProfileAdmin) diff --git a/mediathread/main/clumper.py b/mediathread/main/clumper.py index 0fafefbee..58026fca1 100644 --- a/mediathread/main/clumper.py +++ b/mediathread/main/clumper.py @@ -10,7 +10,7 @@ def adapt_date(obj): return [getattr(obj, d) for d in date_fields if hasattr(obj, d)][0] -class Clumper(): +class Clumper(object): """Clumps stuff by thing.content_object""" stuff = None # don't make array here or it persists to other requests. items = None @@ -35,7 +35,7 @@ def __iter__(self): # will use ClumpItem.__cmp__ return iter(sorted(self.items.values())) - class ClumpItem(): + class ClumpItem(object): things = None primary_thing = None diff --git a/mediathread/main/course_details.py b/mediathread/main/course_details.py index 42a7d25da..7a1a8da71 100644 --- a/mediathread/main/course_details.py +++ b/mediathread/main/course_details.py @@ -1,3 +1,4 @@ +from courseaffils.models import Course from django.core.cache import cache @@ -22,6 +23,19 @@ def can_upload(user, course): return True elif value == UPLOAD_PERMISSION_STUDENT: return True + else: + return False + + +def is_upload_enabled(course): + upload_enabled = False + for item in course.asset_set.archives().order_by('title'): + attribute = item.metadata().get('upload', 0) + value = attribute[0] if hasattr(attribute, 'append') else attribute + if value and int(value) == 1: + upload_enabled = True + break + return upload_enabled ALLOW_PUBLIC_COMPOSITIONS_KEY = "allow_public_compositions" ALLOW_PUBLIC_COMPOSITIONS_DEFAULT = 0 @@ -42,15 +56,38 @@ def all_selections_are_visible(course): return bool(value) +ITEM_VISIBILITY_KEY = "item_visibility" +ITEM_VISIBILITY_DEFAULT = 1 + + +def all_items_are_visible(course): + value = int(course.get_detail(ITEM_VISIBILITY_KEY, + ITEM_VISIBILITY_DEFAULT)) + return bool(value) + + +COURSE_INFORMATION_TITLE_KEY = "course_information_title" +COURSE_INFORMATION_TITLE_DEFAULT = "From Your Instructor" + + +def course_information_title(course): + return course.get_detail(COURSE_INFORMATION_TITLE_KEY, + COURSE_INFORMATION_TITLE_DEFAULT) + + def cached_course_is_member(course, user): key = "%s:%s:is_member" % (course.id, user.id) - if not key in cache: + if key not in cache: cache.set(key, course.is_member(user), 3) return cache.get(key) def cached_course_is_faculty(course, user): key = "%s:%s:is_faculty" % (course.id, user.id) - if not key in cache: + if key not in cache: cache.set(key, course.is_faculty(user), 3) return cache.get(key) + + +def get_guest_sandbox(): + return Course.objects.get(title="Mediathread Guest Sandbox") diff --git a/mediathread/main/features/homepage.feature b/mediathread/main/features/homepage.feature index 20c22d617..fc99349e9 100644 --- a/mediathread/main/features/homepage.feature +++ b/mediathread/main/features/homepage.feature @@ -13,7 +13,7 @@ Feature: Homepage When I open the reports menu Then there is an "Assignment Responses" link And there is a "Class Activity" link - And there is a "Student Contributions" link + And there is a "Class Member Contributions" link And there is a From Your Instructor column And there is a Composition column diff --git a/mediathread/main/features/instructor.feature b/mediathread/main/features/instructor.feature index 9d78a777d..68f34054e 100644 --- a/mediathread/main/features/instructor.feature +++ b/mediathread/main/features/instructor.feature @@ -29,7 +29,7 @@ Feature: Instructor Dashboard And I see "forbidden" When I access the url "/reports/class_summary/" - Then I do not see "Student Contributions" + Then I do not see "Class Member Contributions" And I see "forbidden" Finished using Selenium @@ -120,8 +120,8 @@ Feature: Instructor Dashboard When I open the reports menu When I click the "Assignment Responses" link - Then there is a "1 / 6" link - When I click the "1 / 6" link + Then there is a "1 / 4" link + When I click the "1 / 4" link Then I see "Assignment Report: Sample Assignment" And I see "Student One" And I there is a "Sample Assignment Response" link @@ -141,7 +141,7 @@ Feature: Instructor Dashboard Given I am test_instructor in Sample Course When I open the reports menu - When I click the "Student Contributions" link - Then I see "Report: Student Contributions" + When I click the "Class Member Contributions" link + Then I see "Report: Class Member Contributions" Finished using Selenium \ No newline at end of file diff --git a/mediathread/main/forms.py b/mediathread/main/forms.py index 09d80e4d9..edd7312ad 100644 --- a/mediathread/main/forms.py +++ b/mediathread/main/forms.py @@ -1,5 +1,6 @@ from django import forms -from captcha.fields import CaptchaField +from registration.forms import RegistrationForm + TERM_CHOICES = ( ('Fall', 'Fall'), @@ -8,18 +9,18 @@ class RequestCourseForm(forms.Form): - name = forms.CharField(required=True, max_length="512") + name = forms.CharField(required=True, max_length=512) email = forms.EmailField(required=True) uni = forms.CharField(required=True, max_length=512) - course = forms.CharField(required=True, max_length="512") - course_id = forms.CharField(required=True, max_length="512") + course = forms.CharField(required=True, max_length=512) + course_id = forms.CharField(required=True, max_length=512) term = forms.ChoiceField(required=True, choices=TERM_CHOICES) - year = forms.CharField(required=True, max_length="512") + year = forms.CharField(required=True, max_length=512) - instructor = forms.CharField(required=True, max_length="512") - section_leader = forms.CharField(max_length="512", required=False) + instructor = forms.CharField(required=True, max_length=512) + section_leader = forms.CharField(max_length=512, required=False) start = forms.DateField(required=True) end = forms.DateField(required=True) @@ -28,4 +29,66 @@ class RequestCourseForm(forms.Form): assignments_required = forms.BooleanField(required=True) description = forms.CharField(widget=forms.Textarea, required=True) - captcha = CaptchaField(required=True) + + decoy = forms.CharField(widget=forms.Textarea, required=False) + + title = forms.CharField(widget=forms.HiddenInput()) + pid = forms.CharField(widget=forms.HiddenInput()) + mid = forms.CharField(widget=forms.HiddenInput()) + type = forms.CharField(widget=forms.HiddenInput()) + owner = forms.CharField(widget=forms.HiddenInput()) + assigned_to = forms.CharField(widget=forms.HiddenInput()) + + def clean(self): + cleaned_data = super(RequestCourseForm, self).clean() + + if 'decoy' in cleaned_data and len(cleaned_data['decoy']) > 0: + self._errors["decoy"] = self.error_class([ + "Please leave this field blank"]) + + return cleaned_data + + +class ContactUsForm(forms.Form): + name = forms.CharField(required=True, max_length=512) + email = forms.EmailField(required=True) + username = forms.CharField(required=False, max_length=512) + course = forms.CharField(required=True, max_length=512) + + issue_date = forms.DateTimeField(required=True) + + category = forms.CharField(required=True, max_length=512) + + description = forms.CharField(widget=forms.Textarea, required=True) + + decoy = forms.CharField(widget=forms.Textarea, required=False) + + debug_info = forms.CharField(widget=forms.HiddenInput()) + title = forms.CharField(widget=forms.HiddenInput()) + pid = forms.CharField(widget=forms.HiddenInput()) + mid = forms.CharField(widget=forms.HiddenInput()) + type = forms.CharField(widget=forms.HiddenInput()) + owner = forms.CharField(widget=forms.HiddenInput()) + assigned_to = forms.CharField(widget=forms.HiddenInput()) + + def clean(self): + cleaned_data = super(ContactUsForm, self).clean() + + if cleaned_data['category'] == '-----': + self._errors["category"] = self.error_class([ + "An issue category is required"]) + + if 'decoy' in cleaned_data and len(cleaned_data['decoy']) > 0: + self._errors["decoy"] = self.error_class([ + "Please leave this field blank"]) + + return cleaned_data + + +class CustomRegistrationForm(RegistrationForm): + first_name = forms.CharField(required=True) + last_name = forms.CharField(required=True) + title = forms.CharField(required=False) + institution = forms.CharField(required=True) + referred_by = forms.CharField(required=True, widget=forms.Textarea) + user_story = forms.CharField(required=False, widget=forms.Textarea) diff --git a/mediathread/main/models.py b/mediathread/main/models.py index 8a0f0f7a2..65cd153e7 100644 --- a/mediathread/main/models.py +++ b/mediathread/main/models.py @@ -1,5 +1,8 @@ from django.contrib.auth.models import User from django.db import models +from registration.signals import user_registered, user_activated + +from mediathread.main.course_details import get_guest_sandbox class UserSetting(models.Model): @@ -31,3 +34,41 @@ def set_setting(cls, user, setting_id, value): user_setting.save() except: UserSetting.objects.create(user=user, name=setting_id, value=value) + + +class UserProfile(models.Model): + '''UserProfile adds extra information to a user, + and associates the user with a group, school, + and country.''' + user = models.OneToOneField(User, related_name='profile') + title = models.CharField(max_length=256, null=True, blank=True) + institution = models.TextField() + referred_by = models.TextField() + user_story = models.TextField(null=True, blank=True) + self_registered = models.BooleanField(default=False) + + def __unicode__(self): + return self.user.username + + +def user_registered_callback(sender, user, request, **kwargs): + user.first_name = request.POST['first_name'].strip() + user.last_name = request.POST['last_name'].strip() + user.save() + + UserProfile.objects.create(user=user, + self_registered=True, + title=request.POST['title'].strip(), + institution=request.POST['institution'].strip(), + referred_by=request.POST['referred_by'], + user_story=request.POST['user_story']) + + +def user_activated_callback(sender, user, request, **kwargs): + # add this user to the guest sandbox by default + sandbox = get_guest_sandbox() + sandbox.group.user_set.add(user) + + +user_registered.connect(user_registered_callback) +user_activated.connect(user_activated_callback) diff --git a/mediathread/main/tests/__init__.py b/mediathread/main/tests/__init__.py index 186f3db18..54224664b 100644 --- a/mediathread/main/tests/__init__.py +++ b/mediathread/main/tests/__init__.py @@ -1,4 +1,8 @@ -#pylint: disable-msg=W0401 +# pylint: disable-msg=W0401 # flake8: noqa from mediathread.main.tests.test_api import * +from mediathread.main.tests.test_auth import * +from mediathread.main.tests.test_forms import * from mediathread.main.tests.test_views import * +from mediathread.main.tests.test_commands import * +from mediathread.main.tests.test_course_details import * diff --git a/mediathread/main/tests/test_api.py b/mediathread/main/tests/test_api.py index 01dce914e..02580a0bc 100644 --- a/mediathread/main/tests/test_api.py +++ b/mediathread/main/tests/test_api.py @@ -1,27 +1,23 @@ # pylint: disable-msg=R0904 # pylint: disable-msg=E1103 -from courseaffils.models import Course from django.contrib.auth.models import User +from django.test.testcases import TestCase + +from courseaffils.models import Course from mediathread.api import UserResource -from tastypie.test import ResourceTestCase from mediathread.factories import (UserFactory, GroupFactory, CourseFactory) -class UserApiTest(ResourceTestCase): - # Use ``fixtures`` & ``urls`` as normal. See Django's ``TestCase`` - # documentation for the gory details. +class UserApiTest(TestCase): def get_credentials(self): return None def test_render_one(self): - u = UserFactory(username='test_student_one', first_name='Student', - last_name='One') - u.set_password('test') - u.save() + u = UserFactory(username='test_student_one', + first_name='Student', last_name='One') self.assertTrue( - self.api_client.client.login(username="test_student_one", - password="test")) + self.client.login(username=u.username, password="test")) student_one = User.objects.get(username='test_student_one') @@ -30,13 +26,11 @@ def test_render_one(self): self.assertEquals(member['public_name'], "Student One") def test_render_list(self): - u = UserFactory(username='test_student_one', first_name='Student', - last_name='One') - u.set_password('test') - u.save() + u = UserFactory(username='test_student_one', + first_name='Student', last_name='One') + self.assertTrue( - self.api_client.client.login(username="test_student_one", - password="test")) + self.client.login(username=u.username, password="test")) g1 = GroupFactory(name="group1") g2 = GroupFactory(name="group2") @@ -46,11 +40,11 @@ def test_render_list(self): first_name='Instructor', last_name='One').groups.add(g2) UserFactory(username='test_instructor_two').groups.add(g2) UserFactory(username='test_student_three').groups.add(g2) - UserFactory(username='student_two', first_name='Student', - last_name='Two').groups.add(g2) + UserFactory(username='student_two', + first_name='Student', last_name='Two').groups.add(g2) UserFactory(username='teachers_assistant', first_name="Teacher's", - last_name=" Assistant").groups.add(g2) + last_name="Assistant").groups.add(g2) sample_course = Course.objects.get(title="Sample Course") @@ -59,10 +53,10 @@ def test_render_list(self): self.assertEquals(len(members), 6) self.assertEquals(members[0]['public_name'], "test_instructor_two") self.assertEquals(members[1]['public_name'], "test_student_three") - self.assertEquals(members[2]['public_name'], "Instructor One") - self.assertEquals(members[3]['public_name'], "Student One") - self.assertEquals(members[4]['public_name'], "Student Two") - self.assertEquals(members[5]['public_name'], "Teacher's Assistant") + self.assertEquals(members[2]['public_name'], "Teacher's Assistant") + self.assertEquals(members[3]['public_name'], "Instructor One") + self.assertEquals(members[4]['public_name'], "Student One") + self.assertEquals(members[5]['public_name'], "Student Two") def test_get_course_list(self): g1 = GroupFactory(name="group1") diff --git a/mediathread/main/tests/test_views.py b/mediathread/main/tests/test_views.py index ce139e7f9..8c3367378 100644 --- a/mediathread/main/tests/test_views.py +++ b/mediathread/main/tests/test_views.py @@ -1,14 +1,26 @@ -#pylint: disable-msg=R0904 -#pylint: disable-msg=E1103 +# pylint: disable-msg=R0904 +# pylint: disable-msg=E1103 +from datetime import datetime +import json + from courseaffils.models import Course -from django.contrib.auth.models import User +from django.conf import settings +from django.contrib.auth.models import User, AnonymousUser +from django.core import mail from django.http.response import Http404 from django.test import TestCase from django.test.client import Client, RequestFactory + from mediathread.assetmgr.models import Asset -from mediathread.main.views import MigrateCourseView +from mediathread.factories import UserFactory, MediathreadTestMixin, \ + AssetFactory, ProjectFactory, SherdNoteFactory +from mediathread.main import course_details +from mediathread.main.course_details import allow_public_compositions, \ + course_information_title, all_items_are_visible, all_selections_are_visible +from mediathread.main.forms import ContactUsForm, RequestCourseForm +from mediathread.main.views import MigrateCourseView, ContactUsView, \ + RequestCourseView, CourseSettingsView, CourseManageSourcesView from mediathread.projects.models import Project -import json class SimpleViewTest(TestCase): @@ -31,25 +43,53 @@ def test_smoke(self): self.assertEquals(response.status_code, 200) -class MigrateCourseViewTest(TestCase): - fixtures = ['unittest_sample_course.json', - 'unittest_sample_projects.json'] +class MigrateCourseViewTest(MediathreadTestMixin, TestCase): def setUp(self): - self.factory = RequestFactory() - self.faculty = User.objects.get(username='test_instructor_two') + self.setup_sample_course() + self.setup_alternate_course() self.superuser = User.objects.create(username='ccnmtl', password='test', is_superuser=True, is_staff=True) + # instructor that sees both Sample Course & Alternate Course + self.instructor_three = UserFactory(username='instructor_three', + first_name='Instructor', + last_name='Three') + self.add_as_faculty(self.sample_course, self.instructor_three) + self.add_as_faculty(self.alt_course, self.instructor_three) + self.sample_course = Course.objects.get(title='Sample Course') self.alt_course = Course.objects.get(title="Alternate Course") + self.asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image') + + self.student_note = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',student_one_selection', + body='student one selection note', range1=0, range2=1) + self.instructor_note = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_selection,', + body='instructor one selection note', range1=0, range2=1) + self.instructor_ga = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_global,', + body='instructor one global note', + title=None, range1=None, range2=None) + self.instructor_two_ga = SherdNoteFactory( + asset=self.asset1, author=self.instructor_two, + tags=',instructor_two_global,', + body='instructor two global note', + title=None, range1=None, range2=None) + def test_as_student(self): self.assertTrue( - self.client.login(username='test_student_one', password='test')) + self.client.login(username=self.student_one.username, + password='test')) response = self.client.get('/dashboard/migrate/') self.assertEquals(response.status_code, 403) @@ -59,7 +99,7 @@ def test_not_logged_in(self): def test_get_context_data(self): request = RequestFactory().get('/dashboard/migrate/') - request.user = User.objects.get(username='test_instructor_two') + request.user = self.instructor_three request.course = self.sample_course view = MigrateCourseView() @@ -67,11 +107,13 @@ def test_get_context_data(self): ctx = view.get_context_data() - self.assertEquals(len(ctx['current_course_faculty']), 2) + self.assertEquals(len(ctx['current_course_faculty']), 3) self.assertEquals(ctx['current_course_faculty'][0].username, - 'test_instructor') + 'instructor_one') self.assertEquals(ctx['current_course_faculty'][1].username, - 'test_instructor_two') + 'instructor_three') + self.assertEquals(ctx['current_course_faculty'][2].username, + 'instructor_two') self.assertEquals(len(ctx['available_courses']), 2) self.assertEquals(ctx['available_courses'][0].title, @@ -88,9 +130,7 @@ def test_get_context_data(self): 'Sample Course') def test_post_invalidcourse(self): - data = { - 'fromCourse': 23 - } + data = {'fromCourse': 42} request = RequestFactory().post('/dashboard/migrate/', data) request.user = self.superuser @@ -102,10 +142,9 @@ def test_post_invalidcourse(self): self.assertRaises(Http404, view.post, request) def test_post_on_behalf_of_student(self): - student = User.objects.get(username='test_student_alt') data = { 'fromCourse': self.alt_course.id, - 'on_behalf_of': student.id + 'on_behalf_of': self.alt_student.id } request = RequestFactory().post('/dashboard/migrate/', data) @@ -120,10 +159,9 @@ def test_post_on_behalf_of_student(self): self.assertFalse(the_json['success']) def test_post_on_behalf_of_faculty(self): - teacher = User.objects.get(username='test_instructor_alt') data = { 'fromCourse': self.alt_course.id, - 'on_behalf_of': teacher.id + 'on_behalf_of': self.alt_instructor.id } request = RequestFactory().post('/dashboard/migrate/', data) @@ -137,22 +175,163 @@ def test_post_on_behalf_of_faculty(self): the_json = json.loads(response.content) self.assertFalse(the_json['success']) - def test_post(self): - asset1 = Asset.objects.get( - course=self.sample_course, - title="The Armory - Home to CCNMTL'S CUMC Office") - project1 = Project.objects.get( - course=self.sample_course, title='Sample Course Assignment') + def test_migrate_asset(self): + data = {'fromCourse': self.sample_course.id, + 'asset_ids[]': [self.asset1.id], + 'project_ids[]': []} + + # Migrate assets from SampleCourse into Alternate Course + # test_instructor_three is a member of both courses + request = RequestFactory().post('/dashboard/migrate/', data) + request.user = self.instructor_three + request.course = self.alt_course + + view = MigrateCourseView() + view.request = request + response = view.post(request) + + the_json = json.loads(response.content) + self.assertTrue(the_json['success']) + self.assertEquals(the_json['asset_count'], 1) + self.assertEquals(the_json['project_count'], 0) + self.assertEquals(the_json['note_count'], 3) + + new_asset = Asset.objects.get(course=self.alt_course, + title=self.asset1.title) + self.assertEquals(new_asset.sherdnote_set.count(), 2) + + # verify there is a global annotation for instructor three + ga = new_asset.global_annotation(self.instructor_three, False) + self.assertIsNone(ga.title) + self.assertEquals(ga.tags, '') + self.assertIsNone(ga.body) + + # verify there is a selection annotation for instructor three + note = new_asset.sherdnote_set.get(title=self.instructor_note.title) + self.assertEquals(note.tags, '') + self.assertIsNone(note.body) + self.assertFalse(note.is_global_annotation()) + + def test_migrate_with_tags(self): + data = { + 'fromCourse': self.sample_course.id, + 'asset_ids[]': [self.asset1.id], + 'project_ids[]': [], + 'include_tags': 'true', + 'include_notes': 'false' + } + + # Migrate assets from SampleCourse into Alternate Course + # test_instructor_three is a member of both courses + request = RequestFactory().post('/dashboard/migrate/', data) + request.user = self.instructor_three + request.course = self.alt_course + + view = MigrateCourseView() + view.request = request + view.post(request) + + new_asset = Asset.objects.get(course=self.alt_course, + title=self.asset1.title) + self.assertEquals(new_asset.sherdnote_set.count(), 2) + + note = new_asset.sherdnote_set.get(title=self.instructor_note.title) + self.assertEquals(note.tags, self.instructor_note.tags) + self.assertIsNone(note.body) + + note = new_asset.global_annotation(self.instructor_three, False) + self.assertEquals( + note.tags, + u',image, instructor_one_global,,instructor_two_global,') + self.assertIsNone(note.body) + + def test_migrate_with_notes(self): + data = { + 'fromCourse': self.sample_course.id, + 'asset_ids[]': [self.asset1.id], + 'project_ids[]': [], + 'include_tags': 'false', + 'include_notes': 'true', + } + + # Migrate assets from SampleCourse into Alternate Course + # test_instructor_three is a member of both courses + request = RequestFactory().post('/dashboard/migrate/', data) + request.user = self.instructor_three + request.course = self.alt_course + + view = MigrateCourseView() + view.request = request + view.post(request) + + new_asset = Asset.objects.get(course=self.alt_course, + title=self.asset1.title) + self.assertEquals(new_asset.sherdnote_set.count(), 2) + + note = new_asset.sherdnote_set.get(title=self.instructor_note.title) + self.assertEquals(note.tags, '') + self.assertEquals(note.body, self.instructor_note.body) + + note = new_asset.global_annotation(self.instructor_three, False) + self.assertEquals(note.tags, '') + self.assertEquals( + note.body, + u'instructor one global noteinstructor two global note') + + def test_migrate_tags_and_notes(self): data = { 'fromCourse': self.sample_course.id, - 'asset_ids[]': [asset1.id], - 'project_ids[]': [project1.id] + 'asset_ids[]': [self.asset1.id], + 'project_ids[]': [], + 'include_tags': 'true', + 'include_notes': 'true' } # Migrate assets from SampleCourse into Alternate Course - # test_instructor_two is a member of both courses + # test_instructor_three is a member of both courses request = RequestFactory().post('/dashboard/migrate/', data) - request.user = User.objects.get(username='test_instructor_two') + request.user = self.instructor_three + request.course = self.alt_course + + view = MigrateCourseView() + view.request = request + view.post(request) + + new_asset = Asset.objects.get(course=self.alt_course, + title=self.asset1.title) + self.assertEquals(new_asset.sherdnote_set.count(), 2) + + note = new_asset.sherdnote_set.get(title=self.instructor_note.title) + self.assertEquals(note.tags, self.instructor_note.tags) + self.assertEquals(note.body, self.instructor_note.body) + + note = new_asset.global_annotation(self.instructor_three, False) + self.assertEquals( + note.tags, + u',image, instructor_one_global,,instructor_two_global,') + self.assertEquals( + note.body, + u'instructor one global noteinstructor two global note') + + def test_migrate_project(self): + self.project1 = ProjectFactory.create(course=self.sample_course, + author=self.instructor_one, + policy='PublicEditorsAreOwners') + + self.add_citation(self.project1, self.instructor_note) + self.add_citation(self.project1, self.student_note) + self.assertEquals(len(self.project1.citations()), 2) + + data = { + 'fromCourse': self.sample_course.id, + 'asset_ids[]': [], + 'project_ids[]': [self.project1.id] + } + + # Migrate assets from SampleCourse into Alternate Course + # test_instructor_three is a member of both courses + request = RequestFactory().post('/dashboard/migrate/', data) + request.user = self.instructor_three request.course = self.alt_course view = MigrateCourseView() @@ -161,49 +340,56 @@ def test_post(self): the_json = json.loads(response.content) self.assertTrue(the_json['success']) - self.assertEquals(the_json['asset_count'], 4) + self.assertEquals(the_json['asset_count'], 1) self.assertEquals(the_json['project_count'], 1) - self.assertEquals(the_json['note_count'], 6) + self.assertEquals(the_json['note_count'], 2) - Asset.objects.get( - course=self.alt_course, - title="The Armory - Home to CCNMTL'S CUMC Office") - Asset.objects.get( - course=self.alt_course, - title="Mediathread: Introduction") - Asset.objects.get(course=self.alt_course, title="MAAP Award Reception") - Asset.objects.get(course=self.alt_course, title="Project Portfolio") + new_asset = Asset.objects.get(course=self.alt_course, + title=self.asset1.title) + self.assertEquals(new_asset.sherdnote_set.count(), 3) - project1 = Project.objects.get( - course=self.alt_course, title='Sample Course Assignment') - self.assertEquals(len(project1.citations()), 5) + new_note = new_asset.sherdnote_set.get(title=self.student_note.title) + self.assertEquals(new_note.author, self.instructor_three) + new_note = new_asset.sherdnote_set.get( + title=self.instructor_note.title) + self.assertEquals(new_note.author, self.instructor_three) -class MigrateMaterialsTest(TestCase): - fixtures = ['unittest_sample_course.json', - 'unittest_sample_projects.json'] + new_note = new_asset.sherdnote_set.get(title=None) + self.assertEquals(new_note.author, self.instructor_three) + self.assertTrue(new_note.is_global_annotation()) - def get_credentials(self): - return None + new_project = Project.objects.get( + course=self.alt_course, title=self.project1.title) + self.assertEquals(len(new_project.citations()), 2) - def test_as_student(self): - self.assertTrue(self.client.login(username="test_student_one", + def test_migrate_materials_view_student(self): + self.assertTrue(self.client.login(username=self.student_one.username, password="test")) - sample_course = Course.objects.get(title="Sample Course") - response = self.client.get('/dashboard/migrate/materials/%s/' % - sample_course.id, {}, + self.sample_course.id, {}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 403) - def test_sample_course(self): - self.assertTrue(self.client.login(username="test_instructor", - password="test")) - - sample_course = Course.objects.get(title="Sample Course") + def test_migrate_materials_sample_course(self): + self.project1 = ProjectFactory.create(course=self.sample_course, + author=self.instructor_one, + policy='PrivateEditorsAreOwners') + self.project2 = ProjectFactory.create(course=self.sample_course, + author=self.instructor_one, + policy='Assignment') + + self.assertTrue(self.client.login( + username=self.instructor_three.username, + password="test")) + + set_course_url = '/?set_course=%s&next=/' % \ + self.sample_course.group.name + response = self.client.get(set_course_url, follow=True) + self.assertEquals(response.status_code, 200) - url = '/dashboard/migrate/materials/%s/' % sample_course.id + url = '/dashboard/migrate/materials/%s/' % self.sample_course.id response = self.client.get(url, {}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') @@ -211,33 +397,26 @@ def test_sample_course(self): the_json = json.loads(response.content) self.assertEquals(the_json['course']['title'], 'Sample Course') - self.assertEquals(len(the_json['assets']), 4) + self.assertEquals(len(the_json['assets']), 1) self.assertEquals(the_json['assets'][0]['title'], - 'Mediathread: Introduction') - self.assertEquals(the_json['assets'][0]['annotation_count'], 4) - - self.assertEquals(the_json['assets'][1]['title'], - 'Project Portfolio') - self.assertEquals(the_json['assets'][1]['annotation_count'], 0) - - self.assertEquals(the_json['assets'][2]['title'], - 'MAAP Award Reception') - self.assertEquals(the_json['assets'][2]['annotation_count'], 1) - - self.assertEquals(the_json['assets'][3]['title'], - "The Armory - Home to CCNMTL'S CUMC Office") - self.assertEquals(the_json['assets'][3]['annotation_count'], 1) + self.asset1.title) + self.assertEquals(the_json['assets'][0]['annotation_count'], 1) self.assertEquals(len(the_json['projects']), 1) + self.assertEquals(the_json['projects'][0]['title'], + self.project2.title) + + def test_migrate_materials_alternate_course(self): + self.assertTrue(self.client.login( + username=self.instructor_three.username, + password="test")) + set_course_url = '/?set_course=%s&next=/' % \ + self.alt_course.group.name + response = self.client.get(set_course_url, follow=True) + self.assertEquals(response.status_code, 200) - def test_alternate_course(self): - self.assertTrue(self.client.login(username="test_instructor_alt", - password="test")) - - sample_course = Course.objects.get(title="Alternate Course") - - url = '/dashboard/migrate/materials/%s/' % sample_course.id + url = '/dashboard/migrate/materials/%s/' % self.alt_course.id response = self.client.get(url, {}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') @@ -245,10 +424,241 @@ def test_alternate_course(self): the_json = json.loads(response.content) self.assertEquals(the_json['course']['title'], 'Alternate Course') - self.assertEquals(len(the_json['assets']), 1) + self.assertEquals(len(the_json['assets']), 0) + self.assertEquals(len(the_json['projects']), 0) + + +class ContactUsViewTest(TestCase): + + def test_get_initial_anonymous(self): + view = ContactUsView() + view.request = RequestFactory().get('/contact/') + view.request.session = {} + view.request.user = AnonymousUser() + view.get_initial() + + self.assertIsNotNone(view.initial['issue_date']) + self.assertFalse('name' in view.initial) + self.assertFalse('email' in view.initial) + self.assertFalse('username' in view.initial) + + def test_get_initial_not_anonymous(self): + view = ContactUsView() + view.request = RequestFactory().get('/contact/') + view.request.session = {} + view.request.user = UserFactory(first_name='Foo', + last_name='Bar', + email='foo@bar.com') + + view.get_initial() + + self.assertIsNotNone(view.initial['issue_date']) + self.assertEquals(view.initial['name'], 'Foo Bar') + self.assertEquals(view.initial['email'], 'foo@bar.com') + self.assertEquals(view.initial['username'], view.request.user.username) + + def test_form_valid(self): + view = ContactUsView() + form = ContactUsForm() + form.cleaned_data = { + 'issuer_date': datetime.now(), + 'name': 'Linus Torvalds', + 'username': 'ltorvalds', + 'email': 'sender@ccnmtl.columbia.edu', + 'course': 'Introduction to Linux', + 'category': 'View Image', + 'description': 'This is a problem' + } - self.assertEquals(the_json['assets'][0]['title'], - 'Design Research') - self.assertEquals(the_json['assets'][0]['annotation_count'], 2) + # SUPPORT DESTINATION is null + view.form_valid(form) + self.assertEqual(len(mail.outbox), 1) + + self.assertEqual(mail.outbox[0].subject, + 'Mediathread Contact Us Request') + self.assertEquals(mail.outbox[0].from_email, + settings.SERVER_EMAIL) + self.assertEquals(mail.outbox[0].to, + ['sender@ccnmtl.columbia.edu']) + + def test_form_valid_with_support_destination(self): + view = ContactUsView() + form = ContactUsForm() + form.cleaned_data = { + 'issuer_date': datetime.now(), + 'name': 'Linus Torvalds', + 'username': 'ltorvalds', + 'email': 'sender@ccnmtl.columbia.edu', + 'course': 'Introduction to Linux', + 'category': 'View Image', + 'description': 'This is a problem' + } - self.assertEquals(len(the_json['projects']), 1) + with self.settings(SUPPORT_DESTINATION='support@ccnmtl.columbia.edu'): + view.form_valid(form) + self.assertEqual(len(mail.outbox), 1) + + self.assertEqual(mail.outbox[0].subject, + 'Mediathread Contact Us Request') + self.assertEquals(mail.outbox[0].from_email, + 'sender@ccnmtl.columbia.edu') + self.assertEquals(mail.outbox[0].to, + [settings.SUPPORT_DESTINATION]) + + +class RequestCourseViewTest(TestCase): + + def test_form_valid(self): + view = RequestCourseView() + form = RequestCourseForm() + form.cleaned_data = { + 'name': 'Test Instructor', + 'email': 'test_instructor@ccnmtl.columbia.edu', + 'uni': 'ttt123', + 'course': 'Test Course', + 'course_id': 'Test Course Id', + 'term': 'Fall', + 'year': '2014', + 'instructor': 'Test Instructor', + 'section_leader': 'Test Teachers Assistant', + 'start': datetime.now(), + 'end': datetime.now(), + 'students': 24, + 'assignments_required': True, + 'description': 'Description', + 'title': 'The Course', + 'pid': '123', + 'mid': '456', + 'type': 'action item', + 'owner': 'sdreher', + 'assigned_to': 'sdreher' + } + + with self.settings(TASK_ASSIGNMENT_DESTINATION=None): + view.form_valid(form) + + +class CourseSettingsViewTest(MediathreadTestMixin, TestCase): + + def setUp(self): + self.setup_sample_course() + + def test_not_logged_in(self): + response = self.client.get('/dashboard/settings/') + self.assertEquals(response.status_code, 302) + + def test_as_student(self): + self.assertTrue( + self.client.login(username=self.student_one.username, + password='test')) + response = self.client.get('/dashboard/settings/') + self.assertEquals(response.status_code, 403) + + def test_get_context_data(self): + request = RequestFactory().get('/dashboard/settings/') + request.user = self.instructor_one + request.course = self.sample_course + + view = CourseSettingsView() + view.request = request + + ctx = view.get_context_data() + + self.assertEquals(ctx['course'], self.sample_course) + self.assertEquals(ctx[course_details.ALLOW_PUBLIC_COMPOSITIONS_KEY], + course_details.ALLOW_PUBLIC_COMPOSITIONS_DEFAULT) + + self.assertEquals(ctx[course_details.SELECTION_VISIBILITY_KEY], + course_details.SELECTION_VISIBILITY_DEFAULT) + + self.assertEquals(ctx[course_details.ITEM_VISIBILITY_KEY], + course_details.ITEM_VISIBILITY_DEFAULT) + + self.assertEquals(ctx[course_details.COURSE_INFORMATION_TITLE_KEY], + course_details.COURSE_INFORMATION_TITLE_DEFAULT) + + def test_post_allow_public_compositions(self): + self.assertTrue( + self.client.login(username=self.instructor_one.username, + password='test')) + project = ProjectFactory.create( + course=self.sample_course, author=self.instructor_one, + policy='PublicEditorsAreOwners') + + self.client.post( + '/dashboard/settings/', + {course_details.ALLOW_PUBLIC_COMPOSITIONS_KEY: 0}) + + col = project.collaboration() + self.assertEquals(col._policy.policy_name, 'CourseProtected') + + def test_post(self): + self.assertTrue( + self.client.login(username=self.instructor_one.username, + password='test')) + data = { + course_details.COURSE_INFORMATION_TITLE_KEY: "Foo", + course_details.SELECTION_VISIBILITY_KEY: 0, + course_details.ITEM_VISIBILITY_KEY: 0, + course_details.ALLOW_PUBLIC_COMPOSITIONS_KEY: 1 + } + + response = self.client.post('/dashboard/settings/', data) + self.assertEquals(response.status_code, 302) + self.assertEquals(course_information_title(self.sample_course), "Foo") + self.assertTrue(allow_public_compositions(self.sample_course)) + self.assertFalse(all_items_are_visible(self.sample_course)) + self.assertFalse(all_selections_are_visible(self.sample_course)) + + +class CourseManageSourcesViewTest(MediathreadTestMixin, TestCase): + + def setUp(self): + self.setup_sample_course() + self.enable_upload(self.sample_course) + self.superuser = UserFactory(is_staff=True, is_superuser=True) + + def test_not_logged_in(self): + response = self.client.get('/dashboard/sources/') + self.assertEquals(response.status_code, 302) + + def test_as_student(self): + self.assertTrue( + self.client.login(username=self.student_one.username, + password='test')) + response = self.client.get('/dashboard/sources/') + self.assertEquals(response.status_code, 403) + + def test_get_context(self): + request = RequestFactory().get('/dashboard/sources/') + request.user = self.instructor_one + request.course = self.sample_course + + view = CourseManageSourcesView() + view.request = request + + ctx = view.get_context_data() + self.assertEquals(ctx['course'], self.sample_course) + self.assertEquals(list(ctx['supported_archives']), []) + self.assertEquals(ctx['space_viewer'], self.instructor_one) + self.assertFalse(ctx['is_staff']) + self.assertEquals(ctx['newsrc'], '') + self.assertEquals(ctx['delsrc'], '') + self.assertTrue(ctx['upload_enabled']) + + def test_post(self): + self.assertTrue( + self.client.login(username=self.instructor_one.username, + password='test')) + data = { + course_details.UPLOAD_PERMISSION_KEY: + course_details.UPLOAD_PERMISSION_ADMINISTRATOR + } + + self.client.post('/dashboard/sources/', data) + self.assertTrue(course_details.can_upload(self.superuser, + self.sample_course)) + self.assertFalse(course_details.can_upload(self.instructor_one, + self.sample_course)) + self.assertFalse(course_details.can_upload(self.student_one, + self.sample_course)) diff --git a/mediathread/main/views.py b/mediathread/main/views.py index cfbec3c70..b294e6c76 100644 --- a/mediathread/main/views.py +++ b/mediathread/main/views.py @@ -1,9 +1,17 @@ -from courseaffils.lib import in_course, in_course_or_404 +from datetime import datetime +import json +import operator + +from courseaffils.lib import in_course_or_404, in_course +from courseaffils.middleware import SESSION_KEY from courseaffils.models import Course from courseaffils.views import available_courses_query from django.conf import settings +from django.contrib import messages from django.contrib.auth.decorators import login_required from django.contrib.auth.models import User +from django.core.mail import send_mail +from django.core.urlresolvers import reverse from django.http import HttpResponse, HttpResponseRedirect, Http404 from django.shortcuts import get_object_or_404 from django.template import loader @@ -11,38 +19,36 @@ from django.views.generic.base import TemplateView, View from django.views.generic.edit import FormView from djangohelpers.lib import rendered_with, allow_http +from restclient import POST + from mediathread.api import UserResource, CourseInfoResource from mediathread.assetmgr.api import AssetResource from mediathread.assetmgr.models import Asset, SupportedSource from mediathread.discussions.utils import get_course_discussions from mediathread.djangosherd.models import SherdNote from mediathread.main import course_details -from mediathread.main.course_details import cached_course_is_faculty -from mediathread.main.forms import RequestCourseForm +from mediathread.main.course_details import cached_course_is_faculty, \ + course_information_title +from mediathread.main.forms import RequestCourseForm, ContactUsForm from mediathread.main.models import UserSetting -from mediathread.mixins import ajax_required, faculty_only, \ +from mediathread.mixins import ajax_required, \ AjaxRequiredMixin, JSONResponseMixin, LoggedInFacultyMixin from mediathread.projects.api import ProjectResource from mediathread.projects.models import Project -from restclient import POST from structuredcollaboration.models import Collaboration -import json -import operator # returns important setting information for all web pages. def django_settings(request): - whitelist = ['PUBLIC_CONTACT_EMAIL', - 'CONTACT_US_DESTINATION', - 'FLOWPLAYER_SWF_LOCATION', + whitelist = ['FLOWPLAYER_SWF_LOCATION', 'FLOWPLAYER_AUDIO_PLUGIN', 'FLOWPLAYER_PSEUDOSTREAMING_PLUGIN', 'FLOWPLAYER_RTMP_PLUGIN', 'DEBUG', 'REVISION', 'DATABASES', - 'GOOGLE_ANALYTICS_ID' - ] + 'GOOGLE_ANALYTICS_ID', + 'CAS_BASE'] context = {'settings': dict([(k, getattr(settings, k, None)) for k in whitelist]), @@ -51,6 +57,10 @@ def django_settings(request): if request.course: context['is_course_faculty'] = request.course.is_faculty(request.user) + user_agent = request.META.get("HTTP_USER_AGENT") + if user_agent is not None and 'firefox' in user_agent.lower(): + context['settings']['FIREFOX'] = True + return context @@ -101,10 +111,7 @@ def triple_homepage(request): context = { 'classwork_owner': classwork_owner, - 'help_homepage_instructor_column': False, - 'help_homepage_classwork_column': False, - 'upgrade_bookmarklet': UserSetting.get_setting( - logged_in_user, "upgrade_bookmarklet", True), + "information_title": course_information_title(course), 'faculty_feed': Project.objects.faculty_compositions(request, course), 'is_faculty': course.is_faculty(logged_in_user), 'discussions': get_course_discussions(course), @@ -140,103 +147,92 @@ def upgrade_bookmarklet(request): return context -@allow_http("GET", "POST") -@rendered_with('dashboard/class_manage_sources.html') -@faculty_only -def class_manage_sources(request): - key = course_details.UPLOAD_PERMISSION_KEY +class CourseManageSourcesView(LoggedInFacultyMixin, TemplateView): + template_name = 'dashboard/class_manage_sources.html' - course = request.course - user = request.user + def get_context_data(self, **kwargs): + course = self.request.course - upload_enabled = False - for item in course.asset_set.archives().order_by('title'): - attribute = item.metadata().get('upload', 0) - value = attribute[0] if hasattr(attribute, 'append') else attribute - if value and int(value) == 1: - upload_enabled = True - break + upload_enabled = course_details.is_upload_enabled(course) - context = { - 'asset_request': request.GET, - 'course': course, - 'supported_archives': SupportedSource.objects.all().order_by("title"), - 'space_viewer': request.user, - 'is_staff': request.user.is_staff, - 'newsrc': request.GET.get('newsrc', ''), - 'delsrc': request.GET.get('delsrc', ''), - 'upload_enabled': upload_enabled, - 'permission_levels': course_details.UPLOAD_PERMISSION_LEVELS, - 'help_video_upload': UserSetting.get_setting( - user, "help_video_upload", True), - 'help_supported_collections': UserSetting.get_setting( - user, "help_supported_collections", True), - 'help_dashboard_nav_actions': UserSetting.get_setting( - user, "help_dashboard_nav_actions", False), - 'help_dashboard_nav_reports': UserSetting.get_setting( - user, "help_dashboard_nav_reports", False) - } + supported_sources = SupportedSource.objects.all().order_by("title") + upload_permission = int(course.get_detail( + course_details.UPLOAD_PERMISSION_KEY, + course_details.UPLOAD_PERMISSION_DEFAULT)) - if request.method == "GET": - context[key] = int(course.get_detail( - key, course_details.UPLOAD_PERMISSION_DEFAULT)) - else: - upload_permission = request.POST.get(key) - request.course.add_detail(key, upload_permission) - context['changes_saved'] = True - context[key] = int(upload_permission) + return { + 'course': course, + 'supported_archives': supported_sources, + 'space_viewer': self.request.user, + 'is_staff': self.request.user.is_staff, + 'newsrc': self.request.GET.get('newsrc', ''), + 'delsrc': self.request.GET.get('delsrc', ''), + 'upload_enabled': upload_enabled, + 'permission_levels': course_details.UPLOAD_PERMISSION_LEVELS, + course_details.UPLOAD_PERMISSION_KEY: upload_permission + } - return context + def post(self, request): + perm = request.POST.get( + course_details.UPLOAD_PERMISSION_KEY) + request.course.add_detail(course_details.UPLOAD_PERMISSION_KEY, perm) + messages.add_message(request, messages.INFO, + 'Your changes were saved.') -@allow_http("GET", "POST") -@login_required -@rendered_with('dashboard/class_settings.html') -@faculty_only -def class_settings(request): - course = request.course - user = request.user + return HttpResponseRedirect(reverse("class-manage-sources")) - context = { - 'asset_request': request.GET, - 'course': course, - 'space_viewer': request.user, - 'is_staff': request.user.is_staff, - 'help_public_compositions': UserSetting.get_setting( - user, "help_public_compositions", True), - 'help_selection_visibility': UserSetting.get_setting( - user, "help_selection_visibility", True), - } - public_composition_key = course_details.ALLOW_PUBLIC_COMPOSITIONS_KEY - context[course_details.ALLOW_PUBLIC_COMPOSITIONS_KEY] = int( - course.get_detail(public_composition_key, - course_details.ALLOW_PUBLIC_COMPOSITIONS_DEFAULT)) - - selection_visibility_key = course_details.SELECTION_VISIBILITY_KEY - context[course_details.SELECTION_VISIBILITY_KEY] = int( - course.get_detail(selection_visibility_key, - course_details.SELECTION_VISIBILITY_DEFAULT)) - - if request.method == "POST": - if selection_visibility_key in request.POST: - selection_visibility_value = \ - int(request.POST.get(selection_visibility_key)) - request.course.add_detail(selection_visibility_key, - selection_visibility_value) - context[selection_visibility_key] = selection_visibility_value - - if public_composition_key in request.POST: - public_composition_value = \ - int(request.POST.get(public_composition_key)) - request.course.add_detail(public_composition_key, - public_composition_value) - context[public_composition_key] = public_composition_value - - if public_composition_value == 0: +class CourseSettingsView(LoggedInFacultyMixin, TemplateView): + template_name = 'dashboard/class_settings.html' + + def get_context_data(self, **kwargs): + + context = {'course': self.request.course} + + key = course_details.ALLOW_PUBLIC_COMPOSITIONS_KEY + context[key] = int(self.request.course.get_detail(key, + course_details.ALLOW_PUBLIC_COMPOSITIONS_DEFAULT)) + + key = course_details.SELECTION_VISIBILITY_KEY + context[key] = int(self.request.course.get_detail(key, + course_details.SELECTION_VISIBILITY_DEFAULT)) + + key = course_details.ITEM_VISIBILITY_KEY + context[key] = int(self.request.course.get_detail(key, + course_details.ITEM_VISIBILITY_DEFAULT)) + + key = course_details.COURSE_INFORMATION_TITLE_KEY + context[key] = self.request.course.get_detail( + key, course_details.COURSE_INFORMATION_TITLE_DEFAULT) + + return context + + def post(self, request): + key = course_details.COURSE_INFORMATION_TITLE_KEY + if key in request.POST: + value = request.POST.get(key) + request.course.add_detail(key, value) + + key = course_details.SELECTION_VISIBILITY_KEY + if key in request.POST: + value = int(request.POST.get(key)) + request.course.add_detail(key, value) + + key = course_details.ITEM_VISIBILITY_KEY + if key in request.POST: + value = int(request.POST.get(key)) + request.course.add_detail(key, value) + + key = course_details.ALLOW_PUBLIC_COMPOSITIONS_KEY + if key in request.POST: + value = int(request.POST.get(key)) + request.course.add_detail(key, value) + + if value == 0: # Check any existing projects -- if they are # world publishable, turn this feature OFF - projects = Project.objects.filter(course=course) + projects = Project.objects.filter(course=request.course) for project in projects: try: col = Collaboration.objects.get_for_object(project) @@ -246,9 +242,10 @@ def class_settings(request): except: pass - context['changes_saved'] = True + messages.add_message(request, messages.INFO, + 'Your changes were saved.') - return context + return HttpResponseRedirect(reverse('course-settings')) @allow_http("POST") @@ -284,7 +281,8 @@ def get_context_data(self, **kwargs): # Only send down the real faculty. Not all us staff members faculty = [] - for user in self.request.course.faculty.all(): + for user in self.request.course.faculty.all().order_by('last_name', + 'username'): faculty.append(user) return { @@ -360,19 +358,21 @@ def get(self, request, *args, **kwargs): # filter assets & notes by the faculty set assets = Asset.objects.by_course(course) assets = assets.filter(sherdnote_set__author__id__in=faculty) - notes = SherdNote.objects.get_related_notes(assets, None, faculty) + notes = SherdNote.objects.get_related_notes( + assets, None, faculty, True) ares = AssetResource(include_annotations=False) - asset_ctx = ares.render_list(request, None, assets, notes) + asset_ctx = ares.render_list(request, None, None, assets, notes) projects = Project.objects.by_course_and_users(course, faculty) # filter private projects - collabs = Collaboration.objects.get_for_object_list(projects) - collabs = collabs.exclude( - _policy__policy_name='PrivateEditorsAreOwners') - ids = [int(c.object_pk) for c in collabs] - projects = projects.filter(id__in=ids) + if projects.count() > 0: + collabs = Collaboration.objects.get_for_object_list(projects) + collabs = collabs.exclude( + _policy__policy_name='PrivateEditorsAreOwners') + ids = [int(c.object_pk) for c in collabs] + projects = projects.filter(id__in=ids) info_ctx = CourseInfoResource().render_one(request, course) @@ -395,20 +395,59 @@ class RequestCourseView(FormView): def form_valid(self, form): form_data = form.cleaned_data - form_data.pop('captcha') + tmpl = loader.get_template('main/course_request_description.txt') + form_data['description'] = tmpl.render(Context(form_data)) - form_data['title'] = 'Mediathread Course Request' - form_data['pid'] = "514" - form_data['mid'] = "3596" - form_data['type'] = 'action item' - form_data['owner'] = 'ellenm' - form_data['assigned_to'] = 'ellenm' - form_data['assigned_to'] = 'ellenm' + task_email = getattr(settings, 'TASK_ASSIGNMENT_DESTINATION', None) + if task_email is not None: + # POST to the contact destination + POST(task_email, params=form_data, async=True) - template = loader.get_template('main/course_request_description.txt') - form_data['description'] = template.render(Context(form_data)) + return super(RequestCourseView, self).form_valid(form) - POST("http://pmt.ccnmtl.columbia.edu/external_add_item.pl", - params=form_data, async=True) - return super(RequestCourseView, self).form_valid(form) +class ContactUsView(FormView): + template_name = 'main/contact.html' + form_class = ContactUsForm + success_url = "/contact/success/" + + def get_initial(self): + """ + Returns the initial data to use for forms on this view. + """ + if not self.request.user.is_anonymous(): + self.initial['name'] = self.request.user.get_full_name() + self.initial['email'] = self.request.user.email + self.initial['username'] = self.request.user.username + + self.initial['issue_date'] = datetime.now() + + if SESSION_KEY in self.request.session: + self.initial['course'] = self.request.session[SESSION_KEY].title + + return super(ContactUsView, self).get_initial() + + def form_valid(self, form): + subject = "Mediathread Contact Us Request" + form_data = form.cleaned_data + tmpl = loader.get_template('main/contact_description.txt') + form_data['description'] = unicode(tmpl.render(Context(form_data))) + + # POST to the task assignment destination + task_email = getattr(settings, 'TASK_ASSIGNMENT_DESTINATION', None) + if task_email is not None: + POST(task_email, params=form_data, async=True) + + # POST to the support email + support_email = getattr(settings, 'SUPPORT_DESTINATION', None) + if support_email is None: + # POST back to the user. Assumes task or support emails are set. + tmpl = loader.get_template('main/contact_email_response.txt') + send_mail(subject, tmpl.render(Context(form_data)), + settings.SERVER_EMAIL, (form_data['email'],)) + else: + sender = form_data['email'] + recipients = (support_email,) + send_mail(subject, form_data['description'], sender, recipients) + + return super(ContactUsView, self).form_valid(form) diff --git a/mediathread/mixins.py b/mediathread/mixins.py index 5ce2eb37f..b72189601 100644 --- a/mediathread/mixins.py +++ b/mediathread/mixins.py @@ -1,5 +1,6 @@ import json +from courseaffils.lib import in_course_or_404 from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.models import User from django.db.models.query_utils import Q @@ -8,10 +9,9 @@ from django.shortcuts import get_object_or_404 from django.utils.decorators import method_decorator -from courseaffils.lib import in_course_or_404 from mediathread.djangosherd.models import SherdNote from mediathread.main.course_details import cached_course_is_faculty, \ - all_selections_are_visible + all_selections_are_visible, all_items_are_visible def ajax_required(func): @@ -73,9 +73,13 @@ def dispatch(self, *args, **kwargs): # If the viewer is faculty, they can view all records self.all_selections_are_visible = \ all_selections_are_visible(self.request.course) + self.all_items_are_visible = \ + all_items_are_visible(self.request.course) self.visible_authors = [] - if not self.all_selections_are_visible and not self.is_viewer_faculty: + if (not self.is_viewer_faculty and + (not self.all_selections_are_visible or + not self.all_items_are_visible)): self.visible_authors = [self.record_viewer.id] # me for user in self.request.course.faculty.all(): self.visible_authors.append(user.id) @@ -91,7 +95,7 @@ def visible_assets_and_notes(self, request, assets): visible_notes = SherdNote.objects.get_related_notes( assets, self.record_owner or None, self.visible_authors, - tag_string, modified, vocabulary) + self.all_items_are_visible, tag_string, modified, vocabulary) search_text = request.GET.get('search_text', '').strip().lower() if len(search_text) > 0: diff --git a/mediathread/projects/api.py b/mediathread/projects/api.py index 5421533e5..e1a53e9c4 100644 --- a/mediathread/projects/api.py +++ b/mediathread/projects/api.py @@ -1,4 +1,4 @@ -#pylint: disable-msg=R0904 +# pylint: disable-msg=R0904 from courseaffils.lib import get_public_name from mediathread.api import UserResource, ClassLevelAuthentication from mediathread.assetmgr.api import AssetResource diff --git a/mediathread/projects/tests/__init__.py b/mediathread/projects/tests/__init__.py index 212c2da2c..d17c133cd 100644 --- a/mediathread/projects/tests/__init__.py +++ b/mediathread/projects/tests/__init__.py @@ -1,4 +1,4 @@ -#pylint: disable-msg=W0401 +# pylint: disable-msg=W0401 # flake8: noqa from mediathread.projects.tests.test_model import * from mediathread.projects.tests.test_api import * diff --git a/mediathread/projects/tests/test_api.py b/mediathread/projects/tests/test_api.py index c9c0dcd5e..c7c27e19c 100644 --- a/mediathread/projects/tests/test_api.py +++ b/mediathread/projects/tests/test_api.py @@ -1,13 +1,14 @@ -#pylint: disable-msg=R0904 -#pylint: disable-msg=E1103 -from tastypie.test import ResourceTestCase +# pylint: disable-msg=R0904 +# pylint: disable-msg=E1103 +import json +from django.test.testcases import TestCase -class ProjectApiTest(ResourceTestCase): - # Use ``fixtures`` & ``urls`` as normal. See Django's ``TestCase`` - # documentation for the gory details. - fixtures = ['unittest_sample_course.json', - 'unittest_sample_projects.json'] +from mediathread.factories import MediathreadTestMixin, UserFactory, \ + AssetFactory, SherdNoteFactory, ProjectFactory + + +class ProjectApiTest(MediathreadTestMixin, TestCase): def get_credentials(self): return None @@ -21,201 +22,280 @@ def assertSelectionsEqual(self, selections, selection_ids): for idx, selection in enumerate(selections): self.assertEquals(int(selection['id']), selection_ids[idx]) + def setUp(self): + self.setup_sample_course() + self.setup_alternate_course() + + # instructor that sees both Sample Course & Alternate Course + self.instructor_three = UserFactory(username='instructor_three') + self.add_as_faculty(self.sample_course, self.instructor_three) + self.add_as_faculty(self.alt_course, self.instructor_three) + + # Sample Course Image Asset + self.asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image') + + self.student_note = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',student_one_selection', + body='student one selection note', range1=0, range2=1) + self.student_ga = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',student_one_item', + body='student one item note', + title=None, range1=None, range2=None) + self.instructor_note = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_selection,', + body='instructor one selection note', range1=0, range2=1) + self.instructor_ga = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_item,', + body='instructor one item note', + title=None, range1=None, range2=None) + + # Sample Course Projects + self.project_private = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='PrivateEditorsAreOwners') + self.add_citation(self.project_private, self.student_note) + self.add_citation(self.project_private, self.student_ga) + + self.project_instructor_shared = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='InstructorShared') + self.add_citation(self.project_instructor_shared, self.student_note) + self.add_citation(self.project_instructor_shared, self.student_ga) + + self.project_class_shared = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='CourseProtected') + self.add_citation(self.project_class_shared, self.student_note) + self.add_citation(self.project_class_shared, self.student_ga) + + self.assignment = ProjectFactory.create( + course=self.sample_course, author=self.instructor_one, + policy='Assignment') + + # Alt Course Projects + self.project_private_alt_course = ProjectFactory.create( + course=self.alt_course, author=self.alt_student, + policy='PrivateEditorsAreOwners') + + self.project_public_alt_course = ProjectFactory.create( + course=self.alt_course, author=self.alt_student, + policy='PrivateEditorsAreOwners') + def test_student_one_getlist(self): - self.assertTrue( - self.api_client.client.login(username="test_student_one", - password="test")) + self.assertTrue(self.client.login(username=self.student_one.username, + password="test")) - response = self.api_client.get('/api/project/', format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) + response = self.client.get('/api/project/', {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') - json = self.deserialize(response) - projects = json['projects'] + the_json = json.loads(response.content) + projects = the_json['projects'] self.assertEquals(len(projects), 4) - self.assertProjectEquals(projects[0], 'Sample Course Assignment', - 'test_instructor_two') + self.assertProjectEquals(projects[0], self.assignment.title, + 'Instructor One') - self.assertProjectEquals(projects[1], 'Public To Class Composition', + self.assertProjectEquals(projects[1], self.project_class_shared.title, 'Student One') - self.assertProjectEquals(projects[2], 'Instructor Shared', + self.assertProjectEquals(projects[2], + self.project_instructor_shared.title, 'Student One') - self.assertProjectEquals(projects[3], 'Private Composition', + self.assertProjectEquals(projects[3], + self.project_private.title, 'Student One') def test_student_two_getlist(self): - self.assertTrue( - self.api_client.client.login(username="test_student_two", - password="test")) + self.assertTrue(self.client.login(username=self.student_two.username, + password="test")) - response = self.api_client.get('/api/project/', format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) + response = self.client.get('/api/project/', {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) - json = self.deserialize(response) - projects = json['projects'] + the_json = json.loads(response.content) + projects = the_json['projects'] self.assertEquals(len(projects), 2) - self.assertProjectEquals(projects[0], 'Sample Course Assignment', - 'test_instructor_two') + self.assertProjectEquals(projects[0], self.assignment.title, + 'Instructor One') - self.assertProjectEquals(projects[1], 'Public To Class Composition', + self.assertProjectEquals(projects[1], self.project_class_shared.title, 'Student One') def test_student_two_getlist_filtered(self): self.assertTrue( - self.api_client.client.login(username="test_student_two", - password="test")) + self.client.login(username=self.student_two.username, + password="test")) - response = self.api_client.get('/api/project/user/test_student_two/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) + url = '/api/project/user/%s/' % self.student_two.username + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) - json = self.deserialize(response) - projects = json['projects'] + the_json = json.loads(response.content) + projects = the_json['projects'] self.assertEquals(len(projects), 0) def test_instructor_getlist(self): self.assertTrue( - self.api_client.client.login(username="test_instructor", - password="test")) + self.client.login(username=self.instructor_one.username, + password="test")) - response = self.api_client.get('/api/project/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) + response = self.client.get('/api/project/', {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) - json = self.deserialize(response) - projects = json['projects'] + the_json = json.loads(response.content) + projects = the_json['projects'] self.assertEquals(len(projects), 3) - self.assertProjectEquals(projects[0], 'Sample Course Assignment', - 'test_instructor_two') + self.assertProjectEquals(projects[0], self.assignment.title, + 'Instructor One') - self.assertProjectEquals(projects[1], 'Public To Class Composition', + self.assertProjectEquals(projects[1], self.project_class_shared.title, 'Student One') - self.assertProjectEquals(projects[2], 'Instructor Shared', + self.assertProjectEquals(projects[2], + self.project_instructor_shared.title, 'Student One') def test_student_one_getobject(self): - self.assertTrue( - self.api_client.client.login(username="test_student_one", - password="test")) + self.assertTrue(self.client.login(username=self.student_one.username, + password="test")) # My own private composition - response = self.api_client.get('/api/project/1/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) + url = '/api/project/%s/' % self.project_private.id + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) - json = self.deserialize(response) + the_json = json.loads(response.content) - self.assertProjectEquals(json['project'], 'Private Composition', + self.assertProjectEquals(the_json['project'], + self.project_private.title, 'Student One') - self.assertSelectionsEqual(json['annotations'], [10, 8]) + self.assertSelectionsEqual(the_json['annotations'], + [self.student_note.id, self.student_ga.id]) + + def test_student_one_getobject_altcourse(self): + self.assertTrue(self.client.login(username=self.student_one.username, + password="test")) # Student three composition in alt course - response = self.api_client.get('/api/project/4/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/%s/' % self.project_private_alt_course.id + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 403) def test_student_two_getobject(self): self.assertTrue( - self.api_client.client.login(username="test_student_two", - password="test")) + self.client.login(username=self.student_two.username, + password="test")) # Student one private composition - response = self.api_client.get('/api/project/1/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/%s/' % self.project_private.id + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 403) # Student one instructor shared composition - response = self.api_client.get('/api/project/2/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/%s/' % self.project_instructor_shared.id + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 403) # Student one public to class composition - response = self.api_client.get('/api/project/3/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) - json = self.deserialize(response) - self.assertProjectEquals(json['project'], - 'Public To Class Composition', + url = '/api/project/%s/' % self.project_class_shared.id + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) + the_json = json.loads(response.content) + self.assertProjectEquals(the_json['project'], + self.project_class_shared.title, 'Student One') - self.assertSelectionsEqual(json['annotations'], [2, 5, 7]) + self.assertSelectionsEqual(the_json['annotations'], + [self.student_note.id, self.student_ga.id]) # Student three composition in alt course - response = self.api_client.get('/api/project/4/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/%s/' % self.project_public_alt_course.id + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 403) def test_instructor_getobject(self): self.assertTrue( - self.api_client.client.login(username="test_instructor", - password="test")) + self.client.login(username=self.instructor_one.username, + password="test")) # Student one private composition - response = self.api_client.get('/api/project/1/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/%s/' % self.project_private.id + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 403) # Student one instructor shared composition - response = self.api_client.get('/api/project/2/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) - json = self.deserialize(response) - self.assertProjectEquals(json['project'], 'Instructor Shared', + url = '/api/project/%s/' % self.project_instructor_shared.id + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) + the_json = json.loads(response.content) + self.assertProjectEquals(the_json['project'], + self.project_instructor_shared.title, 'Student One') - self.assertEquals(len(json['annotations']), 0) + self.assertEquals(len(the_json['annotations']), 2) + self.assertSelectionsEqual(the_json['annotations'], + [self.student_note.id, self.student_ga.id]) # Student one public to class composition - response = self.api_client.get('/api/project/3/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertValidJSONResponse(response) - json = self.deserialize(response) - self.assertProjectEquals(json['project'], - 'Public To Class Composition', 'Student One') - self.assertSelectionsEqual(json['annotations'], [2, 5, 7]) + url = '/api/project/%s/' % self.project_class_shared.id + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 200) + the_json = json.loads(response.content) + self.assertProjectEquals(the_json['project'], + self.project_class_shared.title, + 'Student One') + self.assertSelectionsEqual(the_json['annotations'], + [self.student_note.id, self.student_ga.id]) # Student three composition in alt course - response = self.api_client.get('/api/project/4/', - format='json', - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/%s/' % self.project_public_alt_course.id + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEqual(response.status_code, 403) def test_post_list(self): self.assertTrue( - self.api_client.client.login(username="test_instructor", - password="test")) + self.client.login(username=self.instructor_one.username, + password="test")) - self.assertHttpMethodNotAllowed(self.api_client.post( - '/api/project/', format='json', data={})) + response = self.client.post('/api/project/', {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 405) def test_put_detail(self): self.assertTrue( - self.api_client.client.login(username="test_instructor", - password="test")) + self.client.login(username=self.instructor_one.username, + password="test")) - self.assertHttpMethodNotAllowed(self.api_client.put( - '/api/project/2/', format='json', data={})) + url = '/api/project/%s/' % self.project_class_shared.id + response = self.client.put(url, data={}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + + self.assertEquals(response.status_code, 405) def test_delete(self): self.assertTrue( - self.api_client.client.login(username="test_instructor", - password="test")) - - self.assertHttpMethodNotAllowed(self.api_client.delete( - '/api/project/2/', format='json')) + self.client.login(username=self.student_one.username, + password="test")) + url = '/api/project/%s/' % self.project_class_shared.id + response = self.client.delete(url, data={}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertEquals(response.status_code, 405) diff --git a/mediathread/projects/tests/test_homepage.py b/mediathread/projects/tests/test_homepage.py index 88bcacc0b..2664f5564 100644 --- a/mediathread/projects/tests/test_homepage.py +++ b/mediathread/projects/tests/test_homepage.py @@ -1,13 +1,78 @@ -#pylint: disable-msg=R0904 -#pylint: disable-msg=E1103 -from django.test import TestCase -from django.test.client import Client +# pylint: disable-msg=R0904 +# pylint: disable-msg=E1103 import json +from django.test import TestCase -class HomepageTest(TestCase): - fixtures = ['unittest_sample_course.json', - 'unittest_sample_projects.json'] +from mediathread.factories import MediathreadTestMixin, UserFactory, \ + AssetFactory, SherdNoteFactory, ProjectFactory + + +class HomepageTest(MediathreadTestMixin, TestCase): + + def setUp(self): + self.setup_sample_course() + self.setup_alternate_course() + + # instructor that sees both Sample Course & Alternate Course + self.instructor_three = UserFactory(username='instructor_three') + self.add_as_faculty(self.sample_course, self.instructor_three) + self.add_as_faculty(self.alt_course, self.instructor_three) + + # Sample Course Image Asset + self.asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image') + + self.student_note = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',student_one_selection', + body='student one selection note', range1=0, range2=1) + self.student_ga = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',student_one_item', + body='student one item note', + title=None, range1=None, range2=None) + self.instructor_note = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_selection,', + body='instructor one selection note', range1=0, range2=1) + self.instructor_ga = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_item,', + body='instructor one item note', + title=None, range1=None, range2=None) + + # Sample Course Projects + self.project_private = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='PrivateEditorsAreOwners') + self.add_citation(self.project_private, self.student_note) + self.add_citation(self.project_private, self.student_ga) + + self.project_instructor_shared = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='InstructorShared') + self.add_citation(self.project_instructor_shared, self.student_note) + self.add_citation(self.project_instructor_shared, self.student_ga) + + self.project_class_shared = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='CourseProtected') + self.add_citation(self.project_class_shared, self.student_note) + self.add_citation(self.project_class_shared, self.student_ga) + + self.assignment = ProjectFactory.create( + course=self.sample_course, author=self.instructor_one, + policy='Assignment') + + # Alt Course Projects + self.project_private_alt_course = ProjectFactory.create( + course=self.alt_course, author=self.alt_student, + policy='PrivateEditorsAreOwners') + + self.project_public_alt_course = ProjectFactory.create( + course=self.alt_course, author=self.alt_student, + policy='PrivateEditorsAreOwners') def assertProjectEquals(self, project, title, author, editable): self.assertEquals(project['title'], title) @@ -15,50 +80,42 @@ def assertProjectEquals(self, project, title, author, editable): self.assertEquals(project['editable'], editable) def test_get_my_projectlist_as_student(self): - client = Client() - - self.assertTrue( - client.login(username='test_student_one', password='test')) + self.assertTrue(self.client.login(username=self.student_one.username, + password='test')) - response = client.get('/api/project/user/test_student_one/', - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/user/%s/' % self.student_one.username + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') the_json = json.loads(response.content) assignments = the_json['assignments'] self.assertEquals(len(assignments), 1) - self.assertEquals(assignments[0]['title'], "Sample Course Assignment") + self.assertEquals(assignments[0]['title'], self.assignment.title) self.assertTrue(assignments[0]['display_as_assignment']) self.assertFalse(assignments[0]['is_faculty']) projects = the_json['projects'] self.assertEquals(len(projects), 3) - self.assertProjectEquals(projects[0], - 'Public To Class Composition', - 'Student One', - True) + self.assertProjectEquals(projects[0], self.project_class_shared.title, + 'Student One', True) self.assertProjectEquals(projects[1], - 'Instructor Shared', - 'Student One', - True) + self.project_instructor_shared.title, + 'Student One', True) - self.assertProjectEquals(projects[2], - 'Private Composition', - 'Student One', - True) + self.assertProjectEquals(projects[2], self.project_private.title, + 'Student One', True) def test_get_my_instructor_projectlist(self): - client = Client() + self.assertTrue(self.client.login(username=self.student_one.username, + password='test')) - self.assertTrue( - client.login(username='test_student_one', password='test')) + url = '/api/project/user/%s/' % self.instructor_one.username - response = client.get('/api/project/user/test_instructor_two/', - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') the_json = json.loads(response.content) @@ -67,20 +124,16 @@ def test_get_my_instructor_projectlist(self): projects = the_json['projects'] self.assertEquals(len(projects), 1) - self.assertProjectEquals(projects[0], - 'Sample Course Assignment', - 'test_instructor_two', - False) + self.assertProjectEquals(projects[0], self.assignment.title, + 'Instructor One', False) def test_get_my_peer_projectlist(self): - client = Client() + self.assertTrue(self.client.login(username=self.student_two.username, + password='test')) - self.assertTrue( - client.login(username='test_student_two', password='test')) - - response = client.get('/api/project/user/test_student_one/', - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/user/%s/' % self.student_one.username + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') the_json = json.loads(response.content) assignments = the_json['assignments'] @@ -89,23 +142,17 @@ def test_get_my_peer_projectlist(self): projects = the_json['projects'] self.assertEquals(len(projects), 1) - self.assertProjectEquals(projects[0], - 'Public To Class Composition', - 'Student One', - False) + self.assertProjectEquals(projects[0], self.project_class_shared.title, + 'Student One', False) def test_get_my_projectlist_as_instructor(self): - client = Client() - self.assertTrue( - client.login(username='test_instructor_two', password='test')) - - response = client.get('/?set_course=Sample_Course_Students') - self.assertEquals(response.status_code, 200) + self.client.login(username=self.instructor_one.username, + password='test')) - response = client.get('/api/project/user/test_instructor_two/', - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/user/%s/' % self.instructor_one.username + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') the_json = json.loads(response.content) @@ -114,40 +161,36 @@ def test_get_my_projectlist_as_instructor(self): projects = the_json['projects'] self.assertEquals(len(projects), 1) - self.assertProjectEquals(projects[0], - 'Sample Course Assignment', - 'test_instructor_two', - True) + self.assertProjectEquals(projects[0], self.assignment.title, + 'Instructor One', True) def test_instructor_projectlist_as_instructor_two(self): - client = Client() - self.assertTrue( - client.login(username='test_instructor_two', password='test')) - - response = client.get('/?set_course=Sample_Course_Students') - self.assertEquals(response.status_code, 200) + self.client.login(username=self.instructor_two.username, + password='test')) - response = client.get('/api/project/user/test_instructor/', - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/user/%s/' % self.instructor_one.username + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') the_json = json.loads(response.content) self.assertEquals(len(the_json['assignments']), 0) - # The assignment is viewable here. - self.assertEquals(len(the_json['projects']), 0) + # The assignment is viewable here. + self.assertEquals(len(the_json['projects']), 1) + self.assertProjectEquals(the_json['projects'][0], + self.assignment.title, + 'Instructor One', False) def test_get_student_projectlist_as_instructor(self): - client = Client() - self.assertTrue( - client.login(username='test_instructor', password='test')) + self.client.login(username=self.instructor_one.username, + password='test')) - response = client.get('/api/project/user/test_student_one/', - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/user/%s/' % self.student_one.username + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') the_json = json.loads(response.content) self.assertEquals(len(the_json['assignments']), 0) @@ -155,41 +198,35 @@ def test_get_student_projectlist_as_instructor(self): projects = the_json['projects'] self.assertEquals(len(projects), 2) - self.assertProjectEquals(projects[0], - 'Public To Class Composition', - 'Student One', - False) + self.assertProjectEquals(projects[0], self.project_class_shared.title, + 'Student One', False) self.assertProjectEquals(projects[1], - 'Instructor Shared', - 'Student One', - False) + self.project_instructor_shared.title, + 'Student One', False) def test_request_nonclassmember_projectlist(self): - client = Client() - self.assertTrue( - client.login(username='test_student_one', password='test')) + self.client.login(username=self.student_one.username, + password='test')) - response = client.get('/api/project/user/test_student_alt/', - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/user/%s/' % self.alt_student.username + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 404) - response = client.get('/api/project/user/test_instructor_alt/', - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + url = '/api/project/user/%s/' % self.alt_instructor.username + response = self.client.get(url, {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 404) def test_get_all_projects_as_student(self): - client = Client() - self.assertTrue( - client.login(username='test_student_one', password='test')) + self.client.login(username=self.student_one.username, + password='test')) - response = client.get('/api/project/', - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = self.client.get('/api/project/', {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') the_json = json.loads(response.content) self.assertTrue('assignments' not in the_json) @@ -197,74 +234,56 @@ def test_get_all_projects_as_student(self): projects = the_json['projects'] self.assertEquals(len(projects), 4) - self.assertProjectEquals(projects[0], - 'Sample Course Assignment', - 'test_instructor_two', - False) + self.assertProjectEquals(projects[0], self.assignment.title, + 'Instructor One', False) - self.assertProjectEquals(projects[1], - 'Public To Class Composition', - 'Student One', - False) + self.assertProjectEquals(projects[1], self.project_class_shared.title, + 'Student One', False) self.assertProjectEquals(projects[2], - 'Instructor Shared', - 'Student One', - False) + self.project_instructor_shared.title, + 'Student One', False) - self.assertProjectEquals(projects[3], - 'Private Composition', - 'Student One', - False) + self.assertProjectEquals(projects[3], self.project_private.title, + 'Student One', False) def test_get_all_projects_as_peer(self): - client = Client() - self.assertTrue( - client.login(username='test_student_two', password='test')) + self.client.login(username=self.student_two.username, + password='test')) - response = client.get('/api/project/', - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = self.client.get('/api/project/', {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') the_json = json.loads(response.content) self.assertTrue('assignments' not in the_json) projects = the_json['projects'] self.assertEquals(len(projects), 2) - self.assertProjectEquals(projects[0], - 'Sample Course Assignment', - 'test_instructor_two', - False) - self.assertProjectEquals(projects[1], - 'Public To Class Composition', - 'Student One', - False) + self.assertProjectEquals(projects[0], self.assignment.title, + 'Instructor One', False) - def test_get_all_projects_as_instructor(self): - client = Client() + self.assertProjectEquals(projects[1], self.project_class_shared.title, + 'Student One', False) + def test_get_all_projects_as_instructor(self): self.assertTrue( - client.login(username='test_instructor', password='test')) + self.client.login(username=self.instructor_one.username, + password='test')) - response = client.get('/api/project/', - {}, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') + response = self.client.get('/api/project/', {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') the_json = json.loads(response.content) self.assertTrue('assignments' not in the_json) projects = the_json['projects'] - self.assertEquals(len(projects), 3) - self.assertProjectEquals(projects[0], - 'Sample Course Assignment', - 'test_instructor_two', - False) - self.assertProjectEquals(projects[1], - 'Public To Class Composition', - 'Student One', - False) + self.assertProjectEquals(projects[0], self.assignment.title, + 'Instructor One', False) + + self.assertProjectEquals(projects[1], self.project_class_shared.title, + 'Student One', False) + self.assertProjectEquals(projects[2], - 'Instructor Shared', - 'Student One', - False) + self.project_instructor_shared.title, + 'Student One', False) diff --git a/mediathread/projects/tests/test_model.py b/mediathread/projects/tests/test_model.py index 9c241ea02..f5792d917 100644 --- a/mediathread/projects/tests/test_model.py +++ b/mediathread/projects/tests/test_model.py @@ -1,266 +1,232 @@ -#pylint: disable-msg=R0904 -from courseaffils.models import Course +# pylint: disable-msg=R0904 from datetime import datetime -from django.contrib.auth.models import User + +from courseaffils.models import Course from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError from django.http import HttpRequest from django.test import TestCase -from mediathread.assetmgr.models import Asset +from django.test.client import RequestFactory + from mediathread.djangosherd.models import SherdNote +from mediathread.factories import MediathreadTestMixin, \ + AssetFactory, SherdNoteFactory, ProjectFactory from mediathread.projects.models import Project from structuredcollaboration.models import Collaboration -class ProjectTest(TestCase): - fixtures = ['unittest_sample_course.json', - 'unittest_sample_projects.json'] +class ProjectTest(MediathreadTestMixin, TestCase): + + def setUp(self): + self.setup_sample_course() + self.setup_alternate_course() + + # Sample Course Image Asset + self.asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image') + + self.student_note = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',student_one_selection', + body='student one selection note', range1=0, range2=1) + self.student_ga = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',student_one_item', + body='student one item note', + title=None, range1=None, range2=None) + self.instructor_note = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_selection,', + body='instructor one selection note', range1=0, range2=1) + self.instructor_ga = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_item,', + body='instructor one item note', + title=None, range1=None, range2=None) + + # Sample Course Projects + self.project_private = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='PrivateEditorsAreOwners') + + self.project_instructor_shared = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='InstructorShared') + + self.project_class_shared = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='CourseProtected') + + self.assignment = ProjectFactory.create( + course=self.sample_course, author=self.instructor_one, + policy='Assignment') + self.add_citation(self.assignment, self.student_note) + self.add_citation(self.assignment, self.instructor_note) + self.add_citation(self.assignment, self.student_ga) + self.add_citation(self.assignment, self.instructor_ga) def test_description(self): - project = Project.objects.get(title='Private Composition') + + project = Project.objects.get(id=self.project_private.id) self.assertEquals(project.description(), 'Composition') - self.assertEquals(project.visibility_short(), 'Private') - project = Project.objects.get(title="Public To Class Composition") + self.assertEquals(project.visibility_short(), 'Private') + project = Project.objects.get(id=self.project_class_shared.id) self.assertEquals(project.visibility_short(), 'Published to Class') - project = Project.objects.get(title="Instructor Shared") + project = Project.objects.get(id=self.project_instructor_shared.id) self.assertEquals(project.visibility_short(), 'Submitted to Instructor') - assignment = Project.objects.get(title='Sample Course Assignment') + assignment = Project.objects.get(id=self.assignment.id) self.assertEquals(assignment.description(), 'Assignment') self.assertEquals(assignment.visibility_short(), 'Assignment') - def test_verify_testdata(self): - course = Course.objects.get(id=1) - self.assertEquals(course.title, "Sample Course") + def test_migrate_one(self): + new_project = Project.objects.migrate_one(self.project_private, + self.alt_course, + self.alt_instructor) - alt_course = Course.objects.get(id=2) - self.assertEquals(alt_course.title, "Alternate Course") + self.assertEquals(new_project.title, self.project_private.title) + self.assertEquals(new_project.author, self.alt_instructor) + self.assertEquals(new_project.course, self.alt_course) + self.assertEquals(new_project.visibility_short(), "Private") - def test_migrate_one(self): - alt_course = Course.objects.get(id=2) - self.assertEquals(alt_course.title, "Alternate Course") + new_project = Project.objects.migrate_one(self.assignment, + self.alt_course, + self.alt_instructor) - alt_instructor = User.objects.get(username='test_instructor_alt') + self.assertEquals(new_project.title, self.assignment.title) + self.assertEquals(new_project.author, self.alt_instructor) + self.assertEquals(new_project.course, self.alt_course) + self.assertEquals(new_project.visibility_short(), "Assignment") - private_essay = Project.objects.get(id=1) - new_project = Project.objects.migrate_one(private_essay, - alt_course, - alt_instructor) + def test_migrate_projects_to_alt_course(self): + self.assertEquals(len(self.alt_course.asset_set.all()), 0) + self.assertEquals(len(self.alt_course.project_set.all()), 0) - self.assertEquals(new_project.title, "Private Composition") - self.assertEquals(new_project.author, alt_instructor) - self.assertEquals(new_project.course, alt_course) - self.assertEquals(new_project.visibility_short(), "Private") + projects = Project.objects.filter(id=self.assignment.id) - assignment = Project.objects.get(id=5) - new_project = Project.objects.migrate_one(assignment, - alt_course, - alt_instructor) + object_map = {'assets': {}, 'notes': {}, 'projects': {}} + object_map = Project.objects.migrate(projects, self.alt_course, + self.alt_instructor, + object_map, + True, True) - self.assertEquals(new_project.title, "Sample Course Assignment") - self.assertEquals(new_project.author, alt_instructor) - self.assertEquals(new_project.course, alt_course) - self.assertEquals(new_project.visibility_short(), "Assignment") + assets = self.alt_course.asset_set + self.assertEquals(assets.count(), 1) + asset = assets.all()[0] + self.assertEquals(asset.title, self.asset1.title) - def test_migrate_set(self): - self.assertTrue(True) + self.assertEquals(asset.sherdnote_set.count(), 3) - course = Course.objects.get(id=2) - self.assertEquals(course.title, "Alternate Course") - self.assertEquals(len(course.asset_set.all()), 1) - self.assertEquals(len(course.project_set.all()), 2) + ga = asset.global_annotation(self.alt_instructor, auto_create=False) + self.assertIsNotNone(asset.global_annotation(self.alt_instructor, + auto_create=False)) + self.assertEquals(ga.tags, + ',student_one_item,image, instructor_one_item,') + self.assertEquals(ga.body, + 'student one item noteinstructor one item note') - user = User.objects.get(username='test_instructor_two') + asset.sherdnote_set.get(title=self.student_note.title) + asset.sherdnote_set.get(title=self.instructor_note.title) - projects = Project.objects.filter(title="Sample Course Assignment") + self.assertEquals(self.alt_course.project_set.count(), 1) + project = self.alt_course.project_set.all()[0] + self.assertEquals(project.title, self.assignment.title) + self.assertEquals(project.visibility_short(), 'Assignment') + self.assertEquals(project.author, self.alt_instructor) - object_map = {'assets': {}, 'notes': {}, 'projects': {}} - object_map = Project.objects.migrate(projects, course, - user, object_map, - True, True) + citations = SherdNote.objects.references_in_string(project.body, + self.alt_instructor) + self.assertEquals(len(citations), 4) - self.assertEquals(len(course.asset_set.all()), 4) - asset = Asset.objects.get(title="Mediathread: Introduction", - course=course) - self.assertEquals(len(asset.sherdnote_set.all()), 2) - self.assertIsNotNone(asset.global_annotation(user, auto_create=False)) - asset.sherdnote_set.get(title="Video Selection Is Time-based") - - asset = Asset.objects.get(title="MAAP Award Reception", - course=course) - self.assertEquals(len(asset.sherdnote_set.all()), 2) - self.assertIsNotNone(asset.global_annotation(user, auto_create=False)) - asset.sherdnote_set.get(title="Nice Tie") - - asset = Asset.objects.get(title="Project Portfolio", - course=course) - self.assertEquals(len(asset.sherdnote_set.all()), 1) - - self.assertEquals(len(course.project_set.all()), 3) - assignment = Project.objects.get(title="Sample Course Assignment", - course=course) - self.assertEquals(assignment.visibility_short(), 'Assignment') - self.assertEquals(assignment.author, user) - - citations = SherdNote.objects.references_in_string(assignment.body, - user) - self.assertEquals(len(citations), 5) - - # Student annotation - self.assertEquals(citations[0].title, "Nice Tie") - self.assertEquals(citations[0].range1, 0.0) - self.assertEquals(citations[0].range2, 0.0) - annotation_data = ('{"geometry":{"type":"Polygon",' - '"coordinates":[[[38.5,-91],[38.5,2.5],[61.5,' - '2.500000000000007],[61.5,-91],[38.5,-91]]]},' - '"default":false,"x":0,"y":0,"zoom":2,' - '"extent":[-120,-90,120,90]}') - self.assertEquals(citations[0].annotation_data, annotation_data) - self.assertEquals(citations[0].tags, ',student_two_selection') - self.assertEquals(citations[0].body, 'student two selection note') - self.assertEquals(citations[0].author, user) - - # Migrated global annotation - self.assertTrue(citations[1].asset.title, - "Mediathread: Introduction") - self.assertEquals(citations[1].id, - citations[1].asset.global_annotation(user, False).id) - self.assertEquals( - citations[1].tags, - u',youtube, test_instructor_item,test_instructor_two') - self.assertEquals( - citations[1].body, - u'All credit to Mark and Caseytest_instructor_two notes') - - # Own selection - self.assertEquals(citations[2].title, "Video Selection Is Time-based") - self.assertEquals(citations[2].range1, 60.0) - self.assertEquals(citations[2].range2, 120.0) - annotation_data = ('{"startCode":"00:01:00","endCode":"00:02:00",' - '"duration":171,"timeScale":1,"start":60,' - '"end":120}') - self.assertEquals(citations[2].annotation_data, annotation_data) - self.assertEquals(citations[2].tags, ',test_instructor_two') - self.assertEquals(citations[2].author, user) - - # Own asset - self.assertTrue(citations[3].asset.title, "Project Portfolio") - self.assertTrue(citations[3].is_global_annotation()) - self.assertEquals(citations[3].id, - citations[3].asset.global_annotation(user, False).id) - - # Own global annotation - self.assertTrue(citations[4].asset.title, - "Mediathread: Introduction") - self.assertEquals(citations[4].id, - citations[4].asset.global_annotation(user, False).id) + self.assertEquals(citations[0].title, self.student_note.title) + self.assertEquals(citations[0].author, self.alt_instructor) - def test_visible_by_course(self): - student_one = User.objects.get(username='test_student_one') - student_two = User.objects.get(username='test_student_two') - instructor = User.objects.get(username='test_instructor') + self.assertEquals(citations[1].title, self.instructor_note.title) + self.assertEquals(citations[1].author, self.alt_instructor) - sample_course = Course.objects.get(title="Sample Course") + self.assertEquals(citations[2].id, ga.id) + self.assertEquals(citations[3].id, ga.id) + def test_visible_by_course(self): request = HttpRequest() - request.course = sample_course + request.course = self.sample_course request.collaboration_context, created = \ Collaboration.objects.get_or_create( content_type=ContentType.objects.get_for_model(Course), - object_pk=str(sample_course.pk)) + object_pk=str(self.sample_course.pk)) - request.user = student_one - visible_projects = Project.objects.visible_by_course(request, - sample_course) + request.user = self.student_one + visible_projects = Project.objects.visible_by_course( + request, self.sample_course) self.assertEquals(len(visible_projects), 4) - self.assertEquals(visible_projects[0].__unicode__(), - "Sample Course Assignment <5> " - "by test_instructor_two") - self.assertEquals(visible_projects[1].__unicode__(), - "Public To Class Composition <3> by Student One") - self.assertEquals(visible_projects[2].__unicode__(), - "Instructor Shared <2> by Student One") - self.assertEquals(visible_projects[3].__unicode__(), - "Private Composition <1> by Student One") - - request.user = student_two - visible_projects = Project.objects.visible_by_course(request, - sample_course) + self.assertEquals(visible_projects[0], self.assignment) + self.assertEquals(visible_projects[1], self.project_class_shared) + self.assertEquals(visible_projects[2], self.project_instructor_shared) + self.assertEquals(visible_projects[3], self.project_private) + + request.user = self.student_two + visible_projects = Project.objects.visible_by_course( + request, self.sample_course) + self.assertEquals(len(visible_projects), 2) - self.assertEquals(visible_projects[0].__unicode__(), - "Sample Course Assignment <5> " - "by test_instructor_two") - self.assertEquals(visible_projects[1].__unicode__(), - "Public To Class Composition <3> by Student One") - - request.user = instructor - visible_projects = Project.objects.visible_by_course(request, - sample_course) + self.assertEquals(visible_projects[0], self.assignment) + self.assertEquals(visible_projects[1], self.project_class_shared) + + request.user = self.instructor_one + visible_projects = Project.objects.visible_by_course( + request, self.sample_course) self.assertEquals(len(visible_projects), 3) - self.assertEquals(visible_projects[0].__unicode__(), - "Sample Course Assignment <5> " - "by test_instructor_two") - self.assertEquals(visible_projects[1].__unicode__(), - "Public To Class Composition <3> by Student One") - self.assertEquals(visible_projects[2].__unicode__(), - "Instructor Shared <2> by Student One") + self.assertEquals(visible_projects[0], self.assignment) + self.assertEquals(visible_projects[1], self.project_class_shared) + self.assertEquals(visible_projects[2], self.project_instructor_shared) def test_visible_by_course_and_user(self): - student_one = User.objects.get(username='test_student_one') - student_two = User.objects.get(username='test_student_two') - instructor = User.objects.get(username='test_instructor_two') + # student_one = User.objects.get(username='test_student_one') + # student_two = User.objects.get(username='test_student_two') + # instructor = User.objects.get(username='test_instructor_two') - sample_course = Course.objects.get(title="Sample Course") + # sample_course = Course.objects.get(title="Sample Course") request = HttpRequest() - request.course = sample_course + request.course = self.sample_course request.collaboration_context, created = \ Collaboration.objects.get_or_create( content_type=ContentType.objects.get_for_model(Course), - object_pk=str(sample_course.pk)) + object_pk=str(self.sample_course.pk)) - request.user = student_one + request.user = self.student_one visible_projects = Project.objects.visible_by_course_and_user( - request, sample_course, student_one, False) + request, self.sample_course, self.student_one, False) self.assertEquals(len(visible_projects), 3) - self.assertEquals(visible_projects[0].__unicode__(), - "Public To Class Composition <3> by Student One") - self.assertEquals(visible_projects[1].__unicode__(), - "Instructor Shared <2> by Student One") - self.assertEquals(visible_projects[2].__unicode__(), - "Private Composition <1> by Student One") + self.assertEquals(visible_projects[0], self.project_class_shared) + self.assertEquals(visible_projects[1], self.project_instructor_shared) + self.assertEquals(visible_projects[2], self.project_private) visible_projects = Project.objects.visible_by_course_and_user( - request, sample_course, instructor, True) + request, self.sample_course, self.instructor_one, True) self.assertEquals(len(visible_projects), 1) - self.assertEquals(visible_projects[0].__unicode__(), - "Sample Course Assignment <5> " - "by test_instructor_two") + self.assertEquals(visible_projects[0], self.assignment) - request.user = student_two + request.user = self.student_two visible_projects = Project.objects.visible_by_course_and_user( - request, sample_course, student_one, False) + request, self.sample_course, self.student_one, False) self.assertEquals(len(visible_projects), 1) - self.assertEquals(visible_projects[0].__unicode__(), - "Public To Class Composition <3> by Student One") + self.assertEquals(visible_projects[0], self.project_class_shared) - request.user = instructor + request.user = self.instructor_one visible_projects = Project.objects.visible_by_course_and_user( - request, sample_course, student_one, False) + request, self.sample_course, self.student_one, False) self.assertEquals(len(visible_projects), 2) - self.assertEquals(visible_projects[0].__unicode__(), - "Public To Class Composition <3> by Student One") - self.assertEquals(visible_projects[1].__unicode__(), - "Instructor Shared <2> by Student One") + self.assertEquals(visible_projects[0], self.project_class_shared) + self.assertEquals(visible_projects[1], self.project_instructor_shared) - def test_project_clean(self): - assignment = Project.objects.get(id=5) + def test_project_clean_date_field(self): try: - assignment.due_date = datetime(2012, 3, 13, 0, 0) - assignment.clean() + self.assignment.due_date = datetime(2012, 3, 13, 0, 0) + self.assignment.clean() self.assertTrue(False, 'Due date is in the past') except ValidationError as err: self.assertTrue( @@ -269,40 +235,60 @@ def test_project_clean(self): try: today = datetime.today() this_day = datetime(today.year, today.month, today.day, 0, 0) - assignment.due_date = this_day - assignment.clean() + self.assignment.due_date = this_day + self.assignment.clean() except ValidationError as err: self.assertTrue(False, "Due date is today. That's okay.") try: - assignment.due_date = datetime(2020, 1, 1, 0, 0) - assignment.clean() + self.assignment.due_date = datetime(2020, 1, 1, 0, 0) + self.assignment.clean() except ValidationError as err: self.assertTrue(False, "Due date is in the future, that's ok") def test_faculty_compositions(self): - student = User.objects.get(username='test_student_three') - sample_course = Course.objects.get(title="Sample Course") - alt_course = Course.objects.get(title="Alternate Course") + # student = User.objects.get(username='test_student_three') + # sample_course = Course.objects.get(title="Sample Course") + # alt_course = Course.objects.get(title="Alternate Course") request = HttpRequest() - request.user = student - request.course = sample_course + request.user = self.student_one + request.course = self.sample_course request.collaboration_context, created = \ Collaboration.objects.get_or_create( content_type=ContentType.objects.get_for_model(Course), - object_pk=str(sample_course.pk)) + object_pk=str(self.sample_course.pk)) compositions = Project.objects.faculty_compositions( - request, sample_course) + request, self.sample_course) self.assertEquals(len(compositions), 0) - request.course = alt_course + # instructor composition + ProjectFactory.create( + course=self.sample_course, author=self.instructor_one, + policy='CourseProtected') + + compositions = Project.objects.faculty_compositions( + request, self.sample_course) + self.assertEquals(len(compositions), 1) + + def test_responses(self): + response1 = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='InstructorShared', parent=self.assignment) + ProjectFactory.create( + course=self.sample_course, author=self.student_two, + policy='InstructorShared', parent=self.assignment) + + request = RequestFactory().get('/', {}) + request.user = self.instructor_one + request.course = self.sample_course request.collaboration_context, created = \ Collaboration.objects.get_or_create( content_type=ContentType.objects.get_for_model(Course), - object_pk=str(alt_course.pk)) + object_pk=str(self.sample_course.pk)) - compositions = Project.objects.faculty_compositions( - request, alt_course) - self.assertEquals(len(compositions), 1) + self.assertEquals(len(self.assignment.responses(request)), 2) + + responses = self.assignment.responses_by(request, self.student_one) + self.assertEquals(responses[0], response1) diff --git a/mediathread/projects/tests/test_templatetags.py b/mediathread/projects/tests/test_templatetags.py index 0e932ddbb..907e75cae 100644 --- a/mediathread/projects/tests/test_templatetags.py +++ b/mediathread/projects/tests/test_templatetags.py @@ -1,17 +1,26 @@ -from django.contrib.auth.models import User from django.template.base import Template from django.template.context import Context from django.test.testcases import TestCase +from mediathread.factories import UserFactory, MediathreadTestMixin -class TestTemplateTags(TestCase): - fixtures = ['unittest_sample_course.json'] + +class TestTemplateTags(MediathreadTestMixin, TestCase): + + def setUp(self): + self.setup_sample_course() + self.setup_alternate_course() def test_user_courses(self): - user = User.objects.get(username='test_instructor_two') + + # instructor that sees both Sample Course & Alternate Course + self.instructor_three = UserFactory(username='instructor_three') + self.add_as_faculty(self.sample_course, self.instructor_three) + self.add_as_faculty(self.alt_course, self.instructor_three) + out = Template( "{% load user_projects %}" "{% num_courses for user as user_courses %}" "{{user_courses}}" - ).render(Context({'user': user})) + ).render(Context({'user': self.instructor_three})) self.assertEqual(out, "2") diff --git a/mediathread/projects/tests/test_views.py b/mediathread/projects/tests/test_views.py index 4ca932f6d..3b8038f0d 100644 --- a/mediathread/projects/tests/test_views.py +++ b/mediathread/projects/tests/test_views.py @@ -1,62 +1,125 @@ -#pylint: disable-msg=R0904 -from django.contrib.auth.models import User +# pylint: disable-msg=R0904 +import json + from django.test import TestCase + +from mediathread.factories import MediathreadTestMixin, UserFactory, \ + AssetFactory, SherdNoteFactory, ProjectFactory from mediathread.projects.models import Project -import json -class ProjectViewTest(TestCase): - fixtures = ['unittest_sample_course.json', - 'unittest_sample_projects.json'] +class ProjectViewTest(MediathreadTestMixin, TestCase): + def setUp(self): + self.setup_sample_course() + self.setup_alternate_course() + + # instructor that sees both Sample Course & Alternate Course + self.instructor_three = UserFactory(username='instructor_three') + self.add_as_faculty(self.sample_course, self.instructor_three) + self.add_as_faculty(self.alt_course, self.instructor_three) + + # Sample Course Image Asset + self.asset1 = AssetFactory.create(course=self.sample_course, + primary_source='image') + + self.student_note = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',student_one_selection', + body='student one selection note', range1=0, range2=1) + self.student_ga = SherdNoteFactory( + asset=self.asset1, author=self.student_one, + tags=',student_one_item', + body='student one item note', + title=None, range1=None, range2=None) + self.instructor_note = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_selection,', + body='instructor one selection note', range1=0, range2=1) + self.instructor_ga = SherdNoteFactory( + asset=self.asset1, author=self.instructor_one, + tags=',image, instructor_one_item,', + body='instructor one item note', + title=None, range1=None, range2=None) + + # Sample Course Projects + self.project_private = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='PrivateEditorsAreOwners') + self.add_citation(self.project_private, self.student_note) + self.add_citation(self.project_private, self.student_ga) + + self.project_instructor_shared = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='InstructorShared') + self.add_citation(self.project_instructor_shared, self.student_note) + self.add_citation(self.project_instructor_shared, self.student_ga) + + self.project_class_shared = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='CourseProtected') + self.add_citation(self.project_class_shared, self.student_note) + self.add_citation(self.project_class_shared, self.student_ga) + + self.assignment = ProjectFactory.create( + course=self.sample_course, author=self.instructor_one, + policy='Assignment') + + # Alt Course Projects + self.project_private_alt_course = ProjectFactory.create( + course=self.alt_course, author=self.alt_student, + policy='PrivateEditorsAreOwners') + + self.project_public_alt_course = ProjectFactory.create( + course=self.alt_course, author=self.alt_student, + policy='PrivateEditorsAreOwners') def test_project_save_doesnotexist(self): self.assertTrue( - self.client.login(username='test_student_one', password='test')) + self.client.login(username=self.student_one.username, + password='test')) # Does not exist - response = self.client.post('/project/save/100/', - {}, + response = self.client.post('/project/save/1001/', {}, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 404) def test_project_save_cannot_edit(self): self.assertTrue( - self.client.login(username='test_instructor', password='test')) + self.client.login(username=self.instructor_one.username, + password='test')) # Forbidden to save or view - response = self.client.post('/project/save/1/', - follow=True, + url = '/project/save/%s/' % self.project_private.id + response = self.client.post(url, follow=True, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 403) self.assertEquals(len(response.redirect_chain), 1) elt = response.redirect_chain[0] - self.assertTrue(elt[0].endswith('project/view/1/')) + + view = 'project/view/%s/' % self.project_private.id + self.assertTrue(elt[0].endswith(view)) self.assertEquals(elt[1], 302) def test_project_save_nonajax(self): self.assertTrue( - self.client.login(username='test_student_one', password='test')) + self.client.login(username=self.student_one.username, + password='test')) - response = self.client.post('/project/save/1/') + url = '/project/save/%s/' % self.project_private.id + response = self.client.post(url) self.assertEquals(response.status_code, 405) def test_project_save_valid(self): - user = User.objects.get(username="test_student_one") - - project = Project.objects.get(id=1) - self.assertTrue(project.author.username, "test_student_one") - self.assertEquals(project.title, "Private Composition") - - self.assertTrue(self.client.login(username='test_student_one', + self.assertTrue(self.client.login(username=self.student_one.username, password='test')) data = {u'body': [u'

    abcdefghi

    '], - u'participants': [user.id], + u'participants': [self.student_one.id], u'publish': [u'PrivateEditorsAreOwners'], u'title': [u'Private Student Essay']} - response = self.client.post('/project/save/1/', - data, + url = '/project/save/%s/' % self.project_private.id + response = self.client.post(url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 200) @@ -64,28 +127,21 @@ def test_project_save_valid(self): self.assertEquals(the_json["status"], "success") self.assertFalse(the_json["is_assignment"]) self.assertEquals(the_json["title"], "Private Student Essay") - self.assertEquals(the_json["revision"]["id"], 2) self.assertEquals(the_json["revision"]["visibility"], "Private") self.assertIsNone(the_json["revision"]["public_url"]) self.assertEquals(the_json["revision"]["due_date"], "") def test_project_save_invalid_title(self): - user = User.objects.get(username="test_student_one") - - project = Project.objects.get(id=1) - self.assertTrue(project.author.username, "test_student_one") - self.assertEquals(project.title, "Private Composition") - - self.assertTrue(self.client.login(username='test_student_one', + self.assertTrue(self.client.login(username=self.student_one.username, password='test')) data = {u'body': [u'

    abcdefghi

    '], - u'participants': [user.id], + u'participants': [self.student_one.id], u'publish': [u'PrivateEditorsAreOwners'], u'title': [u'']} - response = self.client.post('/project/save/1/', - data, + url = '/project/save/%s/' % self.project_private.id + response = self.client.post(url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 200) @@ -94,12 +150,10 @@ def test_project_save_invalid_title(self): self.assertTrue(the_json["msg"].startswith(' "" is not valid')) def test_project_create_and_save(self): - user = User.objects.get(username="test_student_one") - - self.assertTrue(self.client.login(username='test_student_one', + self.assertTrue(self.client.login(username=self.student_one.username, password='test')) - data = {u'participants': [user.id], + data = {u'participants': [self.student_one.id], u'publish': [u'PrivateEditorsAreOwners']} response = self.client.post('/project/create/', data, follow=True) @@ -107,17 +161,18 @@ def test_project_create_and_save(self): self.assertTrue(response.redirect_chain[0][0].startswith( 'http://testserver/project/view/')) - project = Project.objects.get(title='Untitled') + project = Project.objects.get(course=self.sample_course, + title='Untitled') self.assertEquals(project.versions.count(), 1) self.assertIsNone(project.submitted_date()) data = {u'body': [u'

    abcdefghi

    '], - u'participants': [user.id], + u'participants': [self.student_one.id], u'publish': [u'InstructorShared'], u'title': [u'Student Essay']} - response = self.client.post('/project/save/1/', - data, + url = '/project/save/%s/' % project.id + response = self.client.post(url, data, HTTP_X_REQUESTED_WITH='XMLHttpRequest') self.assertEquals(response.status_code, 200) diff --git a/mediathread/projects/urls.py b/mediathread/projects/urls.py index 9b6f918a5..e3a82944c 100644 --- a/mediathread/projects/urls.py +++ b/mediathread/projects/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import patterns, url +from django.conf.urls import patterns, url from mediathread.projects.views import ProjectCreateView diff --git a/mediathread/reports/urls.py b/mediathread/reports/urls.py index 2d909df96..e5ac504a1 100644 --- a/mediathread/reports/urls.py +++ b/mediathread/reports/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import patterns, url +from django.conf.urls import patterns, url urlpatterns = patterns( diff --git a/mediathread/reports/views.py b/mediathread/reports/views.py index 914ec48dd..c360dec14 100644 --- a/mediathread/reports/views.py +++ b/mediathread/reports/views.py @@ -37,17 +37,13 @@ def class_assignment_report(request, project_id): @faculty_only def class_assignments(request): assignments = [] - maybe_assignments = Project.objects.filter( - request.course.faculty_filter) - for assignment in maybe_assignments: - if assignment.is_assignment(request): - assignments.append(assignment) + for project in Project.objects.filter(request.course.faculty_filter): + if project.is_assignment(request): + assignments.append(project) - num_students = users_in_course(request.course).count() return {'assignments': sorted(assignments, key=lambda assignment: assignment.title), - 'num_students': num_students, - 'submenu': 'assignments', } + 'num_students': len(request.course.students)} @allow_http("GET") @@ -68,8 +64,6 @@ def class_summary(request): len(Project.objects.visible_by_course_and_user( request, request.course, stud, False)), - # 'project_adds':stud_work.get(stud.id,[0,0])[0], - # 'project_deletes':stud_work.get(stud.id,[0,0])[1], 'comments': DiscussionIndex.objects.filter( participant=stud, @@ -77,9 +71,7 @@ def class_summary(request): }) students.append(stud) - context = {'students': students, 'submenu': 'summary', } - if request.user.is_staff: - context['courses'] = Course.objects.all() + context = {'students': students} return context @@ -200,9 +192,7 @@ def class_activity(request): collaboration__context=collab_context) .order_by('-modified')[:40],)) - context = {'my_feed': my_feed, 'submenu': 'activity', } - if request.user.is_staff: - context['courses'] = Course.objects.all() + context = {'my_feed': my_feed} return context diff --git a/mediathread/settings_production.py b/mediathread/settings_production.py index ac62ddb1f..d576a2712 100644 --- a/mediathread/settings_production.py +++ b/mediathread/settings_production.py @@ -9,6 +9,7 @@ ) MEDIA_ROOT = '/var/www/mediathread/uploads/' + # put any static media here to override app served static media STATICMEDIA_MOUNTS = ( ('/sitemedia', '/var/www/mediathread/mediathread/sitemedia'), diff --git a/mediathread/settings_shared.py b/mediathread/settings_shared.py index 6f7276229..51b1ca4bc 100644 --- a/mediathread/settings_shared.py +++ b/mediathread/settings_shared.py @@ -23,17 +23,15 @@ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': 'mediathread', + 'NAME': 'camron', 'HOST': '', - 'PORT': '', - 'USER': '', - 'PASSWORD': '', + 'PORT': '5432', + 'USER': 'camron', + 'PASSWORD': 'password', } } if 'test' in sys.argv or 'jenkins' in sys.argv: - CAPTCHA_TEST_MODE = True - DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', @@ -48,7 +46,6 @@ JENKINS_TASKS = ( 'django_jenkins.tasks.run_pylint', 'django_jenkins.tasks.with_coverage', - 'django_jenkins.tasks.django_tests', 'django_jenkins.tasks.run_pep8', 'django_jenkins.tasks.run_pyflakes', ) @@ -77,9 +74,10 @@ MEDIA_URL = "/uploads/" STATIC_URL = "/media/" +# Override the secret key with your own. This is for development only SECRET_KEY = ')ng#)ef_u@_^zvvu@dxm7ql-yb^_!a6%v3v^j3b(mp+)l+5%@h' -#appends a slash if nothing is found without a slash. +# appends a slash if nothing is found without a slash. APPEND_SLASH = True TEMPLATE_LOADERS = ( @@ -94,6 +92,8 @@ 'stagingcontext.staging_processor', 'django.core.context_processors.static', 'mediathread.main.views.django_settings', + 'djangowind.context.context_processor', + 'django.contrib.messages.context_processors.messages' ) MIDDLEWARE_CLASSES = [ @@ -108,6 +108,7 @@ 'courseaffils.middleware.CourseManagerMiddleware', 'mediathread.main.middleware.AuthRequirementMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', + 'impersonate.middleware.ImpersonateMiddleware', ] ROOT_URLCONF = 'mediathread.urls' @@ -128,7 +129,6 @@ 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.flatpages', - 'django.contrib.markup', 'django.contrib.staticfiles', 'django.contrib.messages', 'courseaffils', @@ -137,8 +137,8 @@ 'tagging', 'modelversions', 'structuredcollaboration', - 'mediathread.djangosherd', 'mediathread.assetmgr', + 'mediathread.djangosherd', 'mediathread.projects', 'mediathread.discussions', 'django.contrib.comments', @@ -153,21 +153,30 @@ 'mediathread.taxonomy', 'smoketest', 'debug_toolbar', - 'captcha', + 'django_markwhat', + 'impersonate', + 'registration', ] INTERNAL_IPS = ('127.0.0.1', ) -DEBUG_TOOLBAR_PANELS = ( - 'debug_toolbar.panels.version.VersionDebugPanel', - 'debug_toolbar.panels.timer.TimerDebugPanel', - 'debug_toolbar.panels.settings_vars.SettingsVarsDebugPanel', - 'debug_toolbar.panels.headers.HeaderDebugPanel', - 'debug_toolbar.panels.request_vars.RequestVarsDebugPanel', - 'debug_toolbar.panels.template.TemplateDebugPanel', - 'debug_toolbar.panels.sql.SQLDebugPanel', - 'debug_toolbar.panels.signals.SignalDebugPanel', - 'debug_toolbar.panels.logger.LoggingPanel', -) + +DEBUG_TOOLBAR_PATCH_SETTINGS = False + +DEBUG_TOOLBAR_PANELS = [ + 'debug_toolbar.panels.versions.VersionsPanel', + 'debug_toolbar.panels.timer.TimerPanel', + 'debug_toolbar.panels.settings.SettingsPanel', + 'debug_toolbar.panels.headers.HeadersPanel', + 'debug_toolbar.panels.request.RequestPanel', + 'debug_toolbar.panels.sql.SQLPanel', + 'debug_toolbar.panels.staticfiles.StaticFilesPanel', + 'debug_toolbar.panels.templates.TemplatesPanel', + 'debug_toolbar.panels.cache.CachePanel', + 'debug_toolbar.panels.signals.SignalsPanel', + 'debug_toolbar.panels.logging.LoggingPanel', + 'debug_toolbar.panels.redirects.RedirectsPanel', +] + STATIC_ROOT = os.path.join(os.path.dirname(__file__), "../media") STATICFILES_DIRS = ( @@ -182,15 +191,10 @@ COMPRESS_URL = "/media/" COMPRESS_ROOT = "media/" - THUMBNAIL_SUBDIR = "thumbs" EMAIL_SUBJECT_PREFIX = "[mediathread] " EMAIL_HOST = 'localhost' SERVER_EMAIL = "mediathread@example.com" -PUBLIC_CONTACT_EMAIL = "mediathread@example.com" - -# External url for issue reporting system or e-mail notification -CONTACT_US_DESTINATION = "" DATE_FORMAT = DATETIME_FORMAT = "g:i a, m/d/y" LOGOUT_REDIRECT_URL = LOGIN_REDIRECT_URL = '/' @@ -212,7 +216,6 @@ NON_ANONYMOUS_PATHS = ('/asset/', '/annotations/', - '/contact/', '/project/', '/explore/', '/comments/', @@ -272,7 +275,9 @@ def no_reject(request, reason): 'disable_existing_loggers': True, } -CAPTCHA_FONT_SIZE = 34 +SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' + +ACCOUNT_ACTIVATION_DAYS = 7 # if you add a 'deploy_specific' directory # then you can put a settings.py file and templates/ overrides there diff --git a/mediathread/settings_test.py b/mediathread/settings_test.py index e6e2cbc58..70e77d726 100644 --- a/mediathread/settings_test.py +++ b/mediathread/settings_test.py @@ -12,7 +12,8 @@ 'NAME': os.path.join(PROJECT_PATH, '../lettuce.db'), 'OPTIONS': { 'timeout': 30, - } + }, + 'ATOMIC_REQUESTS': True } } diff --git a/mediathread/taxonomy/tests/__init__.py b/mediathread/taxonomy/tests/__init__.py index c9ee14d95..1c51d7c82 100644 --- a/mediathread/taxonomy/tests/__init__.py +++ b/mediathread/taxonomy/tests/__init__.py @@ -1,5 +1,4 @@ -#pylint: disable-msg=W0401 +# pylint: disable-msg=W0401 # flake8: noqa from mediathread.taxonomy.tests.test_views import * from mediathread.taxonomy.tests.test_api import * -from mediathread.taxonomy.tests.test_models import * diff --git a/mediathread/taxonomy/tests/test_api.py b/mediathread/taxonomy/tests/test_api.py index d1b4723a5..29ac892f3 100644 --- a/mediathread/taxonomy/tests/test_api.py +++ b/mediathread/taxonomy/tests/test_api.py @@ -1,26 +1,92 @@ -#pylint: disable-msg=R0904 -#pylint: disable-msg=E1103 -from courseaffils.models import Course +# pylint: disable-msg=R0904 +# pylint: disable-msg=E1103 +import json + from django.contrib.auth.models import User from django.http.request import HttpRequest from django.test.client import RequestFactory +from django.test import TestCase + from mediathread.djangosherd.models import SherdNote +from mediathread.factories import MediathreadTestMixin, AssetFactory, \ + SherdNoteFactory from mediathread.taxonomy.api import VocabularyResource, \ VocabularyAuthorization -from mediathread.taxonomy.models import Vocabulary -from mediathread.taxonomy.tests.factories import TaxonomyTestCase +from mediathread.taxonomy.models import Vocabulary, Term, TermRelationship -class TaxonomyApiTest(TaxonomyTestCase): +class TaxonomyApiTest(MediathreadTestMixin, TestCase): def get_credentials(self): return None + def setUp(self): + self.setup_sample_course() + self.setup_alternate_course() + + taxonomy = { + 'Shapes': ['Square', 'Triangle'], + 'Colors': ['Red', 'Blue', 'Green'] + } + self.create_vocabularies(self.sample_course, taxonomy) + + taxonomy = { + 'Materials': ['Paper', 'Wood', 'Stone'] + } + self.create_vocabularies(self.alt_course, taxonomy) + + asset = AssetFactory(course=self.sample_course, + author=self.student_one, title="Asset One") + + # instructor_one in Sample Course + note = SherdNoteFactory( + asset=asset, author=self.student_three, + title="Nice Tie", tags=',student_three_selection', + body='student three selection note', range1=0, range2=1) + term = Term.objects.get(name="square") + self.create_term_relationship(note, term) + + note = SherdNoteFactory( + asset=asset, author=self.instructor_one, + title="Left Corner", tags=',instructor_one_selection', + body='instructor one selection note', range1=0, range2=1) + term = Term.objects.get(name="square") + self.create_term_relationship(note, term) + term = Term.objects.get(name="red") + self.create_term_relationship(note, term) + + # alt_student in Alternate Course + asset = AssetFactory(course=self.alt_course, + author=self.alt_student, title="Asset Two") + note = SherdNoteFactory( + asset=asset, author=self.student_two, + title="Whole Item Selection", tags=',alt_student_selection', + body='alt student selection note', range1=0, range2=1) + term = Term.objects.get(name="paper") + self.create_term_relationship(note, term) + + def test_single_term_relationship(self): + notes = SherdNote.objects.filter(asset__title='Asset Two') + + lst = TermRelationship.objects.get_for_object_list(notes) + self.assertEquals(len(lst), 1) + self.assertEquals(lst[0].term, Term.objects.get(name='paper')) + + def test_multiple_term_relationship(self): + notes = SherdNote.objects.filter(asset__title="Asset One") + + lst = TermRelationship.objects.get_for_object_list(notes) + + self.assertEquals(len(lst), 3) + self.assertEquals(lst[0].term, Term.objects.get(name='red')) + self.assertEquals(lst[1].term, Term.objects.get(name='square')) + self.assertEquals(lst[2].term, Term.objects.get(name='square')) + def test_vocabulary_authorization(self): factory = RequestFactory() request = factory.get('') - request.course = Course.objects.get(title='Sample Course') - request.user = User.objects.get(username='test_instructor') + request.course = self.sample_course + request.user = User.objects.get(username='instructor_one') vocabulary = Vocabulary.objects.all() authorization = VocabularyAuthorization() @@ -32,16 +98,15 @@ def test_vocabulary_authorization(self): self.assertEquals(len(lst), 2) def test_vocabulary_get_list(self): - self.assertTrue( - self.api_client.client.login(username="test_student_one", - password="test")) + self.assertTrue(self.client.login(username=self.student_one.username, + password="test")) - response = self.api_client.get('/api/vocabulary/', - format='json') - self.assertValidJSONResponse(response) + response = self.client.get('/api/vocabulary/', + {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') - json = self.deserialize(response) - lst = json['objects'] + the_json = json.loads(response.content) + lst = the_json['objects'] self.assertEquals(len(lst), 2) self.assertEquals(lst[0]['display_name'], "Colors") self.assertEquals(len(lst[0]['term_set']), 3) @@ -49,9 +114,8 @@ def test_vocabulary_get_list(self): self.assertEquals(len(lst[1]['term_set']), 2) def test_vocabulary_render_list(self): - course = Course.objects.get(title="Sample Course") request = HttpRequest() - request.course = course + request.course = self.sample_course lst = VocabularyResource().render_list( request, Vocabulary.objects.get_for_object(request.course)) @@ -64,23 +128,20 @@ def test_vocabulary_render_list(self): def test_vocabulary_get_one(self): self.assertTrue( - self.api_client.client.login(username="test_student_one", - password="test")) + self.client.login(username="student_one", password="test")) shapes = Vocabulary.objects.get(name="shapes") - response = self.api_client.get( - '/api/vocabulary/%s/' % shapes.id, - format='json') - self.assertValidJSONResponse(response) + response = self.client.get('/api/vocabulary/%s/' % shapes.id, + {}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') - detail = self.deserialize(response) - self.assertEquals(detail['display_name'], "Shapes") - self.assertEquals(len(detail['term_set']), 2) + the_json = json.loads(response.content) + self.assertEquals(the_json['display_name'], "Shapes") + self.assertEquals(len(the_json['term_set']), 2) def test_vocabulary_render_one(self): - course = Course.objects.get(title="Sample Course") request = HttpRequest() - request.course = course + request.course = self.sample_course vocabulary = Vocabulary.objects.get(name="shapes") detail = VocabularyResource().render_one(request, vocabulary) @@ -88,9 +149,8 @@ def test_vocabulary_render_one(self): self.assertEquals(len(detail['term_set']), 2) def test_vocabulary_render_related(self): - course = Course.objects.get(title="Sample Course") request = HttpRequest() - request.course = course + request.course = self.sample_course notes = SherdNote.objects.filter(title='Left Corner') ctx = VocabularyResource().render_related(request, notes) @@ -107,9 +167,8 @@ def test_vocabulary_render_related(self): self.assertEquals(ctx[1]['term_set'][0]['count'], 1) def test_vocabulary_render_related_multiple(self): - course = Course.objects.get(title="Sample Course") request = HttpRequest() - request.course = course + request.course = self.sample_course notes = SherdNote.objects.filter(title__in=['Left Corner', 'Nice Tie']) ctx = VocabularyResource().render_related(request, notes) @@ -124,3 +183,27 @@ def test_vocabulary_render_related_multiple(self): self.assertEquals(len(ctx[1]['term_set']), 1) self.assertEquals(ctx[1]['term_set'][0]['display_name'], 'Square') self.assertEquals(ctx[1]['term_set'][0]['count'], 2) + + def test_vocabulary_render_for_course(self): + request = HttpRequest() + request.course = self.sample_course + + notes = SherdNote.objects.filter(title='Nice Tie') + ctx = VocabularyResource().render_for_course(request, notes) + + self.assertEquals(len(ctx), 2) + self.assertEquals(ctx[0]['display_name'], 'Colors') + self.assertEquals(len(ctx[0]['term_set']), 3) + self.assertEquals(ctx[0]['term_set'][0]['display_name'], 'Blue') + self.assertEquals(ctx[0]['term_set'][0]['count'], 0) + self.assertEquals(ctx[0]['term_set'][1]['display_name'], 'Green') + self.assertEquals(ctx[0]['term_set'][1]['count'], 0) + self.assertEquals(ctx[0]['term_set'][2]['display_name'], 'Red') + self.assertEquals(ctx[0]['term_set'][2]['count'], 0) + + self.assertEquals(ctx[1]['display_name'], 'Shapes') + self.assertEquals(len(ctx[1]['term_set']), 2) + self.assertEquals(ctx[1]['term_set'][0]['display_name'], 'Square') + self.assertEquals(ctx[1]['term_set'][0]['count'], 1) + self.assertEquals(ctx[1]['term_set'][1]['display_name'], 'Triangle') + self.assertEquals(ctx[1]['term_set'][1]['count'], 0) diff --git a/mediathread/taxonomy/tests/test_views.py b/mediathread/taxonomy/tests/test_views.py index 7bec80cfe..a4fa7c621 100644 --- a/mediathread/taxonomy/tests/test_views.py +++ b/mediathread/taxonomy/tests/test_views.py @@ -1,71 +1,62 @@ -#pylint: disable-msg=R0904 -from courseaffils.models import Course +# pylint: disable-msg=R0904 from django.contrib.contenttypes.models import ContentType -from django.test import TestCase from django.test.client import RequestFactory -from mediathread.djangosherd.models import SherdNote +from django.test.testcases import TestCase + +from mediathread.factories import AssetFactory, SherdNoteFactory, \ + MediathreadTestMixin from mediathread.taxonomy.models import Vocabulary, Term, TermRelationship from mediathread.taxonomy.views import update_vocabulary_terms -class TaxonomyViewTest(TestCase): - fixtures = ['unittest_sample_course.json'] +class TaxonomyViewTest(MediathreadTestMixin, TestCase): def setUp(self): - # Every test needs access to the request factory. + self.setup_sample_course() self.factory = RequestFactory() - course = Course.objects.get(id=1) - course_type = ContentType.objects.get_for_model(course) - taxonomy = { 'Shapes': ['Square', 'Triangle', 'Circle'], 'Colors': ['Red', 'Blue', 'Green'] } + self.create_vocabularies(self.sample_course, taxonomy) - for name, terms in taxonomy.items(): - concept = Vocabulary(display_name=name, - content_type=course_type, - object_id=course.id) - concept.save() - for term_name in terms: - term = Term(display_name=term_name, - vocabulary=concept) - term.save() + self.asset = AssetFactory(course=self.sample_course) + self.note = SherdNoteFactory( + asset=self.asset, author=self.student_one, + title="Whole Item Selection", range1=116.25, range2=6.75) def test_simple_association(self): concept = Vocabulary.objects.get(display_name="Shapes") term = Term.objects.get(display_name="Square") post_data = {'vocabulary-%s' % str(concept.id): [str(term.id)]} - sherd_note = SherdNote.objects.get(id=1) - related_terms = TermRelationship.objects.get_for_object(sherd_note) + related_terms = TermRelationship.objects.get_for_object(self.note) self.assertEquals(len(related_terms), 0) request = self.factory.post('/', post_data) - update_vocabulary_terms(request, sherd_note) + update_vocabulary_terms(request, self.note) - related_terms = TermRelationship.objects.get_for_object(sherd_note) + related_terms = TermRelationship.objects.get_for_object(self.note) self.assertEquals(len(related_terms), 1) self.assertEquals(related_terms[0].term, term) - self.assertEquals(related_terms[0].object_id, sherd_note.id) + self.assertEquals(related_terms[0].object_id, self.note.id) def test_removal(self): term = Term.objects.get(display_name="Square") - sherd_note = SherdNote.objects.get(id=1) - sherdnote_type = ContentType.objects.get_for_model(sherd_note) + sherdnote_type = ContentType.objects.get_for_model(self.note) TermRelationship.objects.create(term=term, - object_id=sherd_note.id, + object_id=self.note.id, content_type=sherdnote_type) - related_terms = TermRelationship.objects.get_for_object(sherd_note) + related_terms = TermRelationship.objects.get_for_object(self.note) self.assertEquals(len(related_terms), 1) request = self.factory.post('/', {}) - update_vocabulary_terms(request, sherd_note) + update_vocabulary_terms(request, self.note) - related_terms = TermRelationship.objects.get_for_object(sherd_note) + related_terms = TermRelationship.objects.get_for_object(self.note) self.assertEquals(len(related_terms), 0) def test_removal_and_association(self): @@ -73,23 +64,22 @@ def test_removal_and_association(self): square = Term.objects.get(display_name="Square") circle = Term.objects.get(display_name="Circle") - sherd_note = SherdNote.objects.get(id=1) - sherdnote_type = ContentType.objects.get_for_model(sherd_note) + sherdnote_type = ContentType.objects.get_for_model(self.note) TermRelationship.objects.create(term=square, - object_id=sherd_note.id, + object_id=self.note.id, content_type=sherdnote_type) - related_terms = TermRelationship.objects.get_for_object(sherd_note) + related_terms = TermRelationship.objects.get_for_object(self.note) self.assertEquals(len(related_terms), 1) post_data = {'vocabulary-%s' % str(concept.id): [str(circle.id)]} request = self.factory.post('/', post_data) - update_vocabulary_terms(request, sherd_note) + update_vocabulary_terms(request, self.note) - related_terms = TermRelationship.objects.get_for_object(sherd_note) + related_terms = TermRelationship.objects.get_for_object(self.note) self.assertEquals(len(related_terms), 1) self.assertEquals(related_terms[0].term, circle) - self.assertEquals(related_terms[0].object_id, sherd_note.id) + self.assertEquals(related_terms[0].object_id, self.note.id) def test_multiple_associations_and_removals(self): shapes = Vocabulary.objects.get(display_name="Shapes") @@ -100,32 +90,31 @@ def test_multiple_associations_and_removals(self): red = Term.objects.get(display_name='Red') blue = Term.objects.get(display_name='Blue') - sherd_note = SherdNote.objects.get(id=1) - sherdnote_type = ContentType.objects.get_for_model(sherd_note) + sherdnote_type = ContentType.objects.get_for_model(self.note) TermRelationship.objects.create(term=square, - object_id=sherd_note.id, + object_id=self.note.id, content_type=sherdnote_type) TermRelationship.objects.create(term=red, - object_id=sherd_note.id, + object_id=self.note.id, content_type=sherdnote_type) - related_terms = TermRelationship.objects.get_for_object(sherd_note) + related_terms = TermRelationship.objects.get_for_object(self.note) self.assertEquals(len(related_terms), 2) self.assertEquals(related_terms[0].term, red) - self.assertEquals(related_terms[0].object_id, sherd_note.id) + self.assertEquals(related_terms[0].object_id, self.note.id) self.assertEquals(related_terms[1].term, square) - self.assertEquals(related_terms[1].object_id, sherd_note.id) + self.assertEquals(related_terms[1].object_id, self.note.id) post_data = { 'vocabulary-%s' % str(shapes.id): [str(circle.id)], 'vocabulary-%s' % str(colors.id): [str(blue.id)], } request = self.factory.post('/', post_data) - update_vocabulary_terms(request, sherd_note) + update_vocabulary_terms(request, self.note) - related_terms = TermRelationship.objects.get_for_object(sherd_note) + related_terms = TermRelationship.objects.get_for_object(self.note) self.assertEquals(len(related_terms), 2) self.assertEquals(related_terms[0].term, blue) - self.assertEquals(related_terms[0].object_id, sherd_note.id) + self.assertEquals(related_terms[0].object_id, self.note.id) self.assertEquals(related_terms[1].term, circle) - self.assertEquals(related_terms[1].object_id, sherd_note.id) + self.assertEquals(related_terms[1].object_id, self.note.id) diff --git a/mediathread/taxonomy/urls.py b/mediathread/taxonomy/urls.py index 46d2275d9..efb818f68 100644 --- a/mediathread/taxonomy/urls.py +++ b/mediathread/taxonomy/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import patterns, url +from django.conf.urls import patterns, url import os.path media_root = os.path.join(os.path.dirname(__file__), "media") diff --git a/mediathread/templates/base.html b/mediathread/templates/base.html index 9c547c8e7..45ca5291a 100644 --- a/mediathread/templates/base.html +++ b/mediathread/templates/base.html @@ -11,7 +11,17 @@ +<<<<<<< HEAD +======= + + {% if settings.FIREFOX %} + + {% else %} + + {% endif %} + +>>>>>>> added spring 2015 files @@ -59,16 +69,27 @@
  • @@ -98,16 +119,16 @@ - + {% endif %} - +
  • {% block standardnav_help %}
    @@ -124,11 +145,7 @@
  • - {% if request.user.is_authenticated and course %} -
    Contact Us
    - {% else %}{% if settings.PUBLIC_CONTACT_EMAIL %} -
    Contact Us
    - {% endif %}{% endif %} +
    Contact Us
  • {% else %}
  • Help
  • @@ -193,11 +210,7 @@

    diff --git a/mediathread/templates/dashboard/base_dashboard.html b/mediathread/templates/dashboard/base_dashboard.html index ea3caa24c..2653e304b 100644 --- a/mediathread/templates/dashboard/base_dashboard.html +++ b/mediathread/templates/dashboard/base_dashboard.html @@ -14,24 +14,7 @@ {% block dashboard_module_header %} {% endblock %} - - {% block switchcourse %} - {%if request.user.is_staff and courses %} -
    -
    - - -
    -
    - {% endif %} - {% endblock %} - +
    {% block dashboard_module %}{% endblock %}
    diff --git a/mediathread/templates/dashboard/class_assignments.html b/mediathread/templates/dashboard/class_assignments.html index e8a92fd5c..5042beeba 100644 --- a/mediathread/templates/dashboard/class_assignments.html +++ b/mediathread/templates/dashboard/class_assignments.html @@ -9,8 +9,6 @@

    Report: Assignment Responses

    {% block dashboard_module %} -

    If you have published assignments to the class, this report allows you to access responses and track your feedback.

    - {% if assignments %} @@ -36,9 +34,7 @@

    Report: Assignment Responses

    {% else %} - -todo: help text - +

    If you have published assignments to the class, this report allows you to access responses and track your feedback.

    {% endif %} {% endblock %} diff --git a/mediathread/templates/dashboard/class_manage_sources.html b/mediathread/templates/dashboard/class_manage_sources.html index db9f97d86..adf4461b2 100644 --- a/mediathread/templates/dashboard/class_manage_sources.html +++ b/mediathread/templates/dashboard/class_manage_sources.html @@ -11,10 +11,7 @@ {% else %}{% if delsrc %} var msg = "{{delsrc|escapejs}} has been disabled for your class."; showMessage(msg); - {% else %}{% if changes_saved %} - var msg = "Your changes have been saved."; - showMessage(msg); - {% endif %}{% endif %}{% endif %} + {% else %}{% endif %}{% endif %} }); {% endblock %} @@ -26,6 +23,15 @@

    Manage Sources

    {% endblock %} {% block dashboard_module %} + + {% if messages %} +
    + {% for message in messages %} + {{ message }}
    + {% endfor %} + + {% endif %} +

    Mediathread Video Uploader

    Mediathread supports direct uploading of videos from a user's desktop. By default this direct upload feature is turned off. @@ -42,7 +48,7 @@

    Mediathread Video Uploader

    {% endfor %}

    - + {% else %}
    diff --git a/mediathread/templates/dashboard/class_settings.html b/mediathread/templates/dashboard/class_settings.html index 9234f43da..e9b67ee60 100644 --- a/mediathread/templates/dashboard/class_settings.html +++ b/mediathread/templates/dashboard/class_settings.html @@ -2,13 +2,91 @@ {% load assetlinks %} {% block title %}Manage Course Settings{% endblock %} +{% block css %} + +{% endblock %} + {% block uncompressable_js %} - @@ -21,33 +99,56 @@

    Manage Course Settings

    {% endblock %} {% block dashboard_module %} + + {% if messages %} +
    + {% for message in messages %} + {{ message }}
    + {% endfor %} + + {% endif %} + +
    + +

    Homepage "From Your Instructor" Title

    +

    This feature allows faculty to customize the left-hand column title on the Mediathread homepage. + Titles should be less than 25 characters long.

    + + + + +

    "Publish To The World" Compositions

    This feature allows authors to publish compositions at a public level, via a link that does not require logging into Mediathread.

    - -
    - Enabled:  - Yes - No -

    - -
    - +
    + Enabled:  + Yes + No +

    + +
    -

    Item Selection Visibility

    -

    Turning off selection visibility hides individual item selections from all - collections views. Faculty and students can still view a selection embedded - in a visible composition.

    +

    Item & Selection Visibility

    +

    Turning off item or selection visibility hides individual items or selections from all + collections views. Students can still view an item or selection embedded + in a visible composition. Faculty always can view all items and selections.

    -
    + + Course members can see each other's items:   + Yes + No +

    + Course members can see each other's selections:   Yes No

    +
    diff --git a/mediathread/templates/dashboard/class_summary.html b/mediathread/templates/dashboard/class_summary.html index b5ef7ad0b..d08976a7d 100644 --- a/mediathread/templates/dashboard/class_summary.html +++ b/mediathread/templates/dashboard/class_summary.html @@ -107,12 +107,11 @@ {% endblock %} {% block dashboard_module_header %} -

    Report: Student Contributions

    +

    Report: Class Member Contributions

    {% endblock %} {% block dashboard_module %} - -

    This report lists the number of each class member's saved selections, discussion thread posts, and compositions. To access an individual student's work, click on her name.

    +

    This report lists the number of each class member's saved selections, discussion thread posts, and compositions. To access an individual person's work, click on her name.

    diff --git a/mediathread/templates/djangosherd/annotator_resources.html b/mediathread/templates/djangosherd/annotator_resources.html index d0aa76eb6..1faa1c19a 100644 --- a/mediathread/templates/djangosherd/annotator_resources.html +++ b/mediathread/templates/djangosherd/annotator_resources.html @@ -7,6 +7,7 @@ flowplayer.audio_plugin = '{{settings.FLOWPLAYER_AUDIO_PLUGIN}}'; flowplayer.pseudostreaming_plugin = '{{settings.FLOWPLAYER_PSEUDOSTREAMING_PLUGIN}}'; flowplayer.rtmp_plugin = '{{settings.FLOWPLAYER_RTMP_PLUGIN}}'; + @@ -16,7 +17,13 @@ - + +{% if settings.FIREFOX %} + +{% else %} + +{% endif %} + diff --git a/mediathread/templates/djangosherd/annotator_resources_css.html b/mediathread/templates/djangosherd/annotator_resources_css.html index 1296ff726..817488319 100644 --- a/mediathread/templates/djangosherd/annotator_resources_css.html +++ b/mediathread/templates/djangosherd/annotator_resources_css.html @@ -22,5 +22,10 @@ width:25px; } +<<<<<<< HEAD +======= + + +>>>>>>> added spring 2015 files \ No newline at end of file diff --git a/mediathread/templates/homepage.html b/mediathread/templates/homepage.html index b31a16208..a07e68c2b 100644 --- a/mediathread/templates/homepage.html +++ b/mediathread/templates/homepage.html @@ -95,13 +95,15 @@ {% block content %} {{ block.super }} - {% if upgrade_bookmarklet %} + {% if course and course.title == "Mediathread Guest Sandbox" %}
    + onclick="jQuery(this).parent().fadeOut(); return false;"> - The Mediathread bookmarklet has been updated to comply with new web security protocols. Upgrade your old bookmarklet now. + Welcome to Mediathread! The Guest Sandbox is a place to experiment with all the application features. +

    Be aware that work here is temporary. All user-created compositions and selections are removed every Monday morning.

    +

    To learn more about Mediathread and how to install an instance at your institution, visit http://mediathread.info

    {% endif %} @@ -111,44 +113,42 @@ {% if faculty_feed|length > 0 or is_faculty or discussions %} {% endif %} diff --git a/mediathread/templates/main/contact.html b/mediathread/templates/main/contact.html index 031055c13..33b63c3e5 100644 --- a/mediathread/templates/main/contact.html +++ b/mediathread/templates/main/contact.html @@ -1,206 +1,16 @@ {% extends "base.html" %} -{% block title %} - Contact Us -{% endblock %} - -{% block css %} - -{% endblock %} - -{% block js %} - -{% endblock %} - -{% block uncompressable_js %} - - -{% endblock %} +{% block title %}Contact Us{% endblock %} {% block content %} -
    +

    Contact Us

    -

    If you have comments, suggestions, or questions about this site, or are having a problem using it, - please contact us using the following form. We will do our best to respond within one business day. + please contact us at {{settings.SERVER_EMAIL}}. +

    +

    For more immediate information, please visit the Mediathread Help documentation.

    - -
    -
    - - -
    - -
    -
    - -
    -
    - -
    -
    - -
    - -
    -
    -
    - - - -
    - - - - - - - - - - - - - - - -
    +
    {% endblock %} diff --git a/mediathread/templates/main/course_request.html b/mediathread/templates/main/course_request.html index 1f062eea5..612db8274 100644 --- a/mediathread/templates/main/course_request.html +++ b/mediathread/templates/main/course_request.html @@ -1,320 +1,13 @@ {% extends "base.html" %} -{% block title %}Request Course{% endblock %} - -{% block js %} - -{% endblock %} - -{% block css %} - -{% endblock %} - -{% block uncompressable_js %} - - -{% endblock %} +{% block title %}Course Request{% endblock %} {% block content %} -
    -

    Request a Mediathread Course

    - -

    - Please provide some basic information on your course and how Mediathread will be integrated into the curriculum. -

    +
    +

    Course Request

    If you are not part of Columbia University, please contact {{settings.PUBLIC_CONTACT_EMAIL}} for information on how to join a guest sandbox or get started with Mediathread at your institution.

    - - {% if form.errors|length %} -
    - Please correct errors before continuing. - {% if form.non_field_errors %} -
    {{ form.non_field_errors}}
    - {% endif %} -
    - {% endif %} - - -
    -
    - -
    - - -
    Name, email and uni are required fields.
    -
    - -
    -
    - - - -
    Course and Course Number are required fields.
    -
    - -
    -
    - - {{form.term}} - -
    Year is a required field.
    -
    - -
    -
    - - {{form.instructor}} -
    Lead Instructor is a required field.
    -
    - -
    - - {{form.section_leader}} -
    - -
    - - - -
    Start Date and End Date are required fields.
    -
    - -
    -
    - - -
    Number of students is a required field.
    -
    - -
    - - - - - -


    Please indicate if assignments are required.
    -
    - -

    -
    -
    -
    -
    - {{form.description}} -
    -


    Course description is a required field.
    -
    - -

    -
    - -
    - {{form.captcha}} {% if form.captcha.errors %}{{form.captcha.errors}}{% endif %} -
    -
    - -
    - Type the characters you see in the picture.

    - if you can't read the picture.
    -
    - - - -
    +
    {% endblock %} diff --git a/mediathread/templates/main/course_request_description.txt b/mediathread/templates/main/course_request_description.txt index 70b13c6c1..f163b8162 100644 --- a/mediathread/templates/main/course_request_description.txt +++ b/mediathread/templates/main/course_request_description.txt @@ -7,15 +7,15 @@ Course Id: {{course_id}} Term: {{term}} Year: {{year}} - + Instructor: {{instructor}} Section Leader: {{section_leader}} Date Range: {{start|date:"SHORT_DATE_FORMAT" }} - {{end|date:"SHORT_DATE_FORMAT" }} - + Students: {{students}} Assignments Required? {{assignments_required}} - + Course Description: {{description}} diff --git a/mediathread/templates/registration/description.html b/mediathread/templates/registration/description.html index ab1a0e5fd..ba4b2bcf0 100644 --- a/mediathread/templates/registration/description.html +++ b/mediathread/templates/registration/description.html @@ -2,12 +2,8 @@

    Mediathread is an open-source platform for exploration, analysis, and organization of web-based multimedia content.

    Mediathread connects to a variety of image and video collections (such as YouTube, Flickr, library databases, and course libraries), enabling users to lift items out of these collections and into an analysis environment. In Mediathread, items can then be clipped, annotated, organized, and embedded into essays and other written analysis. Learn More


    - Request a Course
    - {% if settings.DEBUG %} - {% else %} - {% endif %}
    diff --git a/mediathread/templates/registration/login.html b/mediathread/templates/registration/login.html index 366a6d35f..572673c3c 100644 --- a/mediathread/templates/registration/login.html +++ b/mediathread/templates/registration/login.html @@ -21,6 +21,7 @@

    Log In

    {% endif %} +
    {% csrf_token %} diff --git a/mediathread/templates/taxonomy/taxonomy.html b/mediathread/templates/taxonomy/taxonomy.html index 643808ad6..102427734 100644 --- a/mediathread/templates/taxonomy/taxonomy.html +++ b/mediathread/templates/taxonomy/taxonomy.html @@ -39,7 +39,7 @@ <% } %> - <% if (vocabularies.length < 10) { %> + <% if (vocabularies.length < 3) { %>
  • @@ -64,17 +64,7 @@ <% for (var i=0; i < vocabularies.length; i++) { %>
    -

    - - refresh -

    - -

    <%=vocabularies[i].display_name%> Concept

    +

    <%=vocabularies[i].display_name%> Concept

    Terms

    diff --git a/mediathread/urls.py b/mediathread/urls.py index fc450738c..2b6e899bc 100644 --- a/mediathread/urls.py +++ b/mediathread/urls.py @@ -1,16 +1,23 @@ +import os.path + from django.conf import settings from django.conf.urls import patterns, include, url from django.contrib import admin -from django.contrib.auth.decorators import login_required +from django.contrib.auth.views import password_change, password_change_done, \ + password_reset, password_reset_done, password_reset_complete, \ + password_reset_confirm from django.views.generic.base import TemplateView +from registration.backends.default.views import RegistrationView +from tastypie.api import Api + from mediathread.assetmgr.views import AssetCollectionView, AssetDetailView, \ TagCollectionView +from mediathread.main.forms import CustomRegistrationForm from mediathread.main.views import MigrateCourseView, MigrateMaterialsView, \ - RequestCourseView + RequestCourseView, ContactUsView, CourseSettingsView, \ + CourseManageSourcesView from mediathread.projects.views import ProjectCollectionView, ProjectDetailView from mediathread.taxonomy.api import TermResource, VocabularyResource -from tastypie.api import Api -import os.path tastypie_api = Api('') @@ -26,23 +33,57 @@ redirect_after_logout = getattr(settings, 'LOGOUT_REDIRECT_URL', None) auth_urls = (r'^accounts/', include('django.contrib.auth.urls')) + logout_page = (r'^accounts/logout/$', 'django.contrib.auth.views.logout', {'next_page': redirect_after_logout}) +admin_logout_page = (r'^accounts/logout/$', + 'django.contrib.auth.views.logout', + {'next_page': '/admin/'}) -if hasattr(settings, 'WIND_BASE'): +if hasattr(settings, 'CAS_BASE'): auth_urls = (r'^accounts/', include('djangowind.urls')) - logout_page = (r'^accounts/logout/$', 'djangowind.views.logout', + logout_page = (r'^accounts/logout/$', + 'djangowind.views.logout', {'next_page': redirect_after_logout}) + admin_logout_page = (r'^admin/logout/$', + 'djangowind.views.logout', + {'next_page': redirect_after_logout}) urlpatterns = patterns( '', (r'^$', 'mediathread.main.views.triple_homepage'), # Homepage - + admin_logout_page, + logout_page, (r'^admin/', admin.site.urls), + # override the default urls for pasword + url(r'^password/change/$', + password_change, + name='password_change'), + url(r'^password/change/done/$', + password_change_done, + name='password_change_done'), + url(r'^password/reset/$', + password_reset, + name='password_reset'), + url(r'^password/reset/done/$', + password_reset_done, + name='password_reset_done'), + url(r'^password/reset/complete/$', + password_reset_complete, + name='password_reset_complete'), + url(r'^password/reset/confirm/(?P[0-9A-Za-z]+)-(?P.+)/$', + password_reset_confirm, + name='password_reset_confirm'), + + url(r'^accounts/register/$', + RegistrationView.as_view(form_class=CustomRegistrationForm), + name='registration_register'), + (r'^accounts/', include('registration.backends.default.urls')), + # API - JSON rendering layers. Half hand-written, half-straight tasty=pie (r'^api/asset/user/(?P\w[^/]*)/$', AssetCollectionView.as_view(), {}, 'assets-by-user'), @@ -71,12 +112,12 @@ 'django.views.static.serve', {'document_root': bookmarklet_root}, name='nocache-analyze-bookmarklet'), - url(r'^captcha/', include('captcha.urls')), - (r'^comments/', include('django.contrib.comments.urls')), - (r'^contact/', login_required( - TemplateView.as_view(template_name="main/contact.html"))), + # Columbia only request forms. + (r'^contact/success/$', + TemplateView.as_view(template_name="main/contact_success.html")), + (r'^contact/$', ContactUsView.as_view()), (r'^course/request/success/$', TemplateView.as_view(template_name="main/course_request_success.html")), (r'^course/request/', RequestCourseView.as_view()), @@ -95,12 +136,10 @@ MigrateMaterialsView.as_view(), {}, 'dashboard-migrate-materials'), url(r'^dashboard/migrate/$', MigrateCourseView.as_view(), {}, "dashboard-migrate"), - url(r'^dashboard/sources/', - 'mediathread.main.views.class_manage_sources', + url(r'^dashboard/sources/', CourseManageSourcesView.as_view(), name="class-manage-sources"), - url(r'^dashboard/settings/', - 'mediathread.main.views.class_settings', - name="class-settings"), + url(r'^dashboard/settings/', CourseSettingsView.as_view(), + name="course-settings"), # Discussion (r'^discussion/', include('mediathread.discussions.urls')), @@ -110,9 +149,9 @@ 'mediathread.assetmgr.views.source_redirect', name="source_redirect"), - (r'^jsi18n', 'django.views.i18n.javascript_catalog'), + url(r'^impersonate/', include('impersonate.urls')), - logout_page, + (r'^jsi18n', 'django.views.i18n.javascript_catalog'), (r'^media/(?P.*)$', 'django.views.static.serve', {'document_root': @@ -141,6 +180,13 @@ url(r'^upgrade/', 'mediathread.main.views.upgrade_bookmarklet'), - ### Public Access ### + # Public Access ### (r'^s/', include('structuredcollaboration.urls')), ) + +if settings.DEBUG: + import debug_toolbar + urlpatterns += patterns( + '', + url(r'^__debug__/', include(debug_toolbar.urls)), + ) diff --git a/requirements.txt b/requirements.txt index 466cca3cb..abe66df20 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,8 @@ -Django==1.5.4 +Django==1.6.5 httplib2==0.8 restclient==0.11.0 feedparser==5.1.3 Markdown==2.3.1 -simplejson==3.4.0 smartypants==1.6.0.2 uuid==1.30 psycopg2==2.5.1 @@ -19,7 +18,7 @@ flake8==2.0 BeautifulSoup==3.2.1 lxml==3.2.3 fuzzywuzzy==0.2 -sure==1.2.2 +sure==1.2.7 ipython==0.13.2 ipdb==0.7 extras==0.0.3 @@ -29,18 +28,19 @@ python-subunit==0.0.18 requirements/src/lettuce-0.2.19_ccnmtl-py2-none-any.whl selenium==2.42.1 six==1.3.0 +sqlparse==0.1.11 python-mimeparse==0.1.4 mimeparse==0.1.3 -django-tastypie==0.11.0 +django-tastypie==0.12.0 coverage==3.5.3 logilab-common==0.59.0 logilab-astng==0.24.1 -pylint==0.26.0 +astroid==1.2.1 +pylint==1.3.1 requests==1.2.3 -requirements/src/djangowind-0.11.0.tar.gz +requirements/src/djangowind-0.13.2-py2-none-any.whl django-tagging==0.3.1 template_utils==0.4p2 -typogrify==1.0 requirements/src/django-modelversions-1.0.tar.gz djangohelpers==0.15 django-indexer==0.2.1 @@ -49,15 +49,18 @@ django-paging==0.2.2 South==1.0 django-annoying==0.8.0 django-nose==1.2 -django-threadedcomments==0.9 -requirements/src/django-courseaffils-0.4.9.tar.gz +django-threadedcomments==0.9.0 +requirements/src/django-courseaffils-0.5.0.tar.gz django-statsd-mozilla==0.3.11 raven==4.0.3 django-appconf==0.6 django_compressor==1.4 -django-jenkins==0.14.0 +django-jenkins==0.16.3 requirements/src/django-stagingcontext-0.1.0.tar.gz django-smoketest==0.2.1 -django-debug-toolbar==0.9.4 +django-debug-toolbar==1.1 django-simple-captcha==0.4.1 +django-markwhat==1.0 factory_boy==2.1.1 +django-impersonate==0.7.0 +requirements/src/django-registration-1.1.tar.gz diff --git a/requirements/js.txt b/requirements/js.txt index 1116723cd..e037ba199 100644 --- a/requirements/js.txt +++ b/requirements/js.txt @@ -1 +1,5 @@ +<<<<<<< HEAD requirements/src/sherdjs-0.4.1.tar.gz +======= +requirements/src/sherdjs-0.4.5.tar.gz +>>>>>>> added spring 2015 files diff --git a/scripts/lettuce_base.db b/scripts/lettuce_base.db index 82d01c6cbba81fdecf21144582157e470418e5a9..f33efdcef4249781303998ef2eb36e92e1da4dc0 100644 GIT binary patch delta 1014 zcmZuvO-vI}5Z<@D?=7WNtI|*~#MdRL1S;D?(bO2INaTP(1U+e%#V%MZZP{*(8WRdl zc4QgWip^=4nAjWX;hDkkn;Y>)7MC2eQZUG4t4>QS|Z|0ksZ(evLKRl8@ zxPQ+IK@cI$B+YHm*S-ztGSQ$Iu!7sYH9e4+KbZH5vgn;LX4XaOorzCXKQdX&LZZ-A zkmAJ~#GQi2x1~?w!uOXnWyzY3F>{4v)^ws|E}CV^rcDJPEt8ET7u4he;3AY+x1=xu zvXs;=2l_zo`VRE@r6&%Oec_AeGc|L7MJ7+7m+U!=Y9NWsA_6n!1{D{M0x7#XI z?gp8JlxP@Jm@qkm4C98^hOvaDC7HDsQUI{hn(tckfxgKBQk@*2YU2|eppAWW6|EV3 zL02J;ItJUd(a;qHzrg#jT@!hcCQBt{^d>TOZ%FVTxl6ksaU9sH1s`6Q9BaLs})2-ve4=kQ!;Noe){`V_1PL%a}92Govf?D zY&xujJK!N@$_;asmLb4&mta=*$X=1fR|QGiR61;TR21R*b9HqZC delta 519 zcmZozAltA&c7n9vdjt@%_exCGyNkY_BG>D;(W?&_RxE^D8x} zmCOpR?O#gSc%`zr{1^lo?HO3Eu(q(7v8QnS=l;s`iuWB~6^kx& z9k&QqF;h2x4C7;<5-XrTkU(JxySSw~V [%s]' % (self.title, self.pk, self.content_type, self.object_pk, self.slug) diff --git a/structuredcollaboration/policies.py b/structuredcollaboration/policies.py index ebbfa7c3d..a657bc16e 100644 --- a/structuredcollaboration/policies.py +++ b/structuredcollaboration/policies.py @@ -35,11 +35,7 @@ def read(self, collaboration, request): class PolicyByType(CollaborationPolicy): - """ - A common pattern is to have different policies for each class - >>> PolicyByType(default=EditorsAreOwners, - {Group:ParentEditorsAreOwners,} ) - """ + types = None default = None diff --git a/structuredcollaboration/tests/test_models.py b/structuredcollaboration/tests/test_models.py index 6bd22d88c..3833aa001 100644 --- a/structuredcollaboration/tests/test_models.py +++ b/structuredcollaboration/tests/test_models.py @@ -1,21 +1,21 @@ -from courseaffils.models import Course from django.test.testcases import TestCase -from mediathread.projects.models import Project + +from mediathread.factories import MediathreadTestMixin, ProjectFactory from structuredcollaboration.models import Collaboration -class ModelsTest(TestCase): - # Use ``fixtures`` & ``urls`` as normal. See Django's ``TestCase`` - # documentation for the gory details. - fixtures = ['unittest_sample_course.json', - 'unittest_sample_projects.json'] +class ModelsTest(MediathreadTestMixin, TestCase): + + def setUp(self): + self.setup_sample_course() def test_get_associated_collaboration_project(self): - project = Project.objects.get(id=2) + project = ProjectFactory.create( + course=self.sample_course, author=self.student_one, + policy='PrivateEditorsAreOwners') collaboration = Collaboration.get_associated_collab(project) self.assertIsNotNone(collaboration) def test_get_associated_collaboration_course(self): - course = Course.objects.get(id=2) - collaboration = Collaboration.get_associated_collab(course) + collaboration = Collaboration.get_associated_collab(self.sample_course) self.assertIsNotNone(collaboration) diff --git a/structuredcollaboration/urls.py b/structuredcollaboration/urls.py index 0be350f57..ed008b1b7 100644 --- a/structuredcollaboration/urls.py +++ b/structuredcollaboration/urls.py @@ -1,4 +1,4 @@ -from django.conf.urls.defaults import patterns, url +from django.conf.urls import patterns, url urlpatterns = patterns( 'structuredcollaboration.views', From 82a06b8a101fb15a7834ac5cd63ffad0768bfc90 Mon Sep 17 00:00:00 2001 From: c0cky Date: Sun, 21 Dec 2014 01:11:46 -0500 Subject: [PATCH 19/58] add refresh button with spring2015 compatability --- mediathread/templates/taxonomy/taxonomy.html | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/mediathread/templates/taxonomy/taxonomy.html b/mediathread/templates/taxonomy/taxonomy.html index 102427734..18e22281d 100644 --- a/mediathread/templates/taxonomy/taxonomy.html +++ b/mediathread/templates/taxonomy/taxonomy.html @@ -39,7 +39,7 @@
  • <% } %> - <% if (vocabularies.length < 3) { %> + <% if (vocabularies.length < 100) { %>
  • @@ -64,7 +64,19 @@ <% for (var i=0; i < vocabularies.length; i++) { %>
    -

    <%=vocabularies[i].display_name%> Concept

    + + +

    + + refresh + + + refresh + +
    +

    + +

    <%=vocabularies[i].display_name%> Concept

    Terms

    @@ -109,7 +121,7 @@
    <%= vocabularies[i].term_set[j].display_name %>
    <% } %> - <% if (vocabularies.length < 3) { %> + <% if (vocabularies.length < 100) { %>
    <% } %>
    From df83dc64f9943daa351e6df539ea93c3e2b69b7b Mon Sep 17 00:00:00 2001 From: c0cky Date: Sun, 21 Dec 2014 01:27:06 -0500 Subject: [PATCH 20/58] removed debug print statements --- mediathread/taxonomy/api.py | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/mediathread/taxonomy/api.py b/mediathread/taxonomy/api.py index 05cd0ad20..ad0270281 100644 --- a/mediathread/taxonomy/api.py +++ b/mediathread/taxonomy/api.py @@ -116,9 +116,6 @@ class VocabularyResource(ModelResource): class Meta: queryset = Vocabulary.objects.all().order_by('id') - print 'queryset' - print queryset - print 'end queryset' list_allowed_methods = ['get', 'post'] detail_allowed_methods = ['get', 'put', 'delete'] authentication = ClassLevelAuthentication() @@ -128,56 +125,34 @@ class Meta: validation = VocabularyValidation() def alter_list_data_to_serialize(self, request, to_be_serialized): - print "entering alter_list_data" to_be_serialized['objects'] = sorted( to_be_serialized['objects'], key=lambda bundle: bundle.data['display_name']) - print to_be_serialized - print "leaving alter_list_data" return to_be_serialized def dehydrate(self, bundle): - print "entering dehydratei" - print bundle.obj.content_type.id - print bundle.data bundle.data['content_type_id'] = bundle.obj.content_type.id - print bundle.data['content_type_id'] - print bundle - print "leaving dehydrate" return bundle def hydrate(self, bundle): - print "entering hydrate" - print bundle.obj - print ContentType.objects bundle.obj.content_type = ContentType.objects.get( id=bundle.data['content_type_id']) bundle.obj.course = Course.objects.get(id=bundle.data['object_id']) - print bundle - print "leaving hydrate" return bundle def render_one(self, request, vocabulary): - print "render_one" bundle = self.build_bundle(obj=vocabulary, request=request) dehydrated = self.full_dehydrate(bundle) - print bundle - print dehydrated - print "leaving render_one" return self._meta.serializer.to_simple(dehydrated, None) def render_list(self, request, vocabularies): - print "entering render_list" data = [] for vocabulary in vocabularies: data.append(self.render_one(request, vocabulary)) - print data - print "leaving render_list" return data def render_related(self, request, object_list): - print "entering render_related" if len(object_list) < 1: return [] @@ -208,6 +183,4 @@ def render_related(self, request, object_list): values.sort(lambda a, b: cmp(a['display_name'].lower(), b['display_name'].lower())) - print values - print "leaving render_related" return values From a6b1fdecf461ceca934fa04073265196e4b66dd0 Mon Sep 17 00:00:00 2001 From: c0cky Date: Sun, 21 Dec 2014 16:51:07 -0500 Subject: [PATCH 21/58] fixed unit tests --- mediathread/assetmgr/api.py | 58 +- mediathread/assetmgr/views.py | 82 +- mediathread/main/fixtures/sample_course.json | 2916 ----------------- .../main/fixtures/unittest_sample_course.json | 1253 ------- .../fixtures/unittest_sample_projects.json | 240 -- mediathread/taxonomy/api.py | 64 +- mediathread/taxonomy/tests/factories.py | 64 - mediathread/taxonomy/tests/test_models.py | 23 - mediathread/taxonomy/views.py | 7 +- .../main/course_request_success.html | 9 - 10 files changed, 108 insertions(+), 4608 deletions(-) delete mode 100644 mediathread/main/fixtures/sample_course.json delete mode 100644 mediathread/main/fixtures/unittest_sample_course.json delete mode 100644 mediathread/main/fixtures/unittest_sample_projects.json delete mode 100644 mediathread/taxonomy/tests/factories.py delete mode 100644 mediathread/taxonomy/tests/test_models.py delete mode 100644 mediathread/templates/main/course_request_success.html diff --git a/mediathread/assetmgr/api.py b/mediathread/assetmgr/api.py index e425f14a6..60130abf7 100644 --- a/mediathread/assetmgr/api.py +++ b/mediathread/assetmgr/api.py @@ -16,7 +16,7 @@ class AssetResource(ModelResource): class Meta: queryset = Asset.objects.none() excludes = ['added', 'modified', 'course', - 'active']#, 'metadata_blob'] getting rid of this doesn't break anything + 'active', 'metadata_blob'] list_allowed_methods = [] detail_allowed_methods = [] authentication = ClassLevelAuthentication() @@ -106,33 +106,41 @@ def render_one(self, request, asset, notes=None): except Source.DoesNotExist: return None - def render_list(self, request, me, assets, notes): + def render_list(self, request, record_owner, record_viewer, assets, notes): note_resource = SherdNoteResource() ctx = {} for note in notes.all(): - if note.asset.id not in ctx: - abundle = self.build_bundle(obj=note.asset, request=request) - dehydrated = self.full_dehydrate(abundle) - asset_ctx = self._meta.serializer.to_simple(dehydrated, None) - ctx[note.asset.id] = asset_ctx - - is_global = note.is_global_annotation() - if not is_global: - ctx[note.asset.id]['annotation_count'] += 1 - if note.author == me: - ctx[note.asset.id]['my_annotation_count'] += 1 - - if note.modified > note.asset.modified: - ctx[note.asset.id]['modified'] = \ - self.format_time(note.modified) - - if self.include_annotations: - note_ctx = note_resource.render_one(request, note, "") - - if is_global: - ctx[note.asset.id]['global_annotation'] = note_ctx - else: - ctx[note.asset.id]['annotations'].append(note_ctx) + try: + note.asset.primary + + if note.asset.id not in ctx: + abundle = self.build_bundle(obj=note.asset, + request=request) + dehydrated = self.full_dehydrate(abundle) + asset_ctx = self._meta.serializer.to_simple(dehydrated, + None) + ctx[note.asset.id] = asset_ctx + + is_global = note.is_global_annotation() + if not is_global: + ctx[note.asset.id]['annotation_count'] += 1 + if note.author == record_viewer: + ctx[note.asset.id]['my_annotation_count'] += 1 + + if note.modified > note.asset.modified: + ctx[note.asset.id]['modified'] = \ + self.format_time(note.modified) + + if self.include_annotations: + note_ctx = note_resource.render_one(request, note, "") + + if is_global: + if note.author == record_owner: + ctx[note.asset.id]['global_annotation'] = note_ctx + else: + ctx[note.asset.id]['annotations'].append(note_ctx) + except Source.DoesNotExist: + pass # don't break in this situation values = ctx.values() return sorted(values, diff --git a/mediathread/assetmgr/views.py b/mediathread/assetmgr/views.py index 70d298459..144f1f848 100644 --- a/mediathread/assetmgr/views.py +++ b/mediathread/assetmgr/views.py @@ -22,9 +22,9 @@ from django.template import RequestContext, loader from django.views.generic.base import View from djangohelpers.lib import allow_http -from mediathread.djangosherd.api import SherdNoteResource -from courseaffils.lib import in_course, in_course_or_404, AUTO_COURSE_SELECT -from mediathread.api import UserResource, TagResource, ClassLevelAuthentication + +from courseaffils.lib import in_course_or_404, in_course, AUTO_COURSE_SELECT +from mediathread.api import UserResource, TagResource from mediathread.assetmgr.api import AssetResource from mediathread.assetmgr.models import Asset, Source from mediathread.discussions.api import DiscussionIndexResource @@ -32,12 +32,10 @@ from mediathread.djangosherd.views import create_annotation, edit_annotation, \ delete_annotation, update_annotation from mediathread.main.models import UserSetting -from mediathread.main.course_details import cached_course_is_faculty from mediathread.mixins import ajax_required, LoggedInMixin, \ JSONResponseMixin, AjaxRequiredMixin, RestrictedMaterialsMixin from mediathread.taxonomy.api import VocabularyResource from mediathread.taxonomy.models import Vocabulary -#from waffle.decorators import waffle_switch @login_required @@ -132,35 +130,33 @@ def asset_create(request): user = _parse_user(request) metadata = _parse_metadata(req_dict) title = req_dict.get('title', '') - asset = Asset.objects.get_by_args(req_dict, asset__course=request.course) + success, asset = Asset.objects.get_by_args(req_dict, + asset__course=request.course) - if asset is False: + if success is False: raise AssertionError("no arguments were supplied to make an asset") if asset is None: + asset = Asset(title=title[:1020], # max title length + course=request.course, + author=user) + asset.save() + + for source in sources_from_args(request, asset).values(): + source.save() + + if "tag" in metadata: + for each_tag in metadata["tag"]: + asset.save_tag(user, each_tag) + + asset.metadata_blob = json.dumps(metadata) + asset.save() try: - asset = Asset(title=title[:1020], # max title length - course=request.course, - author=user) - asset.save() - for source in sources_from_args(request, asset).values(): - if len(source.url) <= 4096: - print source - source.save() - - if "tag" in metadata: - for each_tag in metadata["tag"]: - print user, each_tag - asset.save_tag(user, each_tag) - - asset.metadata_blob = json.dumps(metadata) - asset.save() - - except: + asset.primary # make sure a primary source was specified + except Source.DoesNotExist: # we'll make it here if someone doesn't submit # any primary_labels as arguments - # THIS WILL ALSO HAPPEN IF THE USER IS NOT PART OF THE CLASS # @todo verify the above comment. raise AssertionError("no primary source provided") @@ -242,8 +238,9 @@ def sources_from_args(request, asset=None): sources = {} args = request.REQUEST for key, val in args.items(): - if good_asset_arg(key) and val != '': + if good_asset_arg(key) and val != '' and len(val) < 4096: source = Source(label=key, url=val) + # UGLY non-functional programming for url_processing source.request = request if asset: @@ -260,11 +257,15 @@ def sources_from_args(request, asset=None): source.media_type = the_match[4] sources[key] = source + # iterate the primary labels in order of priority + # pickup the first matching one & use that for lbl in Asset.primary_labels: - if lbl in args: + if lbl in sources: sources[lbl].primary = True - break - return sources + return sources + + # no primary source found, return no sources + return {} @login_required @@ -571,7 +572,8 @@ def get(self, request, asset_id): ctx = {} asset = Asset.objects.filter(pk=asset_id, course=request.course) notes = SherdNote.objects.get_related_notes( - asset, self.record_owner, self.visible_authors) + asset, self.record_owner, self.visible_authors, + self.all_items_are_visible) # tags ctx['tags'] = TagResource().render_related(request, notes) @@ -584,7 +586,6 @@ def get(self, request, asset_id): # projects & discussions title, object_pk, content_type, modified indicies = DiscussionIndex.objects.filter( asset=asset).order_by('-modified') - print DiscussionIndexResource().render_list(request, indicies) ctx.update(DiscussionIndexResource().render_list(request, indicies)) @@ -726,10 +727,11 @@ def get_context(self, request, assets, notes): ctx['citable'] = citable # render the assets - ares = AssetResource(include_annotations=include_annotations) - # extras={'editable': self.viewing_own_records, - # 'citable': citable}) + ares = AssetResource(include_annotations=include_annotations, + extras={'editable': self.viewing_own_records, + 'citable': citable}) ctx['assets'] = ares.render_list(request, + self.record_owner, self.record_viewer, assets, notes) @@ -740,10 +742,11 @@ def add_metadata(self, request, assets): # is displayed in the filtered list. # Not sure this is exactly right...will discuss with team notes = SherdNote.objects.get_related_notes( - assets, self.record_owner or None, self.visible_authors) + assets, self.record_owner or None, self.visible_authors, + self.all_items_are_visible) - tags = TagResource().render_related(request, notes) - vocab = VocabularyResource().render_related(request, notes) + tags = TagResource().render_for_course(request, notes) + vocab = VocabularyResource().render_for_course(request, notes) return {'active_tags': tags, 'active_vocabulary': vocab} def apply_pagination(self, assets, notes, offset, limit): @@ -794,7 +797,8 @@ def get(self, request): assets = Asset.objects.filter(course=request.course) notes = SherdNote.objects.get_related_notes( - assets, self.record_owner or None, self.visible_authors) + assets, self.record_owner or None, self.visible_authors, + self.all_items_are_visible) context = {} if len(notes) > 0: diff --git a/mediathread/main/fixtures/sample_course.json b/mediathread/main/fixtures/sample_course.json deleted file mode 100644 index 02a307d83..000000000 --- a/mediathread/main/fixtures/sample_course.json +++ /dev/null @@ -1,2916 +0,0 @@ -[ - { - "pk": 17, - "model": "contenttypes.contenttype", - "fields": { - "model": "threadedcomment", - "name": "Threaded comment", - "app_label": "threadedcomments" - } - }, - { - "pk": 27, - "model": "contenttypes.contenttype", - "fields": { - "model": "annotation", - "name": "annotation", - "app_label": "djangosherd" - } - }, - { - "pk": 24, - "model": "contenttypes.contenttype", - "fields": { - "model": "asset", - "name": "asset", - "app_label": "assetmgr" - } - }, - { - "pk": 20, - "model": "contenttypes.contenttype", - "fields": { - "model": "collaboration", - "name": "collaboration", - "app_label": "structuredcollaboration" - } - }, - { - "pk": 19, - "model": "contenttypes.contenttype", - "fields": { - "model": "collaborationpolicyrecord", - "name": "collaboration policy record", - "app_label": "structuredcollaboration" - } - }, - { - "pk": 15, - "model": "contenttypes.contenttype", - "fields": { - "model": "comment", - "name": "comment", - "app_label": "comments" - } - }, - { - "pk": 16, - "model": "contenttypes.contenttype", - "fields": { - "model": "commentflag", - "name": "comment flag", - "app_label": "comments" - } - }, - { - "pk": 5, - "model": "contenttypes.contenttype", - "fields": { - "model": "contenttype", - "name": "content type", - "app_label": "contenttypes" - } - }, - { - "pk": 8, - "model": "contenttypes.contenttype", - "fields": { - "model": "course", - "name": "course", - "app_label": "courseaffils" - } - }, - { - "pk": 11, - "model": "contenttypes.contenttype", - "fields": { - "model": "coursedetails", - "name": "course details", - "app_label": "courseaffils" - } - }, - { - "pk": 10, - "model": "contenttypes.contenttype", - "fields": { - "model": "courseinfo", - "name": "course info", - "app_label": "courseaffils" - } - }, - { - "pk": 9, - "model": "contenttypes.contenttype", - "fields": { - "model": "coursesettings", - "name": "course settings", - "app_label": "courseaffils" - } - }, - { - "pk": 29, - "model": "contenttypes.contenttype", - "fields": { - "model": "discussionindex", - "name": "discussion index", - "app_label": "djangosherd" - } - }, - { - "pk": 7, - "model": "contenttypes.contenttype", - "fields": { - "model": "flatpage", - "name": "flat page", - "app_label": "flatpages" - } - }, - { - "pk": 2, - "model": "contenttypes.contenttype", - "fields": { - "model": "group", - "name": "group", - "app_label": "auth" - } - }, - { - "pk": 12, - "model": "contenttypes.contenttype", - "fields": { - "model": "logentry", - "name": "log entry", - "app_label": "admin" - } - }, - { - "pk": 4, - "model": "contenttypes.contenttype", - "fields": { - "model": "message", - "name": "message", - "app_label": "auth" - } - }, - { - "pk": 18, - "model": "contenttypes.contenttype", - "fields": { - "model": "migrationhistory", - "name": "migration history", - "app_label": "south" - } - }, - { - "pk": 1, - "model": "contenttypes.contenttype", - "fields": { - "model": "permission", - "name": "permission", - "app_label": "auth" - } - }, - { - "pk": 21, - "model": "contenttypes.contenttype", - "fields": { - "model": "project", - "name": "project", - "app_label": "projects" - } - }, - { - "pk": 22, - "model": "contenttypes.contenttype", - "fields": { - "model": "projectversion", - "name": "project version", - "app_label": "projects" - } - }, - { - "pk": 6, - "model": "contenttypes.contenttype", - "fields": { - "model": "session", - "name": "session", - "app_label": "sessions" - } - }, - { - "pk": 28, - "model": "contenttypes.contenttype", - "fields": { - "model": "sherdnote", - "name": "sherd note", - "app_label": "djangosherd" - } - }, - { - "pk": 25, - "model": "contenttypes.contenttype", - "fields": { - "model": "source", - "name": "source", - "app_label": "assetmgr" - } - }, - { - "pk": 26, - "model": "contenttypes.contenttype", - "fields": { - "model": "supportedsource", - "name": "supported source", - "app_label": "assetmgr" - } - }, - { - "pk": 13, - "model": "contenttypes.contenttype", - "fields": { - "model": "tag", - "name": "tag", - "app_label": "tagging" - } - }, - { - "pk": 14, - "model": "contenttypes.contenttype", - "fields": { - "model": "taggeditem", - "name": "tagged item", - "app_label": "tagging" - } - }, - { - "pk": 3, - "model": "contenttypes.contenttype", - "fields": { - "model": "user", - "name": "user", - "app_label": "auth" - } - }, - { - "pk": 23, - "model": "contenttypes.contenttype", - "fields": { - "model": "usersetting", - "name": "user setting", - "app_label": "main" - } - }, - { - "pk": 1, - "model": "courseaffils.course", - "fields": { - "faculty_group": 1, - "group": 2, - "title": "Sample Course" - } - }, - { - "pk": 2, - "model": "courseaffils.course", - "fields": { - "faculty_group": 4, - "group": 3, - "title": "Alternate Course" - } - }, - { - "pk": 18, - "model": "admin.logentry", - "fields": { - "action_flag": 2, - "action_time": "2012-12-14 14:53:28", - "object_repr": "test_student_three", - "object_id": "5", - "change_message": "Changed groups.", - "user": 1, - "content_type": 3 - } - }, - { - "pk": 17, - "model": "admin.logentry", - "fields": { - "action_flag": 3, - "action_time": "2012-11-26 13:22:34", - "object_repr": "[archive] The Armory - Home to CCNMTL'S CUMC Office <3> (Sample Course)", - "object_id": "14", - "change_message": "", - "user": 1, - "content_type": 25 - } - }, - { - "pk": 16, - "model": "admin.logentry", - "fields": { - "action_flag": 3, - "action_time": "2012-11-26 13:22:20", - "object_repr": "[archive] MAAP Award Reception <2> (Sample Course)", - "object_id": "9", - "change_message": "", - "user": 1, - "content_type": 25 - } - }, - { - "pk": 15, - "model": "admin.logentry", - "fields": { - "action_flag": 2, - "action_time": "2012-11-18 09:26:25", - "object_repr": "Alternate Course", - "object_id": "2", - "change_message": "Changed add_user.", - "user": 1, - "content_type": 8 - } - }, - { - "pk": 14, - "model": "admin.logentry", - "fields": { - "action_flag": 2, - "action_time": "2012-11-18 09:26:19", - "object_repr": "Sample Course", - "object_id": "1", - "change_message": "Changed add_user.", - "user": 1, - "content_type": 8 - } - }, - { - "pk": 13, - "model": "admin.logentry", - "fields": { - "action_flag": 2, - "action_time": "2012-10-06 13:05:31", - "object_repr": "test_ta", - "object_id": "9", - "change_message": "Changed groups.", - "user": 1, - "content_type": 3 - } - }, - { - "pk": 12, - "model": "admin.logentry", - "fields": { - "action_flag": 2, - "action_time": "2012-10-06 13:05:16", - "object_repr": "test_ta", - "object_id": "9", - "change_message": "Changed first_name, last_name and groups.", - "user": 1, - "content_type": 3 - } - }, - { - "pk": 11, - "model": "admin.logentry", - "fields": { - "action_flag": 1, - "action_time": "2012-10-06 13:04:59", - "object_repr": "test_ta", - "object_id": "9", - "change_message": "", - "user": 1, - "content_type": 3 - } - }, - { - "pk": 10, - "model": "admin.logentry", - "fields": { - "action_flag": 1, - "action_time": "2012-10-06 13:04:48", - "object_repr": "Teacher's Assistant", - "object_id": "5", - "change_message": "", - "user": 1, - "content_type": 2 - } - }, - { - "pk": 9, - "model": "admin.logentry", - "fields": { - "action_flag": 2, - "action_time": "2012-10-06 13:04:32", - "object_repr": "test_staff", - "object_id": "8", - "change_message": "Changed is_staff.", - "user": 1, - "content_type": 3 - } - }, - { - "pk": 8, - "model": "admin.logentry", - "fields": { - "action_flag": 1, - "action_time": "2012-10-06 13:04:16", - "object_repr": "test_staff", - "object_id": "8", - "change_message": "", - "user": 1, - "content_type": 3 - } - }, - { - "pk": 7, - "model": "admin.logentry", - "fields": { - "action_flag": 1, - "action_time": "2012-09-15 08:52:30", - "object_repr": "Alternate Course", - "object_id": "2", - "change_message": "", - "user": 1, - "content_type": 8 - } - }, - { - "pk": 6, - "model": "admin.logentry", - "fields": { - "action_flag": 1, - "action_time": "2012-09-15 08:52:05", - "object_repr": "Alternate Course Faculty", - "object_id": "4", - "change_message": "", - "user": 1, - "content_type": 2 - } - }, - { - "pk": 5, - "model": "admin.logentry", - "fields": { - "action_flag": 1, - "action_time": "2012-09-15 08:51:59", - "object_repr": "Alternate Course Members", - "object_id": "3", - "change_message": "", - "user": 1, - "content_type": 2 - } - }, - { - "pk": 4, - "model": "admin.logentry", - "fields": { - "action_flag": 2, - "action_time": "2012-09-15 08:51:46", - "object_repr": "Sample Course", - "object_id": "1", - "change_message": "Changed add_user.", - "user": 1, - "content_type": 8 - } - }, - { - "pk": 3, - "model": "admin.logentry", - "fields": { - "action_flag": 3, - "action_time": "2012-05-12 14:05:01", - "object_repr": "You Tube <4> (Sample Course)", - "object_id": "4", - "change_message": "", - "user": 1, - "content_type": 24 - } - }, - { - "pk": 2, - "model": "admin.logentry", - "fields": { - "action_flag": 1, - "action_time": "2012-05-12 13:58:45", - "object_repr": "You Tube", - "object_id": "1", - "change_message": "", - "user": 1, - "content_type": 26 - } - }, - { - "pk": 1, - "model": "admin.logentry", - "fields": { - "action_flag": 3, - "action_time": "2012-05-12 12:43:41", - "object_repr": "Behind the Scenes - Marc Raymond <2> (Sample Course)", - "object_id": "2", - "change_message": "", - "user": 1, - "content_type": 24 - } - }, - { - "pk": 3, - "model": "tagging.tag", - "fields": { - "name": "flickr" - } - }, - { - "pk": 2, - "model": "tagging.tag", - "fields": { - "name": "function" - } - }, - { - "pk": 4, - "model": "tagging.tag", - "fields": { - "name": "image" - } - }, - { - "pk": 8, - "model": "tagging.tag", - "fields": { - "name": "instructor_one" - } - }, - { - "pk": 9, - "model": "tagging.tag", - "fields": { - "name": "instructor_one_selection" - } - }, - { - "pk": 12, - "model": "tagging.tag", - "fields": { - "name": "research" - } - }, - { - "pk": 7, - "model": "tagging.tag", - "fields": { - "name": "student_one_item" - } - }, - { - "pk": 6, - "model": "tagging.tag", - "fields": { - "name": "student_one_selection" - } - }, - { - "pk": 15, - "model": "tagging.tag", - "fields": { - "name": "student_three_item" - } - }, - { - "pk": 11, - "model": "tagging.tag", - "fields": { - "name": "student_two_item" - } - }, - { - "pk": 10, - "model": "tagging.tag", - "fields": { - "name": "student_two_selection" - } - }, - { - "pk": 13, - "model": "tagging.tag", - "fields": { - "name": "tag1" - } - }, - { - "pk": 16, - "model": "tagging.tag", - "fields": { - "name": "test_instructor_item" - } - }, - { - "pk": 17, - "model": "tagging.tag", - "fields": { - "name": "test_instructor_selection" - } - }, - { - "pk": 18, - "model": "tagging.tag", - "fields": { - "name": "test_instructor_two" - } - }, - { - "pk": 14, - "model": "tagging.tag", - "fields": { - "name": "test_student_three" - } - }, - { - "pk": 5, - "model": "tagging.tag", - "fields": { - "name": "video" - } - }, - { - "pk": 1, - "model": "tagging.tag", - "fields": { - "name": "youtube" - } - }, - { - "pk": 1, - "model": "tagging.taggeditem", - "fields": { - "tag": 1, - "object_id": 1, - "content_type": 28 - } - }, - { - "pk": 5, - "model": "tagging.taggeditem", - "fields": { - "tag": 4, - "object_id": 5, - "content_type": 28 - } - }, - { - "pk": 7, - "model": "tagging.taggeditem", - "fields": { - "tag": 3, - "object_id": 4, - "content_type": 28 - } - }, - { - "pk": 8, - "model": "tagging.taggeditem", - "fields": { - "tag": 5, - "object_id": 2, - "content_type": 28 - } - }, - { - "pk": 9, - "model": "tagging.taggeditem", - "fields": { - "tag": 5, - "object_id": 3, - "content_type": 28 - } - }, - { - "pk": 10, - "model": "tagging.taggeditem", - "fields": { - "tag": 3, - "object_id": 6, - "content_type": 28 - } - }, - { - "pk": 11, - "model": "tagging.taggeditem", - "fields": { - "tag": 4, - "object_id": 7, - "content_type": 28 - } - }, - { - "pk": 12, - "model": "tagging.taggeditem", - "fields": { - "tag": 6, - "object_id": 8, - "content_type": 28 - } - }, - { - "pk": 13, - "model": "tagging.taggeditem", - "fields": { - "tag": 7, - "object_id": 9, - "content_type": 28 - } - }, - { - "pk": 14, - "model": "tagging.taggeditem", - "fields": { - "tag": 8, - "object_id": 4, - "content_type": 28 - } - }, - { - "pk": 15, - "model": "tagging.taggeditem", - "fields": { - "tag": 9, - "object_id": 5, - "content_type": 28 - } - }, - { - "pk": 16, - "model": "tagging.taggeditem", - "fields": { - "tag": 10, - "object_id": 10, - "content_type": 28 - } - }, - { - "pk": 17, - "model": "tagging.taggeditem", - "fields": { - "tag": 11, - "object_id": 11, - "content_type": 28 - } - }, - { - "pk": 18, - "model": "tagging.taggeditem", - "fields": { - "tag": 12, - "object_id": 13, - "content_type": 28 - } - }, - { - "pk": 19, - "model": "tagging.taggeditem", - "fields": { - "tag": 13, - "object_id": 14, - "content_type": 28 - } - }, - { - "pk": 20, - "model": "tagging.taggeditem", - "fields": { - "tag": 14, - "object_id": 15, - "content_type": 28 - } - }, - { - "pk": 21, - "model": "tagging.taggeditem", - "fields": { - "tag": 15, - "object_id": 16, - "content_type": 28 - } - }, - { - "pk": 22, - "model": "tagging.taggeditem", - "fields": { - "tag": 16, - "object_id": 1, - "content_type": 28 - } - }, - { - "pk": 23, - "model": "tagging.taggeditem", - "fields": { - "tag": 17, - "object_id": 17, - "content_type": 28 - } - }, - { - "pk": 24, - "model": "tagging.taggeditem", - "fields": { - "tag": 18, - "object_id": 19, - "content_type": 28 - } - }, - { - "pk": 2, - "model": "structuredcollaboration.collaboration", - "fields": { - "_order": 2, - "group": 3, - "context": null, - "title": "Alternate Course", - "_policy": null, - "object_pk": "2", - "_parent": null, - "user": null, - "content_type": 8, - "slug": "Alternate_Course" - } - }, - { - "pk": 1, - "model": "structuredcollaboration.collaboration", - "fields": { - "_order": 1, - "group": 2, - "context": null, - "title": "Sample Course", - "_policy": null, - "object_pk": "1", - "_parent": null, - "user": null, - "content_type": 8, - "slug": "Sample_Course" - } - }, - { - "pk": 1, - "model": "main.usersetting", - "fields": { - "user": 6, - "value": "true", - "name": "help_item_detail_view" - } - }, - { - "pk": 2, - "model": "main.usersetting", - "fields": { - "user": 3, - "value": "False", - "name": "help_homepage_classwork_column" - } - }, - { - "pk": 3, - "model": "main.usersetting", - "fields": { - "user": 5, - "value": "true", - "name": "help_item_detail_view" - } - }, - { - "pk": 4, - "model": "main.usersetting", - "fields": { - "user": 10, - "value": "False", - "name": "help_homepage_classwork_column" - } - }, - { - "pk": 5, - "model": "main.usersetting", - "fields": { - "user": 10, - "value": "False", - "name": "help_homepage_instructor_column" - } - }, - { - "pk": 6, - "model": "main.usersetting", - "fields": { - "user": 10, - "value": "true", - "name": "help_item_detail_view" - } - }, - { - "pk": 1, - "model": "south.migrationhistory", - "fields": { - "applied": "2012-05-12 16:25:56", - "app_name": "structuredcollaboration", - "migration": "0001_initial" - } - }, - { - "pk": 2, - "model": "south.migrationhistory", - "fields": { - "applied": "2012-05-12 16:25:56", - "app_name": "projects", - "migration": "0001_initial" - } - }, - { - "pk": 3, - "model": "south.migrationhistory", - "fields": { - "applied": "2012-05-12 16:25:56", - "app_name": "main", - "migration": "0001_initial" - } - }, - { - "pk": 4, - "model": "south.migrationhistory", - "fields": { - "applied": "2012-05-12 16:25:57", - "app_name": "assetmgr", - "migration": "0001_initial" - } - }, - { - "pk": 5, - "model": "south.migrationhistory", - "fields": { - "applied": "2012-05-12 16:25:57", - "app_name": "assetmgr", - "migration": "0002_auto__chg_field_source_url" - } - }, - { - "pk": 6, - "model": "south.migrationhistory", - "fields": { - "applied": "2012-05-12 16:25:57", - "app_name": "djangosherd", - "migration": "0001_initial" - } - }, - { - "pk": 7, - "model": "south.migrationhistory", - "fields": { - "applied": "2012-10-06 17:03:38", - "app_name": "djangosherd", - "migration": "0001_initial" - } - }, - { - "pk": 1, - "model": "assetmgr.asset", - "fields": { - "added": "2012-05-12 12:31:20", - "title": "Mediathread: Introduction", - "metadata_blob": "{\"category\": [\"Education\"], \"description\": [\"Mediathread is CCNMTL's innovative, open-source platform for exploration, analysis, and organization of web-based multimedia content. Mediathread connects to a variety of image and video collections (such as YouTube, Flickr, library databases, and course libraries), enabling users to lift items out of these collections and into an analysis environment. In Mediathread, items can then be clipped, annotated, organized, and embedded into essays and other written analysis.\"], \"author\": [\"CCNMTL\"], \"youtube_link\": [\"http://www.youtube.com/watch?v=7KjzRG8zYYo\"], \"author_uri\": [\"http://gdata.youtube.com/feeds/api/users/CCNMTL\"], \"published\": [\"2012-02-09T00:46:25.000Z\"]}", - "author": 2, - "modified": "2012-05-12 12:31:20", - "course": 1, - "active": true - } - }, - { - "pk": 2, - "model": "assetmgr.asset", - "fields": { - "added": "2012-05-12 12:44:24", - "title": "MAAP Award Reception", - "metadata_blob": "", - "author": 2, - "modified": "2012-05-12 12:44:24", - "course": 1, - "active": true - } - }, - { - "pk": 3, - "model": "assetmgr.asset", - "fields": { - "added": "2012-05-12 12:58:17", - "title": "The Armory - Home to CCNMTL'S CUMC Office", - "metadata_blob": "", - "author": 2, - "modified": "2012-05-12 12:58:17", - "course": 1, - "active": true - } - }, - { - "pk": 4, - "model": "assetmgr.asset", - "fields": { - "added": "2012-09-15 08:53:57", - "title": "Design Research", - "metadata_blob": "", - "author": 6, - "modified": "2012-09-15 08:53:57", - "course": 2, - "active": true - } - }, - { - "pk": 5, - "model": "assetmgr.asset", - "fields": { - "added": "2012-12-18 11:47:04", - "title": "Project Portfolio", - "metadata_blob": "", - "author": 10, - "modified": "2012-12-18 11:47:04", - "course": 1, - "active": true - } - }, - { - "pk": 1, - "model": "assetmgr.source", - "fields": { - "url": "http://www.youtube.com/watch?v=7KjzRG8zYYo&list=%2Fwatch%3Fv%3D7KjzRG8zYYo&list=UU6Qe9fItsCXSWWwiLeN_tBA&index=0&feature=plcp", - "primary": false, - "label": "url", - "width": 0, - "asset": 1, - "modified": "2012-05-12 12:31:20", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 2, - "model": "assetmgr.source", - "fields": { - "url": "http://gdata.youtube.com/feeds/api/videos/7KjzRG8zYYo", - "primary": false, - "label": "gdata", - "width": 0, - "asset": 1, - "modified": "2012-05-12 12:31:20", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 3, - "model": "assetmgr.source", - "fields": { - "url": "http://www.youtube.com/v/7KjzRG8zYYo?enablejsapi=1&fs=1", - "primary": true, - "label": "youtube", - "width": 0, - "asset": 1, - "modified": "2012-05-12 12:31:20", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 4, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/mediathread_introduction_thumb.jpg", - "primary": false, - "label": "thumb", - "width": 120, - "asset": 1, - "modified": "2012-05-12 12:31:20", - "media_type": null, - "height": 90, - "size": 0 - } - }, - { - "pk": 5, - "model": "assetmgr.source", - "fields": { - "url": "bookmarklet", - "primary": false, - "label": "asset-source", - "width": 0, - "asset": 1, - "modified": "2012-05-12 12:31:20", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 6, - "model": "assetmgr.source", - "fields": { - "url": "http://www.flickr.com/photos/ccnmtl/4049410921/", - "primary": false, - "label": "url", - "width": 0, - "asset": 2, - "modified": "2012-05-12 12:44:24", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 7, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/maap.jpg", - "primary": true, - "label": "image", - "width": 2816, - "asset": 2, - "modified": "2012-05-12 12:44:24", - "media_type": null, - "height": 2112, - "size": 0 - } - }, - { - "pk": 8, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/maap_thumb.jpg", - "primary": false, - "label": "thumb", - "width": 0, - "asset": 2, - "modified": "2012-05-12 12:44:24", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 10, - "model": "assetmgr.source", - "fields": { - "url": "bookmarklet", - "primary": false, - "label": "asset-source", - "width": 0, - "asset": 2, - "modified": "2012-05-12 12:44:24", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 11, - "model": "assetmgr.source", - "fields": { - "url": "http://www.flickr.com/photos/ccnmtl/3308199560/", - "primary": false, - "label": "url", - "width": 0, - "asset": 3, - "modified": "2012-05-12 12:58:17", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 12, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/armory.jpg/", - "primary": true, - "label": "image", - "width": 1430, - "asset": 3, - "modified": "2012-05-12 12:58:17", - "media_type": null, - "height": 703, - "size": 0 - } - }, - { - "pk": 13, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/armory_thumb.jpg", - "primary": false, - "label": "thumb", - "width": 0, - "asset": 3, - "modified": "2012-05-12 12:58:17", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 15, - "model": "assetmgr.source", - "fields": { - "url": "bookmarklet", - "primary": false, - "label": "asset-source", - "width": 0, - "asset": 3, - "modified": "2012-05-12 12:58:17", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 16, - "model": "assetmgr.source", - "fields": { - "url": "http://ccnmtl.columbia.edu/", - "primary": false, - "label": "url", - "width": 0, - "asset": 4, - "modified": "2012-09-15 08:53:57", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 17, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/dr.jpg", - "primary": true, - "label": "image", - "width": 768, - "asset": 4, - "modified": "2012-09-15 08:53:57", - "media_type": null, - "height": 230, - "size": 0 - } - }, - { - "pk": 18, - "model": "assetmgr.source", - "fields": { - "url": "bookmarklet", - "primary": false, - "label": "asset-source", - "width": 0, - "asset": 4, - "modified": "2012-09-15 08:53:57", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 19, - "model": "assetmgr.source", - "fields": { - "url": "http://ccnmtl.columbia.edu/", - "primary": false, - "label": "url", - "width": 0, - "asset": 5, - "modified": "2012-12-18 11:47:04", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 20, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/portfolio.jpg", - "primary": true, - "label": "image", - "width": 768, - "asset": 5, - "modified": "2012-12-18 11:47:04", - "media_type": null, - "height": 230, - "size": 0 - } - }, - { - "pk": 21, - "model": "assetmgr.source", - "fields": { - "url": "bookmarklet", - "primary": false, - "label": "asset-source", - "width": 0, - "asset": 5, - "modified": "2012-12-18 11:47:04", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 1, - "model": "assetmgr.supportedsource", - "fields": { - "archive_url": "http://youtube.com", - "description": "The largest collection of video on the web, where you can also upload video yourself.", - "thumb_url": "/media/img/thumbs/youtube.jpg", - "title": "You Tube" - } - }, - { - "pk": 1, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 2, - "model": "djangosherd.annotation", - "fields": { - "range2": 39.0, - "range1": 28.0, - "annotation_data": "{\"startCode\":\"00:00:28\",\"endCode\":\"00:00:39\",\"duration\":0,\"timeScale\":1,\"start\":28,\"end\":39}" - } - }, - { - "pk": 3, - "model": "djangosherd.annotation", - "fields": { - "range2": 75.0, - "range1": 43.0, - "annotation_data": "{\"startCode\":\"00:00:43\",\"endCode\":\"00:01:15\",\"duration\":0,\"timeScale\":1,\"start\":43,\"end\":75}" - } - }, - { - "pk": 4, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 5, - "model": "djangosherd.annotation", - "fields": { - "range2": 23.0, - "range1": -4.5, - "annotation_data": "{\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-108,-22],[-108,68],[98.99999999999999,68],[98.99999999999999,-22],[-108,-22]]]},\"default\":false,\"x\":-4.5,\"y\":23,\"zoom\":1,\"extent\":[-120,-90,120,90]}" - } - }, - { - "pk": 6, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 7, - "model": "djangosherd.annotation", - "fields": { - "range2": 0.0, - "range1": 0.0, - "annotation_data": "{\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-167.5,2],[-167.5,80.5],[-95.5,80.5],[-95.5,2],[-167.5,2]]]},\"default\":false,\"x\":0,\"y\":0,\"zoom\":2,\"extent\":[-180,-89,180,89]}" - } - }, - { - "pk": 8, - "model": "djangosherd.annotation", - "fields": { - "range2": 0.0, - "range1": 0.0, - "annotation_data": "{\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-58,-90],[-58,-14],[49,-14],[49,-90],[-58,-90]]]},\"default\":false,\"x\":0,\"y\":0,\"zoom\":2,\"extent\":[-120,-90,120,90]}" - } - }, - { - "pk": 9, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 10, - "model": "djangosherd.annotation", - "fields": { - "range2": 0.0, - "range1": 0.0, - "annotation_data": "{\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[38.5,-91],[38.5,2.5],[61.5,2.500000000000007],[61.5,-91],[38.5,-91]]]},\"default\":false,\"x\":0,\"y\":0,\"zoom\":2,\"extent\":[-120,-90,120,90]}" - } - }, - { - "pk": 11, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 12, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 13, - "model": "djangosherd.annotation", - "fields": { - "range2": -9.25, - "range1": 33.375, - "annotation_data": "{\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[14.249999999999993,-17.512500762939],[14.249999999999993,17.112499237061],[60.375,17.112499237061],[60.375000000000014,-17.512500762939],[14.249999999999993,-17.512500762939]]]},\"default\":false,\"x\":33.375,\"y\":-9.25,\"zoom\":4,\"extent\":[-180,-54,180,54]}" - } - }, - { - "pk": 14, - "model": "djangosherd.annotation", - "fields": { - "range2": 6.75, - "range1": 116.25, - "annotation_data": "{\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[101,0.22499847412108664],[101,44.224998474120994],[142.25,44.22499847412101],[142.25,0.22499847412108664],[101,0.22499847412108664]]]},\"default\":false,\"x\":116.25,\"y\":6.75,\"zoom\":3,\"extent\":[-180,-54,180,54]}" - } - }, - { - "pk": 15, - "model": "djangosherd.annotation", - "fields": { - "range2": 0.0, - "range1": 0.0, - "annotation_data": "{\"default\":true,\"x\":0,\"y\":0,\"zoom\":1,\"extent\":[-180,-54,180,54]}" - } - }, - { - "pk": 16, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 17, - "model": "djangosherd.annotation", - "fields": { - "range2": 0.0, - "range1": 0.0, - "annotation_data": "{\"startCode\":\"00:00:00\",\"endCode\":\"00:00:00\",\"duration\":0,\"timeScale\":1,\"start\":0,\"end\":0}" - } - }, - { - "pk": 18, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 19, - "model": "djangosherd.annotation", - "fields": { - "range2": 120.0, - "range1": 60.0, - "annotation_data": "{\"startCode\":\"00:01:00\",\"endCode\":\"00:02:00\",\"duration\":171,\"timeScale\":1,\"start\":60,\"end\":120}" - } - }, - { - "pk": 20, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 1, - "model": "djangosherd.sherdnote", - "fields": { - "body": "All credit to Mark and Casey", - "added": "2012-05-12 12:32:07", - "author": 2, - "tags": ",youtube, test_instructor_item", - "modified": "2012-12-01 11:57:53", - "asset": 1, - "title": null - } - }, - { - "pk": 2, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-05-12 12:37:15", - "author": 2, - "tags": ",video", - "modified": "2012-05-12 12:51:22", - "asset": 1, - "title": "Manage Sources" - } - }, - { - "pk": 3, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-05-12 12:38:33", - "author": 2, - "tags": ",video", - "modified": "2012-05-12 12:51:30", - "asset": 1, - "title": "Annotations" - } - }, - { - "pk": 4, - "model": "djangosherd.sherdnote", - "fields": { - "body": "instructor one item note", - "added": "2012-05-12 12:47:33", - "author": 2, - "tags": ",flickr, instructor_one", - "modified": "2012-07-29 09:57:23", - "asset": 2, - "title": null - } - }, - { - "pk": 5, - "model": "djangosherd.sherdnote", - "fields": { - "body": "instructor one selection note", - "added": "2012-05-12 12:51:04", - "author": 2, - "tags": ",image, instructor_one_selection, ", - "modified": "2012-07-29 09:57:15", - "asset": 2, - "title": "Our esteemed leaders" - } - }, - { - "pk": 6, - "model": "djangosherd.sherdnote", - "fields": { - "body": "This is for deletion purposes", - "added": "2012-05-12 12:58:33", - "author": 2, - "tags": ",flickr", - "modified": "2012-05-12 12:58:33", - "asset": 3, - "title": null - } - }, - { - "pk": 7, - "model": "djangosherd.sherdnote", - "fields": { - "body": "This is for deletion.", - "added": "2012-05-12 12:58:49", - "author": 2, - "tags": ",image", - "modified": "2012-05-12 12:58:49", - "asset": 3, - "title": "Left Corner" - } - }, - { - "pk": 8, - "model": "djangosherd.sherdnote", - "fields": { - "body": "student one selection note", - "added": "2012-07-29 09:56:02", - "author": 3, - "tags": ",student_one_selection", - "modified": "2012-07-29 09:56:02", - "asset": 2, - "title": "The Award" - } - }, - { - "pk": 9, - "model": "djangosherd.sherdnote", - "fields": { - "body": "student one item note", - "added": "2012-07-29 09:56:02", - "author": 3, - "tags": ",student_one_item", - "modified": "2012-07-29 09:56:14", - "asset": 2, - "title": null - } - }, - { - "pk": 10, - "model": "djangosherd.sherdnote", - "fields": { - "body": "student two selection note", - "added": "2012-07-29 09:58:30", - "author": 4, - "tags": ",student_two_selection", - "modified": "2012-07-29 09:58:30", - "asset": 2, - "title": "Nice Tie" - } - }, - { - "pk": 11, - "model": "djangosherd.sherdnote", - "fields": { - "body": "student two item note", - "added": "2012-07-29 09:58:30", - "author": 4, - "tags": ",student_two_item", - "modified": "2012-07-29 09:58:39", - "asset": 2, - "title": null - } - }, - { - "pk": 12, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-09-15 08:53:57", - "author": 6, - "tags": "", - "modified": "2012-09-15 08:53:57", - "asset": 4, - "title": null - } - }, - { - "pk": 13, - "model": "djangosherd.sherdnote", - "fields": { - "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", - "added": "2012-09-15 08:55:29", - "author": 6, - "tags": ",research", - "modified": "2012-09-15 08:55:29", - "asset": 4, - "title": "Research and Evaluation" - } - }, - { - "pk": 14, - "model": "djangosherd.sherdnote", - "fields": { - "body": "Phasellus sed purus et nibh vehicula scelerisque at quis orci. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed dui orci, consequat in fermentum vel, congue eu nulla. ", - "added": "2012-09-15 08:56:00", - "author": 6, - "tags": ",tag1", - "modified": "2012-09-15 08:56:00", - "asset": 4, - "title": "Curricular Context" - } - }, - { - "pk": 15, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-12-01 11:01:01", - "author": 5, - "tags": ",test_student_three", - "modified": "2012-12-01 11:01:01", - "asset": 4, - "title": "Whole Item Selection" - } - }, - { - "pk": 16, - "model": "djangosherd.sherdnote", - "fields": { - "body": "student three item note", - "added": "2012-12-01 11:01:01", - "author": 5, - "tags": ",student_three_item", - "modified": "2012-12-01 11:10:47", - "asset": 4, - "title": null - } - }, - { - "pk": 17, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-12-01 11:58:08", - "author": 2, - "tags": ",test_instructor_selection", - "modified": "2012-12-01 11:58:08", - "asset": 1, - "title": "Whole Item Selection" - } - }, - { - "pk": 18, - "model": "djangosherd.sherdnote", - "fields": { - "body": "test_instructor_two notes", - "added": "2012-12-18 11:47:04", - "author": 10, - "tags": ",test_instructor_two", - "modified": "2012-12-18 11:47:04", - "asset": 5, - "title": null - } - }, - { - "pk": 19, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-12-18 11:49:21", - "author": 10, - "tags": ",test_instructor_two", - "modified": "2012-12-18 11:49:21", - "asset": 1, - "title": "Video Selection Is Time-based" - } - }, - { - "pk": 20, - "model": "djangosherd.sherdnote", - "fields": { - "body": "test_instructor_two notes", - "added": "2012-12-18 11:49:22", - "author": 10, - "tags": ",test_instructor_two", - "modified": "2012-12-18 11:49:22", - "asset": 1, - "title": null - } - }, - { - "pk": 1, - "model": "djangosherd.discussionindex", - "fields": { - "comment": null, - "collaboration": 3, - "participant": null, - "asset": 5, - "modified": "2012-12-18 11:48:16" - } - }, - { - "pk": 34, - "model": "auth.permission", - "fields": { - "codename": "add_logentry", - "name": "Can add log entry", - "content_type": 12 - } - }, - { - "pk": 35, - "model": "auth.permission", - "fields": { - "codename": "change_logentry", - "name": "Can change log entry", - "content_type": 12 - } - }, - { - "pk": 36, - "model": "auth.permission", - "fields": { - "codename": "delete_logentry", - "name": "Can delete log entry", - "content_type": 12 - } - }, - { - "pk": 71, - "model": "auth.permission", - "fields": { - "codename": "add_asset", - "name": "Can add asset", - "content_type": 24 - } - }, - { - "pk": 99, - "model": "auth.permission", - "fields": { - "codename": "can_upload_for", - "name": "Can upload for other users", - "content_type": 24 - } - }, - { - "pk": 72, - "model": "auth.permission", - "fields": { - "codename": "change_asset", - "name": "Can change asset", - "content_type": 24 - } - }, - { - "pk": 73, - "model": "auth.permission", - "fields": { - "codename": "delete_asset", - "name": "Can delete asset", - "content_type": 24 - } - }, - { - "pk": 74, - "model": "auth.permission", - "fields": { - "codename": "add_source", - "name": "Can add source", - "content_type": 25 - } - }, - { - "pk": 75, - "model": "auth.permission", - "fields": { - "codename": "change_source", - "name": "Can change source", - "content_type": 25 - } - }, - { - "pk": 76, - "model": "auth.permission", - "fields": { - "codename": "delete_source", - "name": "Can delete source", - "content_type": 25 - } - }, - { - "pk": 77, - "model": "auth.permission", - "fields": { - "codename": "add_supportedsource", - "name": "Can add supported source", - "content_type": 26 - } - }, - { - "pk": 78, - "model": "auth.permission", - "fields": { - "codename": "change_supportedsource", - "name": "Can change supported source", - "content_type": 26 - } - }, - { - "pk": 79, - "model": "auth.permission", - "fields": { - "codename": "delete_supportedsource", - "name": "Can delete supported source", - "content_type": 26 - } - }, - { - "pk": 4, - "model": "auth.permission", - "fields": { - "codename": "add_group", - "name": "Can add group", - "content_type": 2 - } - }, - { - "pk": 5, - "model": "auth.permission", - "fields": { - "codename": "change_group", - "name": "Can change group", - "content_type": 2 - } - }, - { - "pk": 6, - "model": "auth.permission", - "fields": { - "codename": "delete_group", - "name": "Can delete group", - "content_type": 2 - } - }, - { - "pk": 10, - "model": "auth.permission", - "fields": { - "codename": "add_message", - "name": "Can add message", - "content_type": 4 - } - }, - { - "pk": 11, - "model": "auth.permission", - "fields": { - "codename": "change_message", - "name": "Can change message", - "content_type": 4 - } - }, - { - "pk": 12, - "model": "auth.permission", - "fields": { - "codename": "delete_message", - "name": "Can delete message", - "content_type": 4 - } - }, - { - "pk": 1, - "model": "auth.permission", - "fields": { - "codename": "add_permission", - "name": "Can add permission", - "content_type": 1 - } - }, - { - "pk": 2, - "model": "auth.permission", - "fields": { - "codename": "change_permission", - "name": "Can change permission", - "content_type": 1 - } - }, - { - "pk": 3, - "model": "auth.permission", - "fields": { - "codename": "delete_permission", - "name": "Can delete permission", - "content_type": 1 - } - }, - { - "pk": 7, - "model": "auth.permission", - "fields": { - "codename": "add_user", - "name": "Can add user", - "content_type": 3 - } - }, - { - "pk": 8, - "model": "auth.permission", - "fields": { - "codename": "change_user", - "name": "Can change user", - "content_type": 3 - } - }, - { - "pk": 9, - "model": "auth.permission", - "fields": { - "codename": "delete_user", - "name": "Can delete user", - "content_type": 3 - } - }, - { - "pk": 43, - "model": "auth.permission", - "fields": { - "codename": "add_comment", - "name": "Can add comment", - "content_type": 15 - } - }, - { - "pk": 46, - "model": "auth.permission", - "fields": { - "codename": "can_moderate", - "name": "Can moderate comments", - "content_type": 15 - } - }, - { - "pk": 44, - "model": "auth.permission", - "fields": { - "codename": "change_comment", - "name": "Can change comment", - "content_type": 15 - } - }, - { - "pk": 45, - "model": "auth.permission", - "fields": { - "codename": "delete_comment", - "name": "Can delete comment", - "content_type": 15 - } - }, - { - "pk": 47, - "model": "auth.permission", - "fields": { - "codename": "add_commentflag", - "name": "Can add comment flag", - "content_type": 16 - } - }, - { - "pk": 48, - "model": "auth.permission", - "fields": { - "codename": "change_commentflag", - "name": "Can change comment flag", - "content_type": 16 - } - }, - { - "pk": 49, - "model": "auth.permission", - "fields": { - "codename": "delete_commentflag", - "name": "Can delete comment flag", - "content_type": 16 - } - }, - { - "pk": 13, - "model": "auth.permission", - "fields": { - "codename": "add_contenttype", - "name": "Can add content type", - "content_type": 5 - } - }, - { - "pk": 14, - "model": "auth.permission", - "fields": { - "codename": "change_contenttype", - "name": "Can change content type", - "content_type": 5 - } - }, - { - "pk": 15, - "model": "auth.permission", - "fields": { - "codename": "delete_contenttype", - "name": "Can delete content type", - "content_type": 5 - } - }, - { - "pk": 22, - "model": "auth.permission", - "fields": { - "codename": "add_course", - "name": "Can add course", - "content_type": 8 - } - }, - { - "pk": 23, - "model": "auth.permission", - "fields": { - "codename": "change_course", - "name": "Can change course", - "content_type": 8 - } - }, - { - "pk": 24, - "model": "auth.permission", - "fields": { - "codename": "delete_course", - "name": "Can delete course", - "content_type": 8 - } - }, - { - "pk": 31, - "model": "auth.permission", - "fields": { - "codename": "add_coursedetails", - "name": "Can add course details", - "content_type": 11 - } - }, - { - "pk": 32, - "model": "auth.permission", - "fields": { - "codename": "change_coursedetails", - "name": "Can change course details", - "content_type": 11 - } - }, - { - "pk": 33, - "model": "auth.permission", - "fields": { - "codename": "delete_coursedetails", - "name": "Can delete course details", - "content_type": 11 - } - }, - { - "pk": 28, - "model": "auth.permission", - "fields": { - "codename": "add_courseinfo", - "name": "Can add course info", - "content_type": 10 - } - }, - { - "pk": 29, - "model": "auth.permission", - "fields": { - "codename": "change_courseinfo", - "name": "Can change course info", - "content_type": 10 - } - }, - { - "pk": 30, - "model": "auth.permission", - "fields": { - "codename": "delete_courseinfo", - "name": "Can delete course info", - "content_type": 10 - } - }, - { - "pk": 25, - "model": "auth.permission", - "fields": { - "codename": "add_coursesettings", - "name": "Can add course settings", - "content_type": 9 - } - }, - { - "pk": 26, - "model": "auth.permission", - "fields": { - "codename": "change_coursesettings", - "name": "Can change course settings", - "content_type": 9 - } - }, - { - "pk": 27, - "model": "auth.permission", - "fields": { - "codename": "delete_coursesettings", - "name": "Can delete course settings", - "content_type": 9 - } - }, - { - "pk": 80, - "model": "auth.permission", - "fields": { - "codename": "add_annotation", - "name": "Can add annotation", - "content_type": 27 - } - }, - { - "pk": 81, - "model": "auth.permission", - "fields": { - "codename": "change_annotation", - "name": "Can change annotation", - "content_type": 27 - } - }, - { - "pk": 82, - "model": "auth.permission", - "fields": { - "codename": "delete_annotation", - "name": "Can delete annotation", - "content_type": 27 - } - }, - { - "pk": 86, - "model": "auth.permission", - "fields": { - "codename": "add_discussionindex", - "name": "Can add discussion index", - "content_type": 29 - } - }, - { - "pk": 87, - "model": "auth.permission", - "fields": { - "codename": "change_discussionindex", - "name": "Can change discussion index", - "content_type": 29 - } - }, - { - "pk": 88, - "model": "auth.permission", - "fields": { - "codename": "delete_discussionindex", - "name": "Can delete discussion index", - "content_type": 29 - } - }, - { - "pk": 83, - "model": "auth.permission", - "fields": { - "codename": "add_sherdnote", - "name": "Can add sherd note", - "content_type": 28 - } - }, - { - "pk": 84, - "model": "auth.permission", - "fields": { - "codename": "change_sherdnote", - "name": "Can change sherd note", - "content_type": 28 - } - }, - { - "pk": 85, - "model": "auth.permission", - "fields": { - "codename": "delete_sherdnote", - "name": "Can delete sherd note", - "content_type": 28 - } - }, - { - "pk": 19, - "model": "auth.permission", - "fields": { - "codename": "add_flatpage", - "name": "Can add flat page", - "content_type": 7 - } - }, - { - "pk": 20, - "model": "auth.permission", - "fields": { - "codename": "change_flatpage", - "name": "Can change flat page", - "content_type": 7 - } - }, - { - "pk": 21, - "model": "auth.permission", - "fields": { - "codename": "delete_flatpage", - "name": "Can delete flat page", - "content_type": 7 - } - }, - { - "pk": 68, - "model": "auth.permission", - "fields": { - "codename": "add_usersetting", - "name": "Can add user setting", - "content_type": 23 - } - }, - { - "pk": 69, - "model": "auth.permission", - "fields": { - "codename": "change_usersetting", - "name": "Can change user setting", - "content_type": 23 - } - }, - { - "pk": 70, - "model": "auth.permission", - "fields": { - "codename": "delete_usersetting", - "name": "Can delete user setting", - "content_type": 23 - } - }, - { - "pk": 62, - "model": "auth.permission", - "fields": { - "codename": "add_project", - "name": "Can add project", - "content_type": 21 - } - }, - { - "pk": 63, - "model": "auth.permission", - "fields": { - "codename": "change_project", - "name": "Can change project", - "content_type": 21 - } - }, - { - "pk": 64, - "model": "auth.permission", - "fields": { - "codename": "delete_project", - "name": "Can delete project", - "content_type": 21 - } - }, - { - "pk": 65, - "model": "auth.permission", - "fields": { - "codename": "add_projectversion", - "name": "Can add project version", - "content_type": 22 - } - }, - { - "pk": 66, - "model": "auth.permission", - "fields": { - "codename": "change_projectversion", - "name": "Can change project version", - "content_type": 22 - } - }, - { - "pk": 67, - "model": "auth.permission", - "fields": { - "codename": "delete_projectversion", - "name": "Can delete project version", - "content_type": 22 - } - }, - { - "pk": 16, - "model": "auth.permission", - "fields": { - "codename": "add_session", - "name": "Can add session", - "content_type": 6 - } - }, - { - "pk": 17, - "model": "auth.permission", - "fields": { - "codename": "change_session", - "name": "Can change session", - "content_type": 6 - } - }, - { - "pk": 18, - "model": "auth.permission", - "fields": { - "codename": "delete_session", - "name": "Can delete session", - "content_type": 6 - } - }, - { - "pk": 53, - "model": "auth.permission", - "fields": { - "codename": "add_migrationhistory", - "name": "Can add migration history", - "content_type": 18 - } - }, - { - "pk": 54, - "model": "auth.permission", - "fields": { - "codename": "change_migrationhistory", - "name": "Can change migration history", - "content_type": 18 - } - }, - { - "pk": 55, - "model": "auth.permission", - "fields": { - "codename": "delete_migrationhistory", - "name": "Can delete migration history", - "content_type": 18 - } - }, - { - "pk": 59, - "model": "auth.permission", - "fields": { - "codename": "add_collaboration", - "name": "Can add collaboration", - "content_type": 20 - } - }, - { - "pk": 60, - "model": "auth.permission", - "fields": { - "codename": "change_collaboration", - "name": "Can change collaboration", - "content_type": 20 - } - }, - { - "pk": 61, - "model": "auth.permission", - "fields": { - "codename": "delete_collaboration", - "name": "Can delete collaboration", - "content_type": 20 - } - }, - { - "pk": 56, - "model": "auth.permission", - "fields": { - "codename": "add_collaborationpolicyrecord", - "name": "Can add collaboration policy record", - "content_type": 19 - } - }, - { - "pk": 57, - "model": "auth.permission", - "fields": { - "codename": "change_collaborationpolicyrecord", - "name": "Can change collaboration policy record", - "content_type": 19 - } - }, - { - "pk": 58, - "model": "auth.permission", - "fields": { - "codename": "delete_collaborationpolicyrecord", - "name": "Can delete collaboration policy record", - "content_type": 19 - } - }, - { - "pk": 37, - "model": "auth.permission", - "fields": { - "codename": "add_tag", - "name": "Can add tag", - "content_type": 13 - } - }, - { - "pk": 38, - "model": "auth.permission", - "fields": { - "codename": "change_tag", - "name": "Can change tag", - "content_type": 13 - } - }, - { - "pk": 39, - "model": "auth.permission", - "fields": { - "codename": "delete_tag", - "name": "Can delete tag", - "content_type": 13 - } - }, - { - "pk": 40, - "model": "auth.permission", - "fields": { - "codename": "add_taggeditem", - "name": "Can add tagged item", - "content_type": 14 - } - }, - { - "pk": 41, - "model": "auth.permission", - "fields": { - "codename": "change_taggeditem", - "name": "Can change tagged item", - "content_type": 14 - } - }, - { - "pk": 42, - "model": "auth.permission", - "fields": { - "codename": "delete_taggeditem", - "name": "Can delete tagged item", - "content_type": 14 - } - }, - { - "pk": 50, - "model": "auth.permission", - "fields": { - "codename": "add_threadedcomment", - "name": "Can add Threaded comment", - "content_type": 17 - } - }, - { - "pk": 51, - "model": "auth.permission", - "fields": { - "codename": "change_threadedcomment", - "name": "Can change Threaded comment", - "content_type": 17 - } - }, - { - "pk": 52, - "model": "auth.permission", - "fields": { - "codename": "delete_threadedcomment", - "name": "Can delete Threaded comment", - "content_type": 17 - } - }, - { - "pk": 1, - "model": "auth.group", - "fields": { - "name": "Sample_Course_Faculty", - "permissions": [ - 34, - 35, - 36, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 4, - 5, - 6, - 10, - 11, - 12, - 1, - 2, - 3, - 7, - 8, - 9, - 43, - 46, - 44, - 45, - 47, - 48, - 49, - 13, - 14, - 15, - 22, - 23, - 24, - 31, - 32, - 33, - 28, - 29, - 30, - 25, - 26, - 27, - 80, - 81, - 82, - 86, - 87, - 88, - 83, - 84, - 85, - 19, - 20, - 21, - 68, - 69, - 70, - 62, - 63, - 64, - 65, - 66, - 67, - 16, - 17, - 18, - 53, - 54, - 55, - 59, - 60, - 61, - 56, - 57, - 58, - 37, - 38, - 39, - 40, - 41, - 42, - 50, - 51, - 52 - ] - } - }, - { - "pk": 2, - "model": "auth.group", - "fields": { - "name": "Sample_Course_Students", - "permissions": [] - } - }, - { - "pk": 3, - "model": "auth.group", - "fields": { - "name": "Alternate Course Members", - "permissions": [] - } - }, - { - "pk": 4, - "model": "auth.group", - "fields": { - "name": "Alternate Course Faculty", - "permissions": [] - } - }, - { - "pk": 5, - "model": "auth.group", - "fields": { - "name": "Teacher's Assistant", - "permissions": [ - 99 - ] - } - }, - { - "pk": 1, - "model": "auth.user", - "fields": { - "username": "selenium", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": true, - "is_staff": true, - "last_login": "2012-12-14 14:53:12", - "groups": [], - "user_permissions": [], - "password": "sha1$03e73$f0d7f55609d802cc7e24c31bca9091b09ea310e7", - "email": "selenium@ccnmtl.columbia.edu", - "date_joined": "2012-04-21 08:28:13" - } - }, - { - "pk": 2, - "model": "auth.user", - "fields": { - "username": "test_instructor", - "first_name": "Instructor", - "last_name": "One", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-12-01 11:57:24", - "groups": [ - 1, - 2 - ], - "user_permissions": [], - "password": "sha1$26134$97ef50a76b5bbfa21bf308637ca7d3f2efe0c995", - "email": "", - "date_joined": "2012-04-21 08:29:22" - } - }, - { - "pk": 3, - "model": "auth.user", - "fields": { - "username": "test_student_one", - "first_name": "Student", - "last_name": "One", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-12-01 10:59:26", - "groups": [ - 2 - ], - "user_permissions": [], - "password": "sha1$6bd4b$80001435b98c7005dcf3d94f00c132c6b38447a9", - "email": "", - "date_joined": "2012-04-21 08:29:45" - } - }, - { - "pk": 4, - "model": "auth.user", - "fields": { - "username": "test_student_two", - "first_name": "Student", - "last_name": "Two", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-07-29 09:57:35", - "groups": [ - 2 - ], - "user_permissions": [], - "password": "sha1$4c349$35250ab5720ba6d16f1dfab25559ee27d562e8ae", - "email": "", - "date_joined": "2012-04-21 08:30:02" - } - }, - { - "pk": 5, - "model": "auth.user", - "fields": { - "username": "test_student_three", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-12-01 11:09:35", - "groups": [ - 2, - 3 - ], - "user_permissions": [], - "password": "sha1$3cd75$d91efff1e581f6ed6a5663761ec2d997e752b6eb", - "email": "", - "date_joined": "2012-09-15 08:51:46" - } - }, - { - "pk": 6, - "model": "auth.user", - "fields": { - "username": "test_instructor_alt", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-12-01 11:08:08", - "groups": [ - 3, - 4 - ], - "user_permissions": [], - "password": "sha1$1db49$7efd2133c0100e5aed24de0080c883e27aec6e29", - "email": "", - "date_joined": "2012-09-15 08:52:30" - } - }, - { - "pk": 7, - "model": "auth.user", - "fields": { - "username": "test_student_alt", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-09-15 08:56:21", - "groups": [ - 3 - ], - "user_permissions": [], - "password": "sha1$48465$279290fe16e6e367fe7abaeece026ac628685d2d", - "email": "", - "date_joined": "2012-09-15 08:52:30" - } - }, - { - "pk": 8, - "model": "auth.user", - "fields": { - "username": "test_staff", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": false, - "is_staff": true, - "last_login": "2012-10-06 13:04:16", - "groups": [], - "user_permissions": [], - "password": "sha1$11b57$1502aadc812c8cf9e671800c0e5861490bba64b1", - "email": "", - "date_joined": "2012-10-06 13:04:16" - } - }, - { - "pk": 9, - "model": "auth.user", - "fields": { - "username": "test_ta", - "first_name": "Teacher's ", - "last_name": "Assistant", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-10-06 13:05:39", - "groups": [ - 2, - 5 - ], - "user_permissions": [], - "password": "sha1$90f6e$17aedb174d31ba3d8195fb1c6183d15ccdc7b7f0", - "email": "", - "date_joined": "2012-10-06 13:04:59" - } - }, - { - "pk": 10, - "model": "auth.user", - "fields": { - "username": "test_instructor_two", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-12-18 11:41:42", - "groups": [ - 1, - 2, - 3, - 4 - ], - "user_permissions": [], - "password": "sha1$38bcf$64ca5339edec9d8507b69789a0833745b94012f0", - "email": "", - "date_joined": "2012-11-18 09:26:19" - } - } -] \ No newline at end of file diff --git a/mediathread/main/fixtures/unittest_sample_course.json b/mediathread/main/fixtures/unittest_sample_course.json deleted file mode 100644 index 77398fc42..000000000 --- a/mediathread/main/fixtures/unittest_sample_course.json +++ /dev/null @@ -1,1253 +0,0 @@ -[ - { - "pk": 99, - "model": "auth.permission", - "fields": { - "codename": "can_upload_for", - "name": "Can upload for other users", - "content_type": 24 - } - }, - { - "pk": 1, - "model": "auth.group", - "fields": { - "name": "Sample_Course_Faculty", - "permissions": [ - 34, - 35, - 36, - 71, - 72, - 73, - 74, - 75, - 76, - 77, - 78, - 79, - 4, - 5, - 6, - 10, - 11, - 12, - 1, - 2, - 3, - 7, - 8, - 9, - 43, - 46, - 44, - 45, - 47, - 48, - 49, - 13, - 14, - 15, - 22, - 23, - 24, - 31, - 32, - 33, - 28, - 29, - 30, - 25, - 26, - 27, - 80, - 81, - 82, - 86, - 83, - 84, - 85, - 19, - 20, - 21, - 68, - 69, - 70, - 62, - 63, - 64, - 65, - 66, - 67, - 16, - 17, - 18, - 53, - 54, - 55, - 59, - 60, - 61, - 56, - 57, - 58, - 37, - 38, - 39, - 40, - 41, - 42, - 50, - 51, - 52 - ] - } - }, - { - "pk": 2, - "model": "auth.group", - "fields": { - "name": "Sample_Course_Students", - "permissions": [] - } - }, - { - "pk": 3, - "model": "auth.group", - "fields": { - "name": "Alternate Course Members", - "permissions": [] - } - }, - { - "pk": 4, - "model": "auth.group", - "fields": { - "name": "Alternate Course Faculty", - "permissions": [] - } - }, - { - "pk": 5, - "model": "auth.group", - "fields": { - "name": "Teacher's Assistant", - "permissions": [ - 99 - ] - } - }, - { - "pk": 1, - "model": "auth.user", - "fields": { - "username": "selenium", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": true, - "is_staff": true, - "last_login": "2012-12-14 14:53:12", - "groups": [], - "user_permissions": [], - "password": "sha1$03e73$f0d7f55609d802cc7e24c31bca9091b09ea310e7", - "email": "selenium@ccnmtl.columbia.edu", - "date_joined": "2012-04-21 08:28:13" - } - }, - { - "pk": 2, - "model": "auth.user", - "fields": { - "username": "test_instructor", - "first_name": "Instructor", - "last_name": "One", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-12-01 11:57:24", - "groups": [ - 1, - 2 - ], - "user_permissions": [], - "password": "sha1$26134$97ef50a76b5bbfa21bf308637ca7d3f2efe0c995", - "email": "", - "date_joined": "2012-04-21 08:29:22" - } - }, - { - "pk": 3, - "model": "auth.user", - "fields": { - "username": "test_student_one", - "first_name": "Student", - "last_name": "One", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-12-01 10:59:26", - "groups": [ - 2 - ], - "user_permissions": [], - "password": "sha1$6bd4b$80001435b98c7005dcf3d94f00c132c6b38447a9", - "email": "", - "date_joined": "2012-04-21 08:29:45" - } - }, - { - "pk": 4, - "model": "auth.user", - "fields": { - "username": "test_student_two", - "first_name": "Student", - "last_name": "Two", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-07-29 09:57:35", - "groups": [ - 2 - ], - "user_permissions": [], - "password": "sha1$4c349$35250ab5720ba6d16f1dfab25559ee27d562e8ae", - "email": "", - "date_joined": "2012-04-21 08:30:02" - } - }, - { - "pk": 5, - "model": "auth.user", - "fields": { - "username": "test_student_three", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-12-01 11:09:35", - "groups": [ - 2, - 3 - ], - "user_permissions": [], - "password": "sha1$3cd75$d91efff1e581f6ed6a5663761ec2d997e752b6eb", - "email": "", - "date_joined": "2012-09-15 08:51:46" - } - }, - { - "pk": 6, - "model": "auth.user", - "fields": { - "username": "test_instructor_alt", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-12-01 11:08:08", - "groups": [ - 3, - 4 - ], - "user_permissions": [], - "password": "sha1$1db49$7efd2133c0100e5aed24de0080c883e27aec6e29", - "email": "", - "date_joined": "2012-09-15 08:52:30" - } - }, - { - "pk": 7, - "model": "auth.user", - "fields": { - "username": "test_student_alt", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-09-15 08:56:21", - "groups": [ - 3 - ], - "user_permissions": [], - "password": "sha1$48465$279290fe16e6e367fe7abaeece026ac628685d2d", - "email": "", - "date_joined": "2012-09-15 08:52:30" - } - }, - { - "pk": 8, - "model": "auth.user", - "fields": { - "username": "test_staff", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": false, - "is_staff": true, - "last_login": "2012-10-06 13:04:16", - "groups": [], - "user_permissions": [], - "password": "sha1$11b57$1502aadc812c8cf9e671800c0e5861490bba64b1", - "email": "", - "date_joined": "2012-10-06 13:04:16" - } - }, - { - "pk": 9, - "model": "auth.user", - "fields": { - "username": "test_ta", - "first_name": "Teacher's ", - "last_name": "Assistant", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-10-06 13:05:39", - "groups": [ - 2, - 5 - ], - "user_permissions": [], - "password": "sha1$90f6e$17aedb174d31ba3d8195fb1c6183d15ccdc7b7f0", - "email": "", - "date_joined": "2012-10-06 13:04:59" - } - }, - { - "pk": 10, - "model": "auth.user", - "fields": { - "username": "test_instructor_two", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": false, - "is_staff": false, - "last_login": "2012-12-18 11:41:42", - "groups": [ - 1, - 2, - 3, - 4 - ], - "user_permissions": [], - "password": "sha1$38bcf$64ca5339edec9d8507b69789a0833745b94012f0", - "email": "", - "date_joined": "2012-11-18 09:26:19" - } - }, - { - "pk": 1, - "model": "courseaffils.course", - "fields": { - "faculty_group": 1, - "group": 2, - "title": "Sample Course" - } - }, - { - "pk": 2, - "model": "courseaffils.course", - "fields": { - "faculty_group": 4, - "group": 3, - "title": "Alternate Course" - } - }, - { - "pk": 2, - "model": "structuredcollaboration.collaboration", - "fields": { - "_order": 2, - "group": 3, - "context": null, - "title": "Alternate Course", - "_policy": null, - "object_pk": "2", - "_parent": null, - "user": null, - "content_type": [ - "courseaffils", - "course" - ], - "slug": "Alternate_Course" - } - }, - { - "pk": 1, - "model": "structuredcollaboration.collaboration", - "fields": { - "_order": 1, - "group": 2, - "context": null, - "title": "Sample Course", - "_policy": null, - "object_pk": "1", - "_parent": null, - "user": null, - "content_type": [ - "courseaffils", - "course" - ], - "slug": "Sample_Course" - } - }, - { - "pk": 1, - "model": "main.usersetting", - "fields": { - "user": 6, - "value": "true", - "name": "help_item_detail_view" - } - }, - { - "pk": 2, - "model": "main.usersetting", - "fields": { - "user": 3, - "value": "False", - "name": "help_homepage_classwork_column" - } - }, - { - "pk": 3, - "model": "main.usersetting", - "fields": { - "user": 5, - "value": "true", - "name": "help_item_detail_view" - } - }, - { - "pk": 4, - "model": "main.usersetting", - "fields": { - "user": 10, - "value": "False", - "name": "help_homepage_classwork_column" - } - }, - { - "pk": 5, - "model": "main.usersetting", - "fields": { - "user": 10, - "value": "False", - "name": "help_homepage_instructor_column" - } - }, - { - "pk": 6, - "model": "main.usersetting", - "fields": { - "user": 10, - "value": "true", - "name": "help_item_detail_view" - } - }, - { - "pk": 1, - "model": "assetmgr.asset", - "fields": { - "added": "2012-05-12 12:31:20", - "title": "Mediathread: Introduction", - "metadata_blob": "{\"category\": [\"Education\"], \"description\": [\"Mediathread is CCNMTL's innovative, open-source platform for exploration, analysis, and organization of web-based multimedia content. Mediathread connects to a variety of image and video collections (such as YouTube, Flickr, library databases, and course libraries), enabling users to lift items out of these collections and into an analysis environment. In Mediathread, items can then be clipped, annotated, organized, and embedded into essays and other written analysis.\"], \"author\": [\"CCNMTL\"], \"youtube_link\": [\"http://www.youtube.com/watch?v=7KjzRG8zYYo\"], \"author_uri\": [\"http://gdata.youtube.com/feeds/api/users/CCNMTL\"], \"published\": [\"2012-02-09T00:46:25.000Z\"]}", - "author": 2, - "modified": "2012-05-12 12:31:20", - "course": 1, - "active": true - } - }, - { - "pk": 2, - "model": "assetmgr.asset", - "fields": { - "added": "2012-05-12 12:44:24", - "title": "MAAP Award Reception", - "metadata_blob": "", - "author": 2, - "modified": "2012-05-12 12:44:24", - "course": 1, - "active": true - } - }, - { - "pk": 3, - "model": "assetmgr.asset", - "fields": { - "added": "2012-05-12 12:58:17", - "title": "The Armory - Home to CCNMTL'S CUMC Office", - "metadata_blob": "", - "author": 2, - "modified": "2012-05-12 12:58:17", - "course": 1, - "active": true - } - }, - { - "pk": 4, - "model": "assetmgr.asset", - "fields": { - "added": "2012-09-15 08:53:57", - "title": "Design Research", - "metadata_blob": "", - "author": 6, - "modified": "2012-09-15 08:53:57", - "course": 2, - "active": true - } - }, - { - "pk": 5, - "model": "assetmgr.asset", - "fields": { - "added": "2012-12-18 11:47:04", - "title": "Project Portfolio", - "metadata_blob": "", - "author": 10, - "modified": "2012-12-18 11:47:04", - "course": 1, - "active": true - } - }, - { - "pk": 1, - "model": "assetmgr.source", - "fields": { - "url": "http://www.youtube.com/watch?v=7KjzRG8zYYo&list=%2Fwatch%3Fv%3D7KjzRG8zYYo&list=UU6Qe9fItsCXSWWwiLeN_tBA&index=0&feature=plcp", - "primary": false, - "label": "url", - "width": 0, - "asset": 1, - "modified": "2012-05-12 12:31:20", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 2, - "model": "assetmgr.source", - "fields": { - "url": "http://gdata.youtube.com/feeds/api/videos/7KjzRG8zYYo", - "primary": false, - "label": "gdata", - "width": 0, - "asset": 1, - "modified": "2012-05-12 12:31:20", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 3, - "model": "assetmgr.source", - "fields": { - "url": "http://www.youtube.com/v/7KjzRG8zYYo?enablejsapi=1&fs=1", - "primary": true, - "label": "youtube", - "width": 0, - "asset": 1, - "modified": "2012-05-12 12:31:20", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 4, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/mediathread_introduction_thumb.jpg", - "primary": false, - "label": "thumb", - "width": 120, - "asset": 1, - "modified": "2012-05-12 12:31:20", - "media_type": null, - "height": 90, - "size": 0 - } - }, - { - "pk": 5, - "model": "assetmgr.source", - "fields": { - "url": "bookmarklet", - "primary": false, - "label": "asset-source", - "width": 0, - "asset": 1, - "modified": "2012-05-12 12:31:20", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 6, - "model": "assetmgr.source", - "fields": { - "url": "http://www.flickr.com/photos/ccnmtl/4049410921/", - "primary": false, - "label": "url", - "width": 0, - "asset": 2, - "modified": "2012-05-12 12:44:24", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 7, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/maap.jpg", - "primary": true, - "label": "image", - "width": 2816, - "asset": 2, - "modified": "2012-05-12 12:44:24", - "media_type": null, - "height": 2112, - "size": 0 - } - }, - { - "pk": 8, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/maap_thumb.jpg", - "primary": false, - "label": "thumb", - "width": 0, - "asset": 2, - "modified": "2012-05-12 12:44:24", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 10, - "model": "assetmgr.source", - "fields": { - "url": "bookmarklet", - "primary": false, - "label": "asset-source", - "width": 0, - "asset": 2, - "modified": "2012-05-12 12:44:24", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 11, - "model": "assetmgr.source", - "fields": { - "url": "http://www.flickr.com/photos/ccnmtl/3308199560/", - "primary": false, - "label": "url", - "width": 0, - "asset": 3, - "modified": "2012-05-12 12:58:17", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 12, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/armory.jpg/", - "primary": true, - "label": "image", - "width": 1430, - "asset": 3, - "modified": "2012-05-12 12:58:17", - "media_type": null, - "height": 703, - "size": 0 - } - }, - { - "pk": 13, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/armory_thumb.jpg", - "primary": false, - "label": "thumb", - "width": 0, - "asset": 3, - "modified": "2012-05-12 12:58:17", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 15, - "model": "assetmgr.source", - "fields": { - "url": "bookmarklet", - "primary": false, - "label": "asset-source", - "width": 0, - "asset": 3, - "modified": "2012-05-12 12:58:17", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 16, - "model": "assetmgr.source", - "fields": { - "url": "http://ccnmtl.columbia.edu/", - "primary": false, - "label": "url", - "width": 0, - "asset": 4, - "modified": "2012-09-15 08:53:57", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 17, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/dr.jpg", - "primary": true, - "label": "image", - "width": 768, - "asset": 4, - "modified": "2012-09-15 08:53:57", - "media_type": null, - "height": 230, - "size": 0 - } - }, - { - "pk": 18, - "model": "assetmgr.source", - "fields": { - "url": "bookmarklet", - "primary": false, - "label": "asset-source", - "width": 0, - "asset": 4, - "modified": "2012-09-15 08:53:57", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 19, - "model": "assetmgr.source", - "fields": { - "url": "http://ccnmtl.columbia.edu/", - "primary": false, - "label": "url", - "width": 0, - "asset": 5, - "modified": "2012-12-18 11:47:04", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 20, - "model": "assetmgr.source", - "fields": { - "url": "http://localhost:8002/media/img/test/portfolio.jpg", - "primary": true, - "label": "image", - "width": 768, - "asset": 5, - "modified": "2012-12-18 11:47:04", - "media_type": null, - "height": 230, - "size": 0 - } - }, - { - "pk": 21, - "model": "assetmgr.source", - "fields": { - "url": "bookmarklet", - "primary": false, - "label": "asset-source", - "width": 0, - "asset": 5, - "modified": "2012-12-18 11:47:04", - "media_type": null, - "height": 0, - "size": 0 - } - }, - { - "pk": 1, - "model": "assetmgr.supportedsource", - "fields": { - "archive_url": "http://youtube.com", - "description": "The largest collection of video on the web, where you can also upload video yourself.", - "thumb_url": "/media/img/thumbs/youtube.jpg", - "title": "You Tube" - } - }, - { - "pk": 1, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 2, - "model": "djangosherd.annotation", - "fields": { - "range2": 39.0, - "range1": 28.0, - "annotation_data": "{\"startCode\":\"00:00:28\",\"endCode\":\"00:00:39\",\"duration\":0,\"timeScale\":1,\"start\":28,\"end\":39}" - } - }, - { - "pk": 3, - "model": "djangosherd.annotation", - "fields": { - "range2": 75.0, - "range1": 43.0, - "annotation_data": "{\"startCode\":\"00:00:43\",\"endCode\":\"00:01:15\",\"duration\":0,\"timeScale\":1,\"start\":43,\"end\":75}" - } - }, - { - "pk": 4, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 5, - "model": "djangosherd.annotation", - "fields": { - "range2": 23.0, - "range1": -4.5, - "annotation_data": "{\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-108,-22],[-108,68],[98.99999999999999,68],[98.99999999999999,-22],[-108,-22]]]},\"default\":false,\"x\":-4.5,\"y\":23,\"zoom\":1,\"extent\":[-120,-90,120,90]}" - } - }, - { - "pk": 6, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 7, - "model": "djangosherd.annotation", - "fields": { - "range2": 0.0, - "range1": 0.0, - "annotation_data": "{\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-167.5,2],[-167.5,80.5],[-95.5,80.5],[-95.5,2],[-167.5,2]]]},\"default\":false,\"x\":0,\"y\":0,\"zoom\":2,\"extent\":[-180,-89,180,89]}" - } - }, - { - "pk": 8, - "model": "djangosherd.annotation", - "fields": { - "range2": 0.0, - "range1": 0.0, - "annotation_data": "{\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[-58,-90],[-58,-14],[49,-14],[49,-90],[-58,-90]]]},\"default\":false,\"x\":0,\"y\":0,\"zoom\":2,\"extent\":[-120,-90,120,90]}" - } - }, - { - "pk": 9, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 10, - "model": "djangosherd.annotation", - "fields": { - "range2": 0.0, - "range1": 0.0, - "annotation_data": "{\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[38.5,-91],[38.5,2.5],[61.5,2.500000000000007],[61.5,-91],[38.5,-91]]]},\"default\":false,\"x\":0,\"y\":0,\"zoom\":2,\"extent\":[-120,-90,120,90]}" - } - }, - { - "pk": 11, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 12, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 13, - "model": "djangosherd.annotation", - "fields": { - "range2": -9.25, - "range1": 33.375, - "annotation_data": "{\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[14.249999999999993,-17.512500762939],[14.249999999999993,17.112499237061],[60.375,17.112499237061],[60.375000000000014,-17.512500762939],[14.249999999999993,-17.512500762939]]]},\"default\":false,\"x\":33.375,\"y\":-9.25,\"zoom\":4,\"extent\":[-180,-54,180,54]}" - } - }, - { - "pk": 14, - "model": "djangosherd.annotation", - "fields": { - "range2": 6.75, - "range1": 116.25, - "annotation_data": "{\"geometry\":{\"type\":\"Polygon\",\"coordinates\":[[[101,0.22499847412108664],[101,44.224998474120994],[142.25,44.22499847412101],[142.25,0.22499847412108664],[101,0.22499847412108664]]]},\"default\":false,\"x\":116.25,\"y\":6.75,\"zoom\":3,\"extent\":[-180,-54,180,54]}" - } - }, - { - "pk": 15, - "model": "djangosherd.annotation", - "fields": { - "range2": 0.0, - "range1": 0.0, - "annotation_data": "{\"default\":true,\"x\":0,\"y\":0,\"zoom\":1,\"extent\":[-180,-54,180,54]}" - } - }, - { - "pk": 16, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 17, - "model": "djangosherd.annotation", - "fields": { - "range2": 0.0, - "range1": 0.0, - "annotation_data": "{\"startCode\":\"00:00:00\",\"endCode\":\"00:00:00\",\"duration\":0,\"timeScale\":1,\"start\":0,\"end\":0}" - } - }, - { - "pk": 18, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 19, - "model": "djangosherd.annotation", - "fields": { - "range2": 120.0, - "range1": 60.0, - "annotation_data": "{\"startCode\":\"00:01:00\",\"endCode\":\"00:02:00\",\"duration\":171,\"timeScale\":1,\"start\":60,\"end\":120}" - } - }, - { - "pk": 20, - "model": "djangosherd.annotation", - "fields": { - "range2": null, - "range1": null, - "annotation_data": null - } - }, - { - "pk": 1, - "model": "djangosherd.sherdnote", - "fields": { - "body": "All credit to Mark and Casey", - "added": "2012-05-12 12:32:07", - "author": 2, - "tags": ",youtube, test_instructor_item", - "modified": "2012-12-01 11:57:53", - "asset": 1, - "title": null - } - }, - { - "pk": 2, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-05-12 12:37:15", - "author": 2, - "tags": ",video", - "modified": "2012-05-12 12:51:22", - "asset": 1, - "title": "Manage Sources" - } - }, - { - "pk": 3, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-05-12 12:38:33", - "author": 2, - "tags": ",video", - "modified": "2012-05-12 12:51:30", - "asset": 1, - "title": "Annotations" - } - }, - { - "pk": 4, - "model": "djangosherd.sherdnote", - "fields": { - "body": "instructor one item note", - "added": "2012-05-12 12:47:33", - "author": 2, - "tags": ",flickr, instructor_one", - "modified": "2012-07-29 09:57:23", - "asset": 2, - "title": null - } - }, - { - "pk": 5, - "model": "djangosherd.sherdnote", - "fields": { - "body": "instructor one selection note", - "added": "2012-05-12 12:51:04", - "author": 2, - "tags": ",image, instructor_one_selection, ", - "modified": "2012-07-29 09:57:15", - "asset": 2, - "title": "Our esteemed leaders" - } - }, - { - "pk": 6, - "model": "djangosherd.sherdnote", - "fields": { - "body": "This is for deletion purposes", - "added": "2012-05-12 12:58:33", - "author": 2, - "tags": ",flickr", - "modified": "2012-05-12 12:58:33", - "asset": 3, - "title": null - } - }, - { - "pk": 7, - "model": "djangosherd.sherdnote", - "fields": { - "body": "This is for deletion.", - "added": "2012-05-12 12:58:49", - "author": 2, - "tags": ",image", - "modified": "2012-05-12 12:58:49", - "asset": 3, - "title": "Left Corner" - } - }, - { - "pk": 8, - "model": "djangosherd.sherdnote", - "fields": { - "body": "student one selection note", - "added": "2012-07-29 09:56:02", - "author": 3, - "tags": ",student_one_selection", - "modified": "2012-07-29 09:56:02", - "asset": 2, - "title": "The Award" - } - }, - { - "pk": 9, - "model": "djangosherd.sherdnote", - "fields": { - "body": "student one item note", - "added": "2012-07-29 09:56:02", - "author": 3, - "tags": ",student_one_item", - "modified": "2012-07-29 09:56:14", - "asset": 2, - "title": null - } - }, - { - "pk": 10, - "model": "djangosherd.sherdnote", - "fields": { - "body": "student two selection note", - "added": "2012-07-29 09:58:30", - "author": 4, - "tags": ",student_two_selection", - "modified": "2012-07-29 09:58:30", - "asset": 2, - "title": "Nice Tie" - } - }, - { - "pk": 11, - "model": "djangosherd.sherdnote", - "fields": { - "body": "student two item note", - "added": "2012-07-29 09:58:30", - "author": 4, - "tags": ",student_two_item", - "modified": "2012-07-29 09:58:39", - "asset": 2, - "title": null - } - }, - { - "pk": 12, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-09-15 08:53:57", - "author": 6, - "tags": "", - "modified": "2012-09-15 08:53:57", - "asset": 4, - "title": null - } - }, - { - "pk": 13, - "model": "djangosherd.sherdnote", - "fields": { - "body": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", - "added": "2012-09-15 08:55:29", - "author": 6, - "tags": ",research", - "modified": "2012-09-15 08:55:29", - "asset": 4, - "title": "Research and Evaluation" - } - }, - { - "pk": 14, - "model": "djangosherd.sherdnote", - "fields": { - "body": "Phasellus sed purus et nibh vehicula scelerisque at quis orci. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed dui orci, consequat in fermentum vel, congue eu nulla. ", - "added": "2012-09-15 08:56:00", - "author": 6, - "tags": ",tag1", - "modified": "2012-09-15 08:56:00", - "asset": 4, - "title": "Curricular Context" - } - }, - { - "pk": 15, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-12-01 11:01:01", - "author": 5, - "tags": ",test_student_three", - "modified": "2012-12-01 11:01:01", - "asset": 4, - "title": "Whole Item Selection" - } - }, - { - "pk": 16, - "model": "djangosherd.sherdnote", - "fields": { - "body": "student three item note", - "added": "2012-12-01 11:01:01", - "author": 5, - "tags": ",student_three_item", - "modified": "2012-12-01 11:10:47", - "asset": 4, - "title": null - } - }, - { - "pk": 17, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-12-01 11:58:08", - "author": 2, - "tags": ",test_instructor_selection", - "modified": "2012-12-01 11:58:08", - "asset": 1, - "title": "Whole Item Selection" - } - }, - { - "pk": 18, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-12-18 11:47:04", - "author": 10, - "tags": "", - "modified": "2012-12-18 11:47:04", - "asset": 5, - "title": null - } - }, - { - "pk": 19, - "model": "djangosherd.sherdnote", - "fields": { - "body": null, - "added": "2012-12-18 11:49:21", - "author": 10, - "tags": ",test_instructor_two", - "modified": "2012-12-18 11:49:21", - "asset": 1, - "title": "Video Selection Is Time-based" - } - }, - { - "pk": 20, - "model": "djangosherd.sherdnote", - "fields": { - "body": "test_instructor_two notes", - "added": "2012-12-18 11:49:22", - "author": 10, - "tags": ",test_instructor_two", - "modified": "2012-12-18 11:49:22", - "asset": 1, - "title": null - } - } -] \ No newline at end of file diff --git a/mediathread/main/fixtures/unittest_sample_projects.json b/mediathread/main/fixtures/unittest_sample_projects.json deleted file mode 100644 index fc992af89..000000000 --- a/mediathread/main/fixtures/unittest_sample_projects.json +++ /dev/null @@ -1,240 +0,0 @@ -[ - { - "pk": 1, - "model": "projects.project", - "fields": { - "body": "

    Nice Tie - Student Two Annotation

    \r\n

    The Award - Student One Annotation

    ", - "feedback": null, - "author": 3, - "title": "Private Composition", - "modified": "2012-12-08 14:54:02", - "submitted": false, - "course": 1, - "participants": [ - 3 - ] - } - }, - { - "pk": 2, - "model": "projects.project", - "fields": { - "body": "

    Some text here.

    ", - "feedback": null, - "author": 3, - "title": "Instructor Shared", - "modified": "2012-12-08 14:54:24", - "submitted": true, - "course": 1, - "participants": [ - 3 - ] - } - }, - { - "pk": 3, - "model": "projects.project", - "fields": { - "body": "

    Test Instructor Selections

    \r\n

    Manage Sources

    \r\n

    Our esteemed leaders

    \r\n

    Left Corner

    ", - "feedback": null, - "author": 3, - "title": "Public To Class Composition", - "modified": "2012-12-08 14:55:08", - "submitted": true, - "course": 1, - "participants": [ - 3 - ] - } - }, - { - "pk": 4, - "model": "projects.project", - "fields": { - "body": "

    Test Student Three Item & Selections

    \r\n

    Design Research

    \r\n

    Whole Item Selection

    ", - "feedback": null, - "author": 5, - "title": "Public To Class", - "modified": "2012-12-08 14:59:29", - "submitted": true, - "course": 2, - "participants": [ - 5 - ] - } - }, - { - "pk": 5, - "model": "projects.project", - "fields": { - "body": "

    Nice Tie - Student Two Annotation

    \r\n

    Mediathread: Introduction

    \r\n

    Project Portfolio

    \r\n

    Mediathread: Introduction

    \r\n

    Video Selection Is Time-based

    \r\n

     

    ", - "feedback": null, - "author": 10, - "title": "Sample Course Assignment", - "modified": "2012-12-18 12:17:08", - "submitted": true, - "course": 1, - "participants": [ - 10 - ] - } - }, - { - "pk": 6, - "model": "projects.project", - "fields": { - "body": "

    Class Information

    ", - "feedback": null, - "author": 6, - "title": "Alternate Course Information", - "modified": "2012-12-18 12:17:08", - "submitted": true, - "course": 2, - "participants": [ - 10 - ] - } - }, - { - "pk": 1, - "model": "structuredcollaboration.collaborationpolicyrecord", - "fields": { - "policy_name": "PrivateEditorsAreOwners" - } - }, - { - "pk": 2, - "model": "structuredcollaboration.collaborationpolicyrecord", - "fields": { - "policy_name": "InstructorShared" - } - }, - { - "pk": 3, - "model": "structuredcollaboration.collaborationpolicyrecord", - "fields": { - "policy_name": "CourseProtected" - } - }, - { - "pk": 4, - "model": "structuredcollaboration.collaborationpolicyrecord", - "fields": { - "policy_name": "Assignment" - } - }, - { - "pk": 4, - "model": "structuredcollaboration.collaboration", - "fields": { - "_order": 4, - "group": null, - "context": 1, - "title": "Instructor Shared ", - "_policy": 2, - "object_pk": "2", - "_parent": null, - "user": 3, - "content_type": [ - "projects", - "project" - ], - "slug": null - } - }, - { - "pk": 3, - "model": "structuredcollaboration.collaboration", - "fields": { - "_order": 3, - "group": null, - "context": 1, - "title": "Private Composition", - "_policy": 1, - "object_pk": "1", - "_parent": null, - "user": 3, - "content_type": [ - "projects", - "project" - ], - "slug": null - } - }, - { - "pk": 6, - "model": "structuredcollaboration.collaboration", - "fields": { - "_order": 6, - "group": null, - "context": 2, - "title": "Public To Class", - "_policy": 3, - "object_pk": "4", - "_parent": null, - "user": 5, - "content_type": [ - "projects", - "project" - ], - "slug": null - } - }, - { - "pk": 5, - "model": "structuredcollaboration.collaboration", - "fields": { - "_order": 5, - "group": null, - "context": 1, - "title": "Public To Class Composition", - "_policy": 3, - "object_pk": "3", - "_parent": null, - "user": 3, - "content_type": [ - "projects", - "project" - ], - "slug": null - } - }, - { - "pk": 7, - "model": "structuredcollaboration.collaboration", - "fields": { - "_order": 7, - "group": null, - "context": 1, - "title": "Sample Course Assignment", - "_policy": 4, - "object_pk": "5", - "_parent": null, - "user": 10, - "content_type": [ - "projects", - "project" - ], - "slug": null - } - }, - { - "pk": 8, - "model": "structuredcollaboration.collaboration", - "fields": { - "_order": 8, - "group": null, - "context": 1, - "title": "Alternate Course Information", - "_policy": 3, - "object_pk": "6", - "_parent": null, - "user": 6, - "content_type": [ - "projects", - "project" - ], - "slug": null - } - } -] \ No newline at end of file diff --git a/mediathread/taxonomy/api.py b/mediathread/taxonomy/api.py index ad0270281..4c1ae0293 100644 --- a/mediathread/taxonomy/api.py +++ b/mediathread/taxonomy/api.py @@ -1,4 +1,4 @@ -# pylint: disable-msg=R0904 +#pylint: disable-msg=R0904 from courseaffils.models import Course from django.contrib.contenttypes.models import ContentType from django.db.models import Count @@ -7,34 +7,6 @@ from tastypie.fields import ToManyField from tastypie.resources import ModelResource from tastypie.validation import Validation -import inspect - - -class OnomyValidation(Validation): - def is_valid(self, bundle, request=None): - errors = {} - - a = Onomy.objects.filter( - url=bundle.data['url'] - ) - - if len(a) > 0: - if 'pk' not in bundle.data or a[0].pk != int(bundle.ddata['pk']): - msg = 'A %s term already exists. Please choose another name' \ - % bundle.data['display_name'] - errors['error_message'] = [msg] - return errors - - -class OnomyResource(ModelResource): - class Meta: - queryset = Term.objects.all().order_by('id') - list_allowed_methods = ['get', 'post'] - detail_allowed_methods = ['get', 'put', 'delete'] - authentication = ClassLevelAuthentication() - authorization = FacultyAuthorization() - excludes = ['description', 'ordinality'] - validation = OnomyValidation() class TermValidation(Validation): @@ -49,12 +21,13 @@ def is_valid(self, bundle, request=None): if 'pk' not in bundle.data or a[0].pk != int(bundle.data['pk']): # a vocabulary already exists with this name msg = 'A %s term already exists. Please choose another name' \ - % bundle.data['display_name'] + % bundle.data['display_name'] errors['error_message'] = [msg] return errors class TermResource(ModelResource): + class Meta: queryset = Term.objects.all().order_by('id') list_allowed_methods = ['get', 'post'] @@ -84,27 +57,30 @@ def render_one(self, request, term): class VocabularyValidation(Validation): def is_valid(self, bundle, request=None): errors = {} + a = Vocabulary.objects.filter( content_type_id=bundle.data['content_type_id'], display_name=bundle.data['display_name'], - object_id=bundle.data['object_id'] - ) + object_id=bundle.data['object_id']) if len(a) > 0: # vocabulary exists with this name if 'pk' not in bundle.data or a[0].pk != int(bundle.data['pk']): # a vocabulary already exists with this name msg = 'A %s concept exists. Please choose another name' \ - % bundle.data['display_name'] + % bundle.data['display_name'] errors['error_message'] = [msg] return errors class VocabularyAuthorization(FacultyAuthorization): + def read_list(self, object_list, bundle): request = bundle.request + course_type = ContentType.objects.get_for_model(request.course) object_list = object_list.filter(content_type=course_type, object_id=request.course.id) + return object_list.order_by('id') @@ -132,7 +108,6 @@ def alter_list_data_to_serialize(self, request, to_be_serialized): def dehydrate(self, bundle): bundle.data['content_type_id'] = bundle.obj.content_type.id - return bundle def hydrate(self, bundle): @@ -181,6 +156,25 @@ def render_related(self, request, object_list): values = ctx.values() values.sort(lambda a, b: cmp(a['display_name'].lower(), - b['display_name'].lower())) + b['display_name'].lower())) return values + + def render_for_course(self, request, object_list): + term_counts = TermRelationship.objects.none() + if len(object_list) > 0: + related = TermRelationship.objects.get_for_object_list(object_list) + term_counts = related.values('term').annotate(count=Count('id')) + + data = [] + for vocabulary in Vocabulary.objects.get_for_object(request.course): + ctx = self.render_one(request, vocabulary) + for term in ctx['term_set']: + qs = term_counts.filter(term=term['id']) + term['count'] = qs[0]['count'] if len(qs) > 0 else 0 + data.append(ctx) + + data.sort(lambda a, b: cmp(a['display_name'].lower(), + b['display_name'].lower())) + + return data diff --git a/mediathread/taxonomy/tests/factories.py b/mediathread/taxonomy/tests/factories.py deleted file mode 100644 index de6937479..000000000 --- a/mediathread/taxonomy/tests/factories.py +++ /dev/null @@ -1,64 +0,0 @@ -from courseaffils.models import Course -from django.contrib.contenttypes.models import ContentType -from mediathread.djangosherd.models import SherdNote -from mediathread.taxonomy.models import Vocabulary, Term, TermRelationship -from tastypie.test import ResourceTestCase - - -class TaxonomyTestCase(ResourceTestCase): - fixtures = ['unittest_sample_course.json'] - - def create_vocabularies(self, course, taxonomy): - course_type = ContentType.objects.get_for_model(course) - - for name, terms in taxonomy.items(): - concept = Vocabulary(display_name=name, - content_type=course_type, - object_id=course.id) - concept.save() - for term_name in terms: - term = Term(display_name=term_name, - vocabulary=concept) - term.save() - - def create_term_relationship(self, content_object, term): - # Add some tags to a few notes - content_type = ContentType.objects.get_for_model(content_object) - TermRelationship.objects.get_or_create( - term=term, - content_type=content_type, - object_id=content_object.id) - - def setUp(self): - super(TaxonomyTestCase, self).setUp() - - course = Course.objects.get(title="Sample Course") - taxonomy = { - 'Shapes': ['Square', 'Triangle'], - 'Colors': ['Red', 'Blue', 'Green'] - } - self.create_vocabularies(course, taxonomy) - - course = Course.objects.get(title="Alternate Course") - taxonomy = { - 'Materials': ['Paper', 'Wood', 'Stone'] - } - self.create_vocabularies(course, taxonomy) - - # test_instructor in Sample Course - note = SherdNote.objects.get(title="Left Corner") - term = Term.objects.get(name="square") - self.create_term_relationship(note, term) - term = Term.objects.get(name="red") - self.create_term_relationship(note, term) - - # test_student_two in Sample Course - note = SherdNote.objects.get(title="Nice Tie") - term = Term.objects.get(name="square") - self.create_term_relationship(note, term) - - # test_student_three in Alternate Course - note = SherdNote.objects.get(title="Whole Item Selection", - author__username='test_student_three') - term = Term.objects.get(name="paper") - self.create_term_relationship(note, term) diff --git a/mediathread/taxonomy/tests/test_models.py b/mediathread/taxonomy/tests/test_models.py deleted file mode 100644 index 9e9c99023..000000000 --- a/mediathread/taxonomy/tests/test_models.py +++ /dev/null @@ -1,23 +0,0 @@ -from mediathread.djangosherd.models import SherdNote -from mediathread.taxonomy.models import TermRelationship, Term -from mediathread.taxonomy.tests.factories import TaxonomyTestCase - - -class TermRelationshipTest(TaxonomyTestCase): - - def test_single_term_relationship(self): - notes = SherdNote.objects.filter(asset__title='MAAP Award Reception') - - lst = TermRelationship.objects.get_for_object_list(notes) - self.assertEquals(len(lst), 1) - self.assertEquals(lst[0].term, Term.objects.get(name='square')) - - def test_multiple_term_relationship(self): - notes = SherdNote.objects.filter( - asset__title="The Armory - Home to CCNMTL'S CUMC Office") - - lst = TermRelationship.objects.get_for_object_list(notes) - - self.assertEquals(len(lst), 2) - self.assertEquals(lst[0].term, Term.objects.get(name='red')) - self.assertEquals(lst[1].term, Term.objects.get(name='square')) diff --git a/mediathread/taxonomy/views.py b/mediathread/taxonomy/views.py index f44649f52..739fd03e6 100644 --- a/mediathread/taxonomy/views.py +++ b/mediathread/taxonomy/views.py @@ -14,19 +14,18 @@ def taxonomy_workspace(request): vocabularies = Vocabulary.objects.get_for_object(request.course) for v in vocabularies: v.form = VocabularyForm(instance=v) + course_type = ContentType.objects.get_for_model(request.course) form = VocabularyForm(initial={'name': 'initial', 'content_type': course_type, 'object_id': request.course.id}) - print vocabularies - print request + return { 'vocabularies': vocabularies, 'vocabulary_form': form, 'course': request.course, 'course_type': course_type, 'term_form': TermForm(), - 'test' : 'tttttt' } @@ -34,7 +33,7 @@ def update_vocabulary_terms(request, content_object): concepts = dict((key[len('vocabulary-'):], request.POST.getlist(key)) for key, val in request.POST.items() if key.startswith('vocabulary-')) - print concepts + # Retrieve concepts/terms that this object is currently associated with associations = TermRelationship.objects.get_for_object(content_object) diff --git a/mediathread/templates/main/course_request_success.html b/mediathread/templates/main/course_request_success.html deleted file mode 100644 index 289bf7421..000000000 --- a/mediathread/templates/main/course_request_success.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "base.html" %} - -{% block title %}Request Course Sucess{% endblock %} - -{% block content %} -

    Request a Mediathread Course

    -

    Thank you for your request. A Mediathread administrator will contact you shortly.

    -

    Return to Mediathread -{% endblock %} From 1e3fbf5e2dcfe46f7927473adcd9ed295be9b269 Mon Sep 17 00:00:00 2001 From: c0cky Date: Sun, 21 Dec 2014 16:52:58 -0500 Subject: [PATCH 22/58] fixed unit test errors --- .../src/django-courseaffils-0.4.9.tar.gz | Bin 18676 -> 0 bytes requirements/src/djangowind-0.11.0.tar.gz | Bin 10853 -> 0 bytes scripts/dump-test-db.sh | 2 - scripts/pre-commit.py | 133 ------------------ scripts/reset-testdb-from-fixture.sh | 7 - 5 files changed, 142 deletions(-) delete mode 100644 requirements/src/django-courseaffils-0.4.9.tar.gz delete mode 100644 requirements/src/djangowind-0.11.0.tar.gz delete mode 100755 scripts/dump-test-db.sh delete mode 100755 scripts/pre-commit.py delete mode 100755 scripts/reset-testdb-from-fixture.sh diff --git a/requirements/src/django-courseaffils-0.4.9.tar.gz b/requirements/src/django-courseaffils-0.4.9.tar.gz deleted file mode 100644 index 94d5d198099b8f1a5cadcc49d6f2926e75ea9fe9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18676 zcmV(-K-|9{iwFp!araXK|72-%bT4FTVQyz{En{zWa&u*2W@c$@b1g6~G%h(VbYXG; z?R{-~+qki4KkHYp%#%_|W!kbGyHz9i&u4{VT)^u)RL4PHO+s& zGXsDGNKh|M;!RJbXE&BeybNXrgTY`h6a3=G*U8o>nWY)?udc!<+uCvW-9K;t?u&hP z;B)x&DgS%G|L*MW^S}J_yWZ~J;nRb|orC?|-|h4ccK7#xXFdJh1AJy#?x&XZyDUiA zn5B*7pxD*bl0o7?=Fzh^AZ!}vN|vD&NnDv?!R zu-u1ohMHGNI;m8Aj#GJ0>T&Kxw>Upt0fuB^k_HnNK(Jad6o+vxZSn7EISI(<-s9F@l?;y;k zkw4eF2m`0f%Z$cA3g#iGUd|>^G-puOuFpnKDe8{%No2QLeBfD_GdE?|&>c(LI)OMo z78H~eTvP_hw&=YeWvBJK&F6oI{2wLJY%&b}udx2_!o%H){NLL->}}-#Lwtl(O<7BP zo25|{4tKd2&cg|lf;Y?4DgN%_w=4Wsk-cZIc*NJI{&dPxE^z;gUQbv)P6B!muq*2> z46a$;9)W!57t&N&hGc&v^@{>`AOz`&dp!2jmnf#&nk-J)ud^^^!QiDIfwaJOL@4BpD3W@-341ZtEn5*H%7e zu#$}OFo~@jHs`~_hXH^#%9C_%C0Al`%iI=ouU%_*r?=BPJAQrK1Gaey>X6mG8;7H@ z6=v2PK6wD27(PE_fa*KoQAPk6fSeEJu#9!Bd;q{qpq(%uTXxSz%vopClpv0ShT8TX zF9-;XI^Cu|fB>?f`O&o)(`SQ1S<$Uu(E+bMaQ+Qelog?nu3~=s_ zt`PlhHi}1Xmb<)Yzp)`94OuX=t&ZDzHcYMUBmQsed<>Mk3!}&yr3@KHFbO~o;QO;Y zfnkIr!15d>7>q!!o-BZ=H46YcS}oJBz4Fy5Zs4Y%#1hL&Ea41bK^vh3Nm zf7He9V=K%#hO)fGQ$tI>7>C>N-m(bc@Skse_vXzD>!;`cpgAg$FVH~P=eTIk z2$g~+{LqB*%h?Q!E#*RZ%c6M~C=-ru2*vF`cAA>W!s{3S0b#R%&h~f6G&W9H%YMoA zOU_6zO}Uziuf#CvMI+lx{CI}^*0qKVCJN(V;@ryoTPC>&`DPCD)E|vP z$8?t_P)8t?&rWDE^5YvTnKch)T)@{BbrEu*Gk54mKA$pV&IL^fY2aV`$nfy6Qb?TkLwu&{h&+=IsE2AyfQa$PNeJ^`h59x6s^C(qU2DQ{DIPdiBd4Q)uR_@s z0waK)2>EU@`~@1xTqJ9fdi-@8ff;B+GXlLK#HQRW)>G!UMDeVr1(QaZXVn9uAqg-ulkQqA5RTS8yDNTpXX_t^AA)hcc5+O z@%K;_pvWhkbrwc}ZvH3W<|+?5s&Js$+6Nz!X9!AT33xeU1H*6|Yc#Ms`!IqHmp+U~ zRx-BkSQOF3j)K_9QOH5rJO%aC&waO2e&WxE479(IKf^6S?x4DS<=-$}y|94Ga1r&b zX$s;h6>qi1rXXyzN$sK%*Em4?g_FW!}m6(44SyD9j0Wdw+g<;IjBSa<4|xoS<%{9Wh-%l;+{6GG7$+qN1OS z_{zjJJ?9eF9DIUut1X`b>{;&AM(5;@Gj|UE1L@rnGJwnfGeL)Zp&@jnE{R>!E55uM zUzKz=z2ycY=hBUmJC?RP#zJNm6E8W}F03yxRxi|9LJ1NPcR+hV6NMV|bmHfuG3Pb< zs^hKgd{5>fj=Vq88umoxVDKv9ovZ_Vv!r{wmZnZc*XaQrcCBrBbqv;wlwtWU5oKLiY;# z8+N$`;6~&ZZ;iM&ZfC0KZ#%2l|D39@chDjT0`!+{q}=v#4~Vx#l-%0c+1qwH_Y-+y zTsmp)0bvQ=<7^wn-*lTdlTcp9qv`~C`u#)E!u3YGK;;?a)J`dI2sQIg!CZvakb zeHtA5QDnU)v_0F-gQJW8cXavW2+@FN5}rh8@T5bpD?Sg-56OtEl$E-;9n0YNvrDKq zOzBx9ips~P=H4c-?anHVP3a-9LGbgI+f?%y_;;4ohJX)|fqu@KD!z(=qBBH)rE*xj zSOD=O%kihMI|7Vno;-C#oeNFad_SBfQ8Zz3UavXvKX~eU)YSZZmeorZz9DEe^6~_~ z!p@8ymFt3|sxhjmAZ2uJCb>Xo(uvTF$>*gFMY~qXS5;H_q`rd|3JVGdzDQIuLShgL<@$?83YgzdIzMMmXnsrM1M)}TpH6^KKW{nptq0W>)(NGoV-U>( zxC;UA+DD^5sMi%{J#g z^5?|gLdNm3r!4nNnMchQXA4|zg7hBy8JUnJK(K}$zj^=e?Bw~&m#=;}^Z1Xm^LMXa zf9IV(fBW|2ol`$65hl_>F)nvmBd&0E63c-f1mqgJ$d{?fkE!Ov!h8;%tBLc6vnJ$msOfPp6s_XGyq>%PpnH^xPf?aj&2-igvG|`pc2}j zp!rxouE&G=i9FeNElYY709bx(B~w&eKvR7-0y-V_D|P=6<$por@?DY!N6)sY3~18Q zS4ei<YchUl7=15Di6p;G^Zue^N$+7mH^UOyj)nTP+y zpmohsCgG~o=LZJpl3|rvWC4Zr5AHeKdc9}>PPIqJvc;+_*oX)WDj{f}ljN2a+(Z-P z6#5BaB*)_<9I^W_*YTK*ZidN+GZulKDcdUx|2kvo98fvHxnCUJ#lU8&_8=JY9S4!~ zB#8m;!lDfb^%!}>1mGfE=dxPCm87cm zAmORi7!ljxA;m>q9iO?=8-t18d7N1nkF!flD48=lMZ22eW)kMB$rMui%-mvnFE8x0 z%Q@C@5Zf~nR05rN8eloj@^%d8jrB*ko1*5;7<6IJ+}3MdmRv18k)kVwpYF?%mi2QD zIy8wAVAK-}JUaZN(*SMNf^8$QXs5paT6r~Va3~SJ8qm_jReXb5^>f?`m=_fg>$@(| zbbpn$v?~~NGE#LSX{cCMkYj3Tr{3JDsHs{7pBd^04tl#zK(oYpARVO#s8b!^7Z6Nk zulVmDEeDTqxr42*pY1wLUTPIG&@;*J9z$oetlw}lUzVs5vsjj9c^J9m)AmD{nts5tq7+9KE8fd zWc>m^LPDG>d$?{~EZm@u&j++lQp; z1T7sZ+Q+-7_tYEM*eA~8>Oyqol>um>Vb;l_(O%u>(GJzt#nhljHtj?PD@1qq~I{~Xl2 z>M%5#8E3C%EXJLoq#8lzX45DH72@Z(y4-L=P=x{2a}iC`cE?TS7g4ugapx*_jsvBU zi=E4wZxQW+v_id0#9zeXcKNVl9a%lD>gl>SX+^N%^GfZyUI0}_0gp@eT5Gd-mifbomCbfq%@w$Z+9G)bleQ|1 z)B``2wnnA9rhztNS?zLpJ4WG1)3o(q_TMNR-q-rO^#1?;-tJD-{=2ikyRrX1^!@)} zD9o~Ml=uPNyyNCs+||(=&C)Bcr&DQM_RGTZm9czj^o`N_sFgvl4*C9j_`Z++J@2_- zDxhdPXo$vxP<(@KB`s7ExTb>_1JwysHd!`?jd%IFO8&uW z+j6zN7LLl5QZj^qQ?K8uNqzw*cSS913^jS*X_;JuMPxC2PJTV%8hM2Krb1Df+i?R%82tLogUCPeNR+( z+9loE=@aACtyW@V3h!wU75~=yT@i1X1gJI?(<+5+FR-ToOQ}Gc7co29T6N&t=da(q z{>SO7|2gsgcJdFjMT|!AB#&I}^u>>pcs>dL$sEBZV_4E~DV+_YaKx>4oKeuk$ufDY z3_9@j%ujuQh@tjr1qsw(Tffc{G=78DO{Vr=vLxQRN<$bxG?#ia?tKg3ghv3i?H<2* z^S4(gXMN2~jW0a%dGS14?*U35R>iiHjndhWK+J%XK5Cfc)szw!IEqW5xVd3aI~*t3 z@*0YIj{`Jbs^xS=M7p8z1zb09T4L){7%}l)4^VLgJZA%xwqr|?f;FpX_OdFX72A+3 z;DnjS>6Xkgr+&`gKti=`!>Aq!xA`<2ayNPmQUX;-#hH!9HG}~m?k`Cgw=YZ`mVK3W zQw{~FF|9||^Y`a(gjLHsJNe<{_?%zdMLOfrP&()Lbj4nJnoMUASp$Z1i|%$q8{)Lm zpH4GzojFXc+mPMi0kw}1eHTd38=Nd2tnp=$veEqQ@~i_er#QCd*Nb&5xLFb317+=xz- zEDK>O;igYOrQC3X3TMwx-~Mo7{rKwSC+qmli<3S)s~@XIW%!;tsu6diM~X$X`7cGAXu^ra4UZ*ek=TPI@dNW)0=_l%`H$-2lPU8 zj1$)cL+eq?z)0l&w(;orO8S46vls;2SBU=xs((+>|9cq!r3c%9jsE}J#Q$o(r@hqA zqlM8e!~u|^2$shiwsO4T(HuT~{?_~M-JADs&+M*saVeL{vLSCm5oMqeREPNKE}rw2 zLA`|H32TAUeZOW5TY5j4tNyAc)M(_k@SXyt_Uvr}|bSWlrhaX`*> zIzFfMWA!CC{7gKqXlqeTH5<|VEy@2$7=W$^^N!tP1z6($w|lU&SC#+!y{8-b|IqTk zYJ7PowwCW04_dH-{9i9xo?!Qzj=S=y?uWE{h3`^ zMX)|U+7><~mGZSfJYgo!v)WjfuJ(6~F%47@)iWCfD%)<&g%^# z71#&7_3bSthT3F*LFA5Rv@6|ojJG}to~oR)p)?>LfxYf6da zzgyK1sk$|Q_8e0DJh`hW`ly>EUBLS!Yn!J#o@0}yTf_&(v+TX5sm0-!ixHYK;}Bht zn}d^7>oj{*au1q;6JE!#a5<&ba-r{}$pQ-4soPS+zz|}A!`3oTkkF|Uuv0e*3!52_ z$w8q_#9W#}+lZk&;WKOGkH$FbFzi`N6U^1sPd06sT&fXHZKRFjP1fT^V`z4U(+p!f z!Za`(H>2?p>#5og`i~}eP+X39MF5;xoE4FVG%o(`!EL4rO<`a#-$!_ zFJL3oMp!|^VrtaFP8lg_2r{FWk$xje@QzmH!X#iG+TuYa$H|?=H?t&;EjGdUK4_<% zf|SQi^-pKty%OTs*e#21!xYBsaJ|lxc75_P)5tF0tkjI4*5$&nkc*z8;q`?yZ_o(Q z5RMX^w2dfIynqV7?Gl+vJx?4gnWcm9DC*E@m{O}@-@hQJ5s4rVmFR(@HEZpFh!tut zpBW1r#(6alT@Le92JV^Obc{|LGS(Q~hh2aIm!ic04sn(>$>BxyiHcRCuHTHbu&77T zEn_>lD1|hWF$R2lx6|w}gJbhR|0&z0FibM?QrKy%S!KkfE~BQd2eN;%_{Vv>gOSO0 z`U@+sK&?SBu!SuRfdy{vK?*gbm6W&*^7h@y-`}5{Rb*}<+bZ6+cnD&4wdENG29;JX z)L!e}#O>-5L}m)(c!6UUt~*VYv@XaCk&lSSlF2$oZM1piFpGP>k}O$8T1NkG45Pbv z{i`gcCeub+seHkkyfpOJv>30mycV3XaD815F-u|eZ(#)_Z#fZsyv~`bHw@2UF;FS5 z_(_ZtSdl$V4{P3Ni8f$cz|TkNFpuBUqOaVTPv{`SNV}1n3XPL=woWxdo@!ZH1cHRP zJOZaVBTrrZ=njJ-Vl+KGzD>v>22}2Rj*8_JqdWUm4h9aW^Z`szw?$R>pn^ujF{s|? zWgRt76l^|uqTsWmUtz&5M~wwY5cdkD)F??7gIsG8ZT^~;tkp2;!+H@qfT*@=7VyfV zu7TWX&^DWdQ6q#4rkz@6h1pkn`Im;wQttkH*#EEj0(qZQaO>xP+w1K--Q<7!HtCN? zHgxSa^rO`dgO;5CJjI-E`uWe(rw5z!{|EUzvaTZkhW)7zYrwC5>ygFcQG%{YeQTCq zZT(56g~1Rvl=Fp7JF)~_1cKt+B^avDZ+GSMv(Xs#-lvjzDu*$$6hB&#roWD;?v^8wmuJCz-7yYP%%+HA_mAvy{N_LxZH2JPH1sJ$OfBuR2vY(5U<4~rC$97Hpa+w<-l~3k zVT}>JOi~tJ$A4pU=!TL?5Zq`Bqyl_@RJ5O5LE|y#gQAoP0V;Hg!U!Os}t7y`brG@?#A9^+=E5kN-`{E)mm3W-!JD>D7R+&GMfI^X`K=R++X4!Y7+MgPdJ)I%O{&iF1g-(6CwV;*(s;3 zeQVApo|yFBG(9k^;gr1K3u*@U<_Sq`@(Y#9-_Rf3tfc+DU=#;`jcLaWLQ#a3~WI-e>`?ub5D;ceM2U-OD4yF8vN=Xrek zr=5B?Q&^2cvOHxAdy|sj)Q{)q3I411ohMM;E;Zb@MZ3T)(9$r_3IKzwQJ(I3o6!wcdsj07Mto(7ncuHghnj_T9 zlWT^Td#F*pRlQD^YE_s-w9S z4mz5i?lf_iHaO+S%5270xCI=eaUSP2Gq0$qg}1?w3ps42Jfl}AYgaj{?CFcM)YqXa z$Ho^2_6yCb27ak~uCBaR{@dwWifVc2vxuW`_AXY{+d*pT{t}`)hEFR8iJs9H|b~R}#U7pD*bRO8vi$kU!Y3 zH_A5)4@g>4ZP%nLDbSxmRP!{e&3^*TFw6A0zakIt?0uhs*F(o3gQ^N)rBi8tSk!N6 zc#!?B*~!=8?$R{#Re>k_(xM(T!_qxFArsjb?_|{(R%}hzoMpvE`0LNK0>m#h+bVYG zg)CGUpnqHbZ|UjsKJm@?D8We9uz#lr2?I;VfOFaUyz^ffb9wcPfJ^RwE9d{k`ET#= zaC83qEuH^!{=8oZuqEgJ`%imS`tLqH*u?*Op!5GbX2pyJML_>-mNF3oWz6t;Qkdn! z(;Uqytw-n^C+{_~petf+inDwfv$9|qD1SE&M`P}qAFwNw9xR&U9SzwAx{6WI{^=Bo ztG?`RYq8J3I^hetJXu9?A#;W6tlDj)wipwIpRa`e60fwitNmSh!(BRvi*jY&hpN%M zrNo8zDy^3r3*Pi9*;esW9@(r|K{(u8!ym;BnnqYK`Z|VK zssbZml;Lbm@KnR>@-zwREHYlI5-l%CBV5VQpf$ND1%T+_VnR1HCg|2Ghk?n&WQcXq z-enU?#Ct;mffYx%I@I;Cizf0JXp`+ms>$Y>U^EAnZf3CM0F}PAjR^CEGrO6sQx?od ztR4B2Vc=UIy4JjJeX#yz&AWI^hU#-dk!7vFI}N_aKv*tW{*IAY#z{K3;sMXDin#8Q zv`rGU+Q3$?L-)KUG7?xry^i&VvaU!OuS{`qycv5XnxuODu;^@W_VC?OEvpU*5Rp7a{3_HBWRe zmKuGvYRWWTOH^L2g&hS~%?*E8;A~gd5?KEFnUwu(i+G{r^q^K?Hs;ton zq?F^tBhzn#(4f4ITBjH7(B=84!cKW1PctJ|FW0E^^|G3PTt z!h)|k_$9zLU5zylKpJ_NV}S83sQ(8;~qo?+-Y##X&QS zS138nmLlvBv5!hEgm7hc3s?*{_vKi z!vrKGQrVkEvy`$z3*)R+x+&@0g=Zrs&O$@CS0)7e*y~%)*;Hhc+QlEU$%LUN=N|Vs ziJZa&xjs*-4ohhLE^Di%?gmH40^WvNt@>VAV`VYQfjVRv#6!TuBjpamNL&S^+-lYx zMXMEeD*cpM*9k>>0X6l_Pc5D(OPoq5^GY5;EEEXk zCaLvDb&sK~FNC=#r!Ihn=3tV+u#Uif2G%@U5MaHbKvF0xVJ*F37?fawSA@YK z*W!M``hBaB@i_x>Ws$y0UGaXLSa|-nT94oZO{P#`m;V6;XM%UJDOQA)@w^ZgmeX@I z(|g|If6FiM6If;pROlS&O0mZ4!5?BnfMMtCh6#q*y;e6dn1ATsHeBtB=YP{A z3P&N!zC!%(Uaz=ARXjrSyP5)|oo*i_JQ}>T8ZhD z-{Q;pmuMsuVIkIP=4~3@!j9u42w}a+o~P{1U0mcD5c$$GBJmN3c0W{em@)MX*jcOpyw!uStDw_~+9@FW*`c*=03ut&WAq4Gq5C zIs2P3a+QnC)jGeu99Yi2TnT-fCOJi({e}tNi2YTi#+MWs2Eia``!#4@kC$8Ak2Ndy zazIto%Z3((^Q(qOmy}uBS$&R)^T=j_!?{o6xpsvk6_f}>R|>cdEKNzZOigRgw4IcX zS#!mq_6Ug5R}LpdUXl#2+hQK8jcEKTqR~95K4%01tT!Cq zfc5#KDmpF2l|ujWp;KcR%P(G9?dnWYW*X=i)!Q?LG{B-L5w?ZVuyfzpR}sD{3s+t_ zwRg(UKXV{7hOTv&q_9Nbql_^cl5mxHMTQlg^rH1|Zw%e!|7x=T%A~~i3IBPI_@BFn zyN8?jpWh_?wZ~tJ{!jON`y2Xy`}C(nLfAlH5(3QPFrl34Yu^7mJbb#Vpa1Oj4)-?k zUmxW2@v#M)EEWfuWfL{GF&n9ug*hWy+ScPwEhrQvKI|@fe~S6RFb4Dqujb;kJNK_M zEQbY$Q8K#0Hd7y+{NVA&kNlyV&8Dzr`2^*j34b}t3S#3nZ@cuOv#l&0p^M%&|L>O! z``xyWo^A62M=fY_0t$?ejo}F5fOlJmfn5JgKs+PI!J2!Z0?{bTq`M$|-A4A#Tza^D z)YvKPVaXQ)dVEZIvGJk|3q0Wz3eTa$=^55XFC+eww+5xDmbu|!63qF&oCcFj^UN)R z(ZG1Np%(RY*(Eb#I?2oNon zseRF22|yZNhcR>rI(>|Jra(0hvcBE>ldX5%dbSO;I%=)LTQav8U~Fqn-}VkC^cR zm<-=(FiG5V6l~8T133h-aAqn4fYlWiRCH2;w=5oUu{4=Qd59^jNEmIQvz2Wr&G;kS zTn=m)0T!UqfBIxw+@ED&Pm>Hp9FkYE8i?^(RB#$C;WV;QmR@;za>HT{C?`HOnkUmb zFFljI#`CL5%0CO^=}e0GaTo+Fwz)q~^Qk7$bw}lS+UJ{p`X0zcW%)K{S&Q-vyG{zjq+rNE?KL$L~z#zp_cfFoiT zufPB@J&>8#Kv7mOB)OJy~%Xq?{F81k^@X)4tVG7oLVj z(4ABu4-0=>RL~Xf#h_IbGWFwQ6g3W2>o)`=6|1td5DN6Dc>KvexyvT!WFfhq#|IYTan!FV*_aJ%IhnBtmalTtrnPY1d-;$nEr3 zhgRGhTpL(W`$|ZasVe|SJO&0)Uo@llE>8Cq!dRNxv+5`$e*}^NK7I_y=lIj7zJ=du z!TzKvUIomh6crF`7xGAAzP{`<3(J$&FYYHWHSt`Ppv0G42S?Br??sP-WHduVI=UpE zL=3_4$gX_^@gKP$$6db)R5vH$GsAM`f% zpNF>ppyj93-b~xW7GBB>5=?5IF*XsYmRX!87_5hcRV8TK8#1?5NUgu3_UnOM8(@e48x~cDWz*uy9r^#l%(x9PkvU%V$y^eu z#jFp!X&TGVv->p{1COu<#b`S>-Mk_U>DLIvR(!j9ZfKLfBv1be(E6 zPSDcDtJ%2erpKgu6h$$gw};R1y^P>M<$x%B2iJNw-UZ=++z1m?9OtW>RV=!^lg9#R4@ismQ06j+Ev5U#3HCJb^?l6qwLR@6j` zU#&;k=~@kv_v1&*i3@vSo-P;lSF(DUB(JIu>RSbLI0F@xmsPrIYQ2EVO5$i>VBh6$KwIc}ze z;+&_&4j~VY1Z$t;g}|)PvhzSKgInY3v(lfe`sQ5{F?uGM#*Dp^rLfX-vNUGAtyYa9 zMIII?K;Xk)IizPf9%UR!3s0H%Rq`(q6h{sH7;(f=RFI2{5J^kPQf1a`*odk5)M+pC zFCZx;I)sgkf%%FWbnlYyby_8!5dlRJvRcS%##mG-!)@cdoY-HvaX9@tOqVwS8d6IW4BHD*$WLnGvp zep}2D_=2KBmY~(JP%`DOb+&t-tN)=Iu(k`(J@WtV@9u50j;$zDWLOcv#Ko}Rw;U&i4a)y)u;BK7ASX;)B z6?E$BO|okYTA{_NNb{_ON@9gf%UMXi|I{7nk3ewQwTy+Oql;RTIItgQ*0aaik&VGi zY7Hv5lQ1}zr<@ZBQ<)AjQN{gFTfk9vKY|BgC2Rf4`>^e{@5RogcyiI}UzQ{H{0!p&rXna0FR``TzpVAkU-h?kFSYp9 z(17)OK&_uSmAhAdi07n|y(HgpOp^b8*ZR30st{P9WTg&LmJe7CwOfo(p`V12&vyEy zMZLoL$)6&uG+Px`>-=_r-(V#1kDv6ZF;0EHKjofQ`UI%Wn}MiMjjF1Yl)<0XK^ul3 z?2>VlDLK`E*0v< z${~v&NOHP}Z?)H1ovJr%PR(*8nx$J&{Q^o~s>|Asei~A4)LV?jro;(~gCc;jyuPSJ zW8#odAlahrP9s?O)(n^m6MT15mL<_GhE}Lv?-WliR773MICbm;8=;RJ1u|?ud7^_O zsN^Vd0QCxs!W%nfgtPBh&TUp%yRz7cYU=7{!(Y2^VaT)8{-H;#-X#%%g|E zn0Sb5`kBcnAcg+r;@ah07xpgx5Z)zpp;stCa#C>>GZA9FUH8)b42kqr? z@@;Xw)~@n~VmNfV3hm9j!LJm)2HdYbfiXDFERGpA3fq7#1>42h!<}1%X9eAeT$OF4 z+w*{;IaHPc`4VNd`1(a;!xF*Hqiaa9O46CqT{xF@(OeDwS%4g7#8Nmcotj1K{L4jO zt9Mxy9hH4qo7XD9zHo$@m{O{Lz8-8#Wv~4V%97IEb*?a)W1)mw7sVi-VZ=p*C_B3B z*y}3Oy6X7M+BdR2>J?l&-n|s$ls{Ls9=JcK3US zoBV$d@`1G^@^9Fm`mh-N>Wgf9NNrevquFE_`f{i8M0rOyDNHXE1F`-XR-zd7-1@so z7z7c!!#L5Vx7uz=eM7gg$`>0^{`Kd-#ur~K|Lej2&TjSmZ})J2Gyfm>{KrM|$_aH2 z-;dFbSvO!*1;h7#Hili*dGoPRwZ;o_m`Kw3TmPD^QBNep$uS20DIPndD^&U^jDt1$ zIAi46bi?LrRCRkv7TfM%m^q2+wd%GiSqI)Gu=nGBZ|H{gl)l+&4^t|E{zFP% zONg-s3>2&Bhh#)nlLC&@pR7Oolday)vtfGl1G@{eWE@){RH=lcwNN3Txr<2jFtF9u z?0QHqx_URT3jhp7Fq;OJFuW?VYYc3tZYUtqZ47K|xl?5@nvSbZ0DX|!lr2!LKV=_$ zoQ5kQj8B-e7bia&MiH?*#~EZoft+2tdpjoNWlUV^-r4CHUZYDH)ZOWsUZPtfUai*O zbj*ou)cK08b4_Ta2w!@=tdN;al>{d7)E8jg>75q}^wsH$ zoxOv%uXlF#SDij}nOzWc9TFC}+*uiy3gux$3#f~OUBDPGmr}mQ`**Oet$^-)&zbQ2>XRf$AX!oNai$|#laS=3R5f0S0#qJ-YTm~^|3~s z3VGz?%7{nVgcf~I%>!8(IbTN4^4XA5Wb4arK@@&ocn{nd`Vq;&jUCnO*~XmCGLtEQ zR07^v72kEGNXwI{fLKMdbNn6$i=%cLaeWsC*Y)ha>%%14>A0ha`ElDI2CC2*N;Cn9 zMsI!`Rs*zaT@_K6HGq^l#;z)ALZ({it1qvNFIVbYn->M+-@o(qdbhI#E&u%b2zH&b zXl{kv@5VO6(FjA?znMaUEzx7Xt_9Lv4`{%e_&1m~Dz?5qKYuHo+VQ3#hSE5GZ@qYP zOp#Vp4Ke}wX4-?<`Jc*W-qfECoT7YZyV{ixLFwI-bp4*6L_Tt<)J6E7`*I z%2ahwhd@Uh-Riwg3i{fnOe?e`83 ztLy*n(}T_W|BbHy${8P@Js1L5r1UPnF0NM;^ODrt-`QVWdhEwR#8j`QZteQXib}*i z)RWJ!=Kl~5|3fvEjh69+wG2Ofk=(^bP8qp84N<$ZfP)&!=u7j$b{4l#zSXstiiD^R zK-v{l9t&g3RI9ETEo-$>U$ifBCyzR76>nfQQI+55(5ef(n5#F8LrBAA!%>T^KB@(^ z8U|M+d@U_4?86kJlZ0fD;Wth2U=A;h=r>-By)MLwrVeu0NsCjA+L&^rZz@Hhy-Z{z>> zjncpL*ZV^89}W(8YxF-PcPHoigLfx?e}8gTdC211Fim1h_faxjh8k6M ze?&IkmEXQflI@|NYFE@ohWo4;;|Vb^l)z{dPx8pEc(ze$-o7>ZwBWK#rKu{^MWj0f zucm}>f35=j3T83mT=h6zh8o1607dbV>djkhdH?@>%urJW{t?H6LqBkvc#IDt!9z%E zIEzPPE4jkNnyC5WK`EX?$_UlGEV<1@sMm^v$g!VNf=N8fB+V>?T0VE7NGZBPz%Xb6 z3<`jO=CMtGIt9&%yspa9VJ6}dV$WgjS|`_CI?%)#lcY=y_=+0NX`sAEVr*0yVM8^! z&B6&UC}9+bp-?5s$^p67PYe_N@y;jHG`VH+Y<9$~I|gIB#UQ8w3lLbw?~IY?fKkW5 z6G~V&j@vHo7w*C+;{E6F4a3GxpzPe$=(IV!*&N0HEPA>do zn~eg7O4mS>dTCQh3~w5awrktGhLy7c@TSu0N{0K=j&TI5)aIuxwS%k4#FJMZQz_T0 z?pg(F)YkI)=5*xNnyZbWYtgY4-)7>s6@Vhd-w(1>_-siSsz#Vpq!rgD+?r;B)Xhfe zY)ImOLRqM7^{w6~WogZ(1waBr9qp}KE~QzXwskR}#;0x9xuG;X*mPgLPK~rnopfX` zU#@Fa2o~{ERsdiT@k@$5Fxd_4%$rF&x*HX5s?_lmX>-rUK9gNRoxN~oaT#2-(Wvz> z@Ca@C!l^*ysO!J;ZvmS?E`}7PVx-cqOy$td^hE#vz78Z`ektZ($uI1zSZ}r7;lWV#We)_sA2pXBF=8@a2}Jct4TMG|VbGQn1aV>2&$gp0pW_ zFpl6Ig+I#aUs|^%YjI9h16F1X(6<^8pf@{sTdXvWa zCYq)|<~2-omeIL@tt52~Y|})j1J^Vm8u&wnN5Wk4op@qy>dSGRY-PiAUzO=fPAb;L zfz|GOX-?x&y6X3=7K=Dpi^l-0+qG8U-;rA>C&y_t1~pU>kQpH00(~g}O0flvVbXwszN+#kVKw{1X;U1b^hkXo4)l* z8>m0D&apSnVXW z08UJply|EGARz~RsP$*lX_B!(@Q_JO5rvoAr}Ik%k`?7sN=u2+}}y&#WG!r)n|uR zvee@|vmR$^Px9M@GQ!Vt89=_sV#^Px_=P?L1C$I3*q6!U5v4;m_HV-^ZGe0=h(#_P zL!gO*jfRX86#_BHv5MwJBer;JY6yu6t?=>!FV^OFCNPy+T3<`(r6RtIrk8bnK{YRV zN+24(cGRoaLR+d1jUf2I#z{$#teJTZ?L}?uyp^`H z;pkvmqdtwNr4V^G4b-roi%9fO`(Bv;XYEWuOIlo8H?r~;t~_L2q|S(-wj2^2$2`-r zCo(M$=<=@2yIdcO#ONsF$UDeQ`{xCEiVFlk2U z)c^}Rg`3}L4$9K5@F1d~v*6fv<|tSMrI0MJ^v@Rl<5c6n@D^Q4Xg9!U7kdHG2rl== z_ZVpc-uH|zF_sEihfjZcX&VdBEvU}d8spiN7Hx#RV*w22(N?rE8dQh}JIIF)RC5-V z6$$2rq*uDsdo=T{Pi92Z=8AM1;@2?yD_Qo`P#1KB6hNOM>`*{D%vilHuG%SGS%Dun zHZRb+>S**FlAoGykV#?GfB^1J?pUB5gLc-s!nVHspW^O)$@|}`|L4KpA;15l1x-`XBD>mhoSE`%f|c>;B&6{{MrY|A6F6bJ(a9hb%nqIlmptPuTgzJ1)JN z@9r%+Au=5E%yO(W&hu$;uP}{%oh7;Myp9==N#hnGn@3o+Qx`lK76Q zBd%2PcSdJPzhp_=(htgbycXGn5WUAd#Jb3J<==t|iCIFVekjrrC`l!1ML_fy(N#oh z05lB{?DF9*}`O=ZjB(j0;PR4%JP&qKYVW0jQ5_b)Bh`{#HssHQ>H~TB%2@w> zIlcx>kEbi>Ybj)#ifS2SD|HHHZK^C&lJx61X{w?P7%FPN8!Cw`BqCuztt(P2cgTjt zLk1Z}P;*$W3qzSnL#&Z;QjiyOg*&S_d}<`?7|6&yLW(4_+=Ij^_!tz%cmMFt&fmRy z{hg!o@#)F=^Gcd09Io!Xvyk~oFDCO9I&( zC;vbkEbzG(A`uucO0KV20IgRfBJ5et(W*A09SRz;ES!n{XbwANOs9zXRTL{LQI9u9 zW~bcGXL6o}xaVS%3^SAE`5c|MClRd7@-V1?0ido<5L`t;GI9TJvChbtbHYs&4d2Sh z6($qc23m@MRk{FPL*lm`*ym$0STZQn3+)_vsK~L87)X(?EK!PY1pVAcsaJ0LiZR&z zay%t%ZP7iv72ni%`gpL6{-ler5Qg&<@WJg`WqtCRg<9cU zmf$#AHZ^5Q2o;fs#%Nd2OE$n-=-+MUqfJ-A?Y<58g1O0$fyQCZ=+IwWJ;y_M$!FN7pWoJn~Vf zupkgz`ql-i>tvz}FlA1!>{5@v`>B!YrPNfU3 z)_9{R0cPObULo%$8sg^uw_^X{Z#+#?Vb|BRo!eg9{{@c^(zXwk?_TLBjYy|#B{@-K%gBFs`J)Y`f;oM(s{vTH4|KaZb&Sw5U z$o%J8BWU|dqLQHJ7-Fi;m z;C<`(`1R@e58~$*oA_bWx8A@0+v_(!y>9&wj#!+r^1%znO`imxFP)P8=@dxi z8~vGt6&X35@a*$%BR|JqWn-K9zi9rWt&HWfsXLvw zw8dCS`mVq5F@s3$sgfRAehMSyy9CcZJS!F3fI=!r!cLx z?6$3ytvs!j^})p@^JMBp?3P8VtsR@sH@W_=Ma8B1fA48k|KC5{oc}$@N88+Qro(3b z7xQ1y3_r7vrSpGpU)cY9hldC79R3_`?*BZ<2lUvh+%jvDo7wT7Jpb1+@qbzSKS+Yj z{(oct-`M|u%lXf(L8GhduRi~GK-t&ze<;6M{~zRY0b4o`XToJG_piO-ER68(4*kFz z9u`K-z#rLd?4|5B6e&M;TASU%f5QB~=av6;=6`SZX&L`vzqd#F|I^Llrzc$si{?%J(G9Mft(LfLhxZtXNASubmE@ry9Ro8J_bx@F3YB2N<@(-O}|PRo1ZEx5yI^9fVp^Dw4@rP z2W2o)_$EtqHjB;Xv-xa3o6qL6`D{L$&*roFY(AUM=Ck>1KAX?xv-xa3o6qL6`D{L$ X&*roFY(AUMlF$DKb{y}F0Js4FFWxeY diff --git a/requirements/src/djangowind-0.11.0.tar.gz b/requirements/src/djangowind-0.11.0.tar.gz deleted file mode 100644 index 3dad58d0981d6ac4602f8f5d5eeb870096e0891d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10853 zcmV-rDw@?FiwFogv`tb1|72-%bT4FTVQyz{cWG{9Eif)IF)lDJbYXG;?R{Hp8%efU zk(BkICBH4(GoGDVdFGOup=rG=L|kvHbXWe(VlA@y{51W@hf<-|?Bd=Zst*V7%&ncgr=$L*Z&py#S1>hJH!$7mb>R?40C zHvZGo;}hNSfA8a8w+*xA=6Ck0#~%Ob>8Xz6KRz=f#(!dFX6i1WaAItFa*Xk@?)d-U zpVVIurT^L9Bh4S-1blx6U$+OTgRycJQ%YHU7Wr}U_s*`q>F5nFNH=jLNS;Q^@~gcPGa0j!jMj|Br(J=<@$*@&8}_ zgMat+k@$ZO`G3oS19~$%>T#N|Mr{zef@7@W@>t73g`c+yIuZ& z|Kq=FnJ=f$^RP(lK)16)flTM*wX~7C)v{^ zOJHh})l#h9!=Cnt`xL94Vf9}2v{&5s(0w0!+Ar?U(ER{=n)dI{(*2;g?`5?iRzJs{ zo)hLn({lC-pvd~a@X03O%EQ}-p0}f!?vCAXP0chc$E zsyJ3S-vVmc4QNqaJUa(Jn)#w>y554_@XS{d=Gj(y3(#duEG!na!oo`BezCB$SSigv zDzr*8%w6bJRj+QD?nSsajb^@T*P8WpOV6Wtaj#M5H!at5s3mu(U->PsUaO|yS+D#q zV4r3I@N34N!~X<(okY5Pn`CcNl$b<<+)gs*A2Tn7#E6MK>@7>Y>0z&X*z+@R&76PA zUZ?Xrhh-atG02R01~3o6Okm+Odw!P6`~};)$YjbOrX-%c_zTwK4YBPsbN((% zBoc7{ZG-R!;DLh5jX`V%6oMOUfGB;QN_!XB^NZ~HCFcAWhS!(b^DFq^%^8NpuBu{Z zm^aK`Cz#cXjRSs~2H?=|>DOo2^J~aJcm;ejjlH>!979((kXNRGKfDhlsJF>PIh#ft zqC;iTa(8O_p38v-I3b*G00nT6Z+r`WYG&|}Ud4gQdrVQ%6H1`s^Z zMx~+GO-jZ)O2(nCtm{COgX+#Ton5PHLiZV~3Q7Z=5Cn0fYGyygs``k!zFsXrZAc26 zK+;GG0DQ|fG^BFvoT7Kjt?cSG%g}lig)c2XD3&Tdd9G?pRJoYiD%3S931BNtXbsip znyw3-B>*1KdZlXH+m=~*X*=7dLCK&Fnb66=Qv&mqxmR!;+tCP3=LHJ}b7_5wbt}*V zo$x{(uGIiCKD5`LgSc>sbFfv_rUP=p({S9hq{2R77YjcrmKH1b=F0`@7u}T$+Be08 zf`*T?y$Xqr;9DS9>{K0wUJcS^dMd&^P@*q2lpIXp_Y!;u5`FM5P2Zt}doxCi zY??*|WTKEISj8FnC0X(SyClR(f^GLAk9tYTEeYT@XN<&10+9})V;FfF$PeWN$sT0$ z9#$sn2*;-6A{Bh2X2FHsE9t(L(I7o^OWk1L3# zEVf%4#m56ASAko0^i5Oy4AVbXlKZ9ZR*1XA>~LZObVexzgET~`Ziw7u9I7m(s3?hSNkn9pS+K{g~ zB-Gf8E0UTwNhCIF25;D&6vEz?V>dUqsLoOr=Tef14+<+NQw*a_X0?Q6ns%y^uV{2y&1v77HVOBABfZB6L-sY zylT^PNziJ41AkJgBO1;}!~`0yhj=&Q@t3FeYDg*CjKYNXVS8qjo^D=P)?!-d1Q91z zm!>+JQe+k-#7WMJ=>a?GebmY#P-mD`8w8^5xOq~-G~{0~oYVO8lNg82F^8(nn}bk? z6Bm*f68^uW|F^FRn;{{!s*iOIW@Q_=OG$?@*`&#Bq} z|NTv1{U@cS`~G(OUs?W1RtKxN-Tr@ihDqDMmu&w&-`1yT8tiCg>!(@$EPHwu?)%vH z47~uL512!2BY~zpq`<8G-P3bu*W=^!XwZWVj|Mo*%|j~dIX~+~wB%*#1wZu?TJSRU zlAn4RO?a7l#ZSG$jH@bj*iXHRT`Om=g(mH=mF;Wit7u8!Oz{Bb>z$Iwik^XDeaEqP zOsBR7cJ+p}*#!N%Wt_j%oq#b99{)5p9t zL#?fkTCIQ&67b%H1U^K-v47_fFy@p`&ihX;s3$Pkl_6d zSNd3?2*ZC0yb_H8t4Ic|B}F}U&L0qj@K6{<%nA z3iKZ-5ObfpE)r1MVoF)ugSKT3Zc$z$#3Fi3Qp?;=4pDAHw`wkGgDao|OT8E@60X-A zQ#bZdEjBpd2-B6k_W!7A9i}A2#!Tr zR2zj8&uV~S-cM7IpfduB$<&Tb6e4awIs1cySKH-;?FFeKh|K{7hxHDD(mVu~9Gctb z5@>d#3Y|`j!Y^J8C5IBEcRqpQ=rO#f1B8a*PZUTT_YmGs(YeQPelU|bD;P8jpr}5B zvI^xl2>`t4Lpg;P{WwkF#Q@XFWKfVf*%ve?d)h=^0wHhrGv^~(nnWiXJ^6Wxy&=01 zrBo8f9W(tk+q;2+?>WgfL)h(Lf1Y7a8ef75cm^dMT7iEbtw32*N{E#+GQTiE-?Ai> zLeCpH3DMXDV-l~>c6{|?vM$NgTwJ>>Mc(s4EP%j3+C6#o3FR3Qd4^S zpV$+)Q}V5s%94+vk9-UfZowZM?{zUQ`Jk&pMI63@e0d;h-T3 z-M5u{By&okK{XtWA=`ZUJ|iZO5qp3E2f+%uL9BNb*zjEPa^fO zM45g~vd43Bha)SnMEObMD$q7LRgkvxNZUw7-?rRUB-nShg<~n9Y<^+o{}_LMgncAq zII9K?@ldghqjf*%Yh5JelDeKsT!Ri%=lX-4`wxuN6X7yK-6JU-Z(RHV*1Yy{Qn35p zO>{s2r@~xkk>;_PK<^xJ`2)a2lF(oS-}eGvi`9V?GT%m$JbS+aZRlgj*F*XKyp^wq z$)~{LdzVp=_`C|r%acY13iVQ5UujMw$1%6+ zJGO)Bq|j3&t?^IYSw8ZqJK}zHK+e4ThmhkDnch@_8^BOBPa5V1Oeru=QnDUe`{9y{ zv%Y`Yfy{=7EL^FGswx%Yqaxi1H#d2mFwZx7t!aw1A?1gh_t1sW^h7$%kAjMNNXB?u zgaJ#q^Qpn~gy!FrNTVEL>a5n>qjOx%)(s^YzVtmuFxA}v(z{9C1`QLHYVvl8r6>0WCYE1iFbOztaqXrg)aPpbNvP8_1>6GJp78naveMz^ z@82&jRUXcl7MBVz4-!KT`p&kWDx(19SHsY7zfCq3VoN33&idCC#PN}EreW5f`>tM6 z4tfAPADjwKt}2{7)|LS&g^pN3r9=B8{E6VBza^zz8h?3n42*mtHbJ#xq(m!fRAq(M zO@K`V*9e-fRA|!ka7J;PJIFh3#cJSs+OC*$NyrMX=^fP>8Ud;_>Y00oHlyS7Q`P3) zL~nBN=0NYjrGb7L#etxp{dQh@wT9P*xyQX5S5vx36?sEKLeUrp$hx zL{SSe6YRj}Bv;>(I0jky#j9}~`o*uIDdJ=`{0dKohY{PCggn$(0$}WmvmlC$=cb9&3`dvSo?8L@Oxml?h z`c4IA3}>&7P(C2lKSi=~Ak~`~0OR2XNYBflzuZWW*PF=fe2~muMHTtI3pj@ZW7WtT z@ucxN%%wtK`LbOX?~!wH;#Z93QnO9qGX1|3iOW75v183smuex0;S?~4tz4etQ4*%0o_jv9-==( zZ2C=_z1pBspxzH4UqK6y1J1HH0A~T0 zIp$qtuhUq~7x3r#CHDH95TCzcSUQb0-+-F2xQAXLk@Q7_t|SM!!(lC+MzvAe#4sk)YVq}RjZ=nUy=ja%kB0IHbR^9 z7lv9h63&_0n#x8l15}5~3(JoSmGbJn6|GPpaW6c4rtVa?#HvlN&+xP$r4`E1-g#}| zVWm)7(Z183`LcuByn_Zkji~S|G%W2G;RYb@)hIK19~Mi52T^0W3N4d6jzC2-cTHba z6Vs*0cHgRbARy1l4p*c+vJA>2?%WEAv4FVp_I6ASu^}+JdeqsAU^jFvIhi@B z;FhNU#@hWGFtNI$Qei?f2d*kfC9-r`I#Q^apxFB> zRe=zo^6?p!Pf5R<7zk%L!x}?jx9^h|zXs7gK%)CU(q^b%(`G0kOwmI^ zX6IQFLg&LmrV7LCBBT5)$k{|d?$!3Jd7AnJpFZ^2_A52_x;J8qfB{sYVlAhj;Agy| zfg^LD6b63)<+Q8DP;2-)y<;=Y1~QG#rNhQI zabZdTZ5a2Uag;+~*pmA^YuJqpx{wkm%B{|u)C#ufgLO)+H_8J(CFZH&OPCD(6RO$jT3&LsrIh5Yi;b|Ku`%67gri4 zdf`A7;Qn^bJ~bl$vrYCT$Jwc!s3QsPoiT!j-JAqz&iNf*K-$8-3 zxc0ZDHV2yEYuZqR4qj3WUwn=E&>uV?_XQcWpr!o5vn0hp%|k)L+G zT3A|LEL2uYMUmof3?xU%e&D6Umy)smP3(R^(J)tYns3UO*`o;eZmKhPkGd0wUiUFtvN%W=m`hL~( zV5?k-XVT2^n6wF5|5boVlVbay2mR~BpS%-)%9WBHWK=||`vMsi*eSxBV7GExq`Et1 z)dD-w2(Tv?>qjjv1@X8ax4XEJ&E**v8=c`~#3`>2aqQ=UV_UwS4{4>Bk$y5r4xf~~ z4S1Tb``c4jfQl=RK>I3af^f^(h#HM_r2VPCA1-@j9a4B@M3mY8z_Pv4?>CsNDleAb zuhpm|dNnVE$sfukaIwK0w=-T*8GR`wwWfsJVNT5c;tG307)TM*e-P$M!f%V;-CO%U z&RcGUk_2D1`1J$dFpxW-DVI=7dctbSpWx5yBhxmr#{a@H=cI0ejD#9WIw3rB5mx%M z6W}LYzW=Xk;EHuFWR{3OTkt54$W2aLO5o6JF(Y~BK)He9bmD~56nVrrjltfB$m|%C9A2N+TZd` zfs^OC6bzGlA;NP%DA)Z8vi|Qd*JX#}b`#USi4Qt#Ta{Z|6TZrDPWi$8+>V(yUscT= zT!M1NYEr-42!%VU$i3pVEWnD`6P2&lEc6G7q4`j07h*M;yxziHLgpG;GNe1x+)M(6 z`ge5nwC`@kDlgD3WZKc18n`+9aNzP_c6fNWZtudGu^V?j$>ew@xZjWB2IyI4bl>uR z{6_~E{__Lih^VU04S|P;f>6Ot6cxWkoeHQ*Vj@NIZGaA_I1rL|yo%cZ9Z<2_dxZ-D zrgzj@!%*I-%8CI>`N`BvpblGkbqjzVzhVLA-6}nqE(c&`Mms?o?FQa-T~Gro#$Nk=k7?L3tqV_$N@_n($YVk3P7K*;n{ zh2!F5L^)*=s+@Qqgcvam{nk@zI-LeZhR1D$&#K+}qq8I!M@B~8Z@7qRfb0(jEo3tX z#YR-={Q*+8qc2Ogm(R@sz*%49%TNhfJI@57O;8h(0Q{H*Q0zuJ2cV3o+GG?(R*?!o z^5ar6qpjM5aAlxHhcZ;8=~$4lRp@g*5*Oey!awypkrtg4!4*(gP+ne!Z)5@DXddG? zvZ4lxynt+e$OwgWWJiA981Yd_d7h1}5i0jgH-OD|8X`$Cf%#XF2{b@dGQQ?N6W&F5 z@(dr*vKw7k4V8CxO|_2y7zcT+ZIFo;{aSJorl{YnMZ4CpkJ>Zb4;1KDgvLhRAr5FH z$DggK2D;`E+asav)1*U%56Q-))72X2Q9Kg;k`>-2N!{@qJ7M^64APVZ1yE??KZYFS zBxdF?LSppb{_Yp*5f%a=L4XcutYJ2!`^k>ugLF#;mEDga9O%wI5;Rn;&w@}fq99zc z=j_e4X`vEfIDO=0Tu2^-L{>FpE-Y2mN6aCjSTJ;?y@JEyG!P9FqbvuS+1|7qS1R{h z#*drn03;t9*OKj&S>H9`S?D1O1+{qsDTE}O$p<KGR^8s-}%p^!QQbSaZjsagw8R@k^sDr`6`Co>w$AOUbFGy~B$LU0O@`51r^ z>J|;miy$9rem=x*0m$u$kfp<@XqyWnI8o{Egp_Dp&f(A!jKOKgOB7}pqH1Pm)C8fo z<3^R2hG2V0LpBg>A$!vbw?O2yG^C8%KwC+LfCT+{*^D2Df?519l$boF{qsbkR4`{G zNJZse69Z0rTb05|J%=0In=Lg85gj6I9o11#{gusu1GJsvyErf+Yo5YVg0sByYU}8?NZtpgY*i3mtD!zDnIvp{G_SFXo8#PC#DYmW!Rqj72w9Bjj zGM`CIt_3A}mV`&Im5yD<8{eDuIUo61yEu!&^t**VC_lB;!$Wv1 z94Q<82`Si&c6z~KCWB1UN=|THj2V3(wgiCqmApvr=2WFVvE)>W&|@;A`@ji|xB}F9 zA+jYbI?E?nbw~w@Ye74c9||#klustsOB6e=$J*o}r;FsCnCO>w_BoZfmP7L#^+~lq z8Y_)`i3!RGtzM_*B^?KuI>J%rqd3z}$`Be(cVusLh4#4K2!JFeL@?2+I$UM}J+ z0)?pixXPu2+QmQ98+$OZe+F}(opHH%tuT+gkhO&X>VYygxKqJ2YJ zqLwADyv3N)9ZJW0pW9kWVmKW(Wr#MA+A0NArd68z~es;e;C5v0;8rB~jbv8Q~_*W7rT~j_xan1K~97 zGVHYDV?x4A?URkVSq^Zh#Dv5|DPOqX{Z+xWebnJ_)-b=s)N}%(I2x-4GpCs7|E;$jhjE^Vzp-3~nwv!;wm^hF3 zL81qQo9subD$l;;dIB`v-3Qm*|F_TiUq9&nzwxoDiHX+vzY`PU{ohmF{eP#u{}1dU4od zwP`NX;lQ+z^DX9Br@5E`QM&4pjszP?hiZbZ&MDtjUY^`VW@}7aT3b%ZrWR$y$zJ67 z#xuqau`5w@K2D>nLtA1FxkHEkoT?8i3HhC|TwWa|7TxBwe;cSs1p!2qxm$e6+T23s zui`tv_r(5Ue*3m?OOaY1M4{!Pb8u64*7Z#@AJs#}wnaa8oPQ%Y8m%?9w$so;C4+La zMRtS^e>+mdR~r~Nd1_j|{lv7mtzxl057G26xb~CQVzgK3`cTKO#Y0ACsPKT*P1;U| z9y7i65(yp~>MD-L&FK;u`z*BGBDa~n0kT7^P*HjGqM>sXA(3m)@-pteP@dy1G#v%j z-W_P7=Uaw>W=M`hadLQStk9NY(=emnaJfVjH?wC0lry@YA-69Z$Q`&rMhJuG%$joe z)p})^79xk~Wm^kqI2GmTNWHv&oL;NC;DL2eEoBaP)YKV!*|?@g$_9_WjI>p*yS1I+ z8Is#M{>yZ0#RTy)OW4Sz_mSDNL56PS%=iZ_0+3)k^HftfUnEQ5slUS>vV=`?ToaFG zlt(tU<&~!nitKN4JbIktT;xDeg}l+UpzXQfRG+1^(bhbOr|J0;$v^mxwoQi?n+}*G zgQo$8SE=4@rVJmkha?r4(yd06aMNMw8tBYu`T%aCKvYHL;cDB$I${|*qfR5lU5V|( z8%OeLM||}7!S(@&C?yB&ONd3Y8f8Cpw^h$OsxKoW?|L?$T1W%jX8b^y-~c`-tQ>Av zeg_?-3A7Q~^)L7P2jD+z)w~wcLz-$iCvNNhv9zuCq?H_K5uJ=0b0|A8R3bul&Cx?| z^%le49^2D-)4);Gn@FK&0cjJvcVcI>6_&^o14L{NW8=J{-Kk#D#ELgCaL3`#U^Cf=-vPS1Cxc?Bh3eJOZy=014bnxuLLg~@U(rEe? zpRd(8fyAg6^cZaG5V;&1gN{aI)t{Otsd$>SHua)p@ZZ-@^ zrQQ9|l)CMiT;R!bTOEP6?y5yX!i;NgcrTGAB$280zA6;9koFv;u7;p9VZakPuRJW4 zd3pK%%9DAmz~TC_w){7X2D=rmRzN0$t7gtJH+kL2Ro-cmt8rk)Ak40fS)wE!4_7A@s? z`Eg;Ph!=%#3($di?YkV}U}3pbMjsaRVt$D)&Oe%eP$&bi>;Z@XY%i>8g-6&*=zDqf zUU{Xsvbs{>50;k~3BA%iUH%DxTUss?j#kTs9MrNhPnA;y>Pq=1cztiRTqIN#ODhFU zTYbDzTrQ3Bhs#e27`C=B4@Ged5%0?->@p6Q0ppn90>wi zCN7JJIYpB>8be;Z&*vAvDPrSgb{O|^QPK#ZZsDOsd|ocolN#XJc5C^cQZO8-VN`bX z>bAa#tJ;(*!b8YWJJQl9MucOSA_wip+=x&xWGX6Swlg;}PxC5&4AY2fH$o396zMZJ zBBtJObWcvcD+nIrwiS9)(YBSZRvUG%7Jgw7w8VUs-`U!^vnYJ=Pg-p=OtxLTHpTeeuKxd9GX92%K}}|(x>u=LjcqsYz4A`X{=?hI z_)ksVo#@(sr}60u<*xicnExMt_271~bbt8=@&An79lsml|M97@F8`m(=aK2@sQ2Ce zMri4?9B3e2qTNR=N=W-p1(Br7&3avT_GbB^HQw|?yKdgb-6OO7NE&;3o{WN79<~@{ z@@?_903Ks2q?d$-Jj+2pPA|6f^>=7}2Yo&LSnAnW@m~7(mjBDks~T9*?>6lpn*XQA zCL;VlF+SZr|Kn6X%G;lgKlY_Vay8xlmhhOQP&N@u5XtB#hO)|KkeH-UHWi)}mln*} zK98jjAoBEKmEhQn4cog7cJ^(wvtu)O_D-c{?wYkDGSsQ_f3Qa<&;Kp`pWX(K{C{_1 zyvzSx{lEM9?co3839JM8e|$1B|4)uhj&=F}bjQCd3A+5>+W%4A*gUa14&?ut2>*{y zPmXo>f1Jwa8OZnw%J{Xkr*Br)n^p~9N=Y{uQ32OubmQ(eDvr5pp&Nh_yYk}qlmCzB v>pKzu-}v3T)7|$!bp3x_|6ljh{d7OwPxsUPbU)qCiGThd+NrB20LTCU(l|bs diff --git a/scripts/dump-test-db.sh b/scripts/dump-test-db.sh deleted file mode 100755 index 6bddb42f7..000000000 --- a/scripts/dump-test-db.sh +++ /dev/null @@ -1,2 +0,0 @@ -./manage.py dumpdata --settings=settings_test --indent=4 > main/fixtures/sample_course.json -./manage.py dumpdata --settings=settings_test --exclude contenttypes --exclude tagging --indent=4 > main/fixtures/unittest_sample_course.json \ No newline at end of file diff --git a/scripts/pre-commit.py b/scripts/pre-commit.py deleted file mode 100755 index 19aa54436..000000000 --- a/scripts/pre-commit.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python -# link me into your git hooks like so: -# ln -s `pwd`/scripts/pre-commit.py `pwd`/.git/hooks/pre-commit - -import os -import re -import subprocess -import sys - -modified = re.compile('^(?:M|A)(\s+)(?P.*)') - -CHECKS = [ - { - 'output': 'Checking for pdbs...', - 'command': 'grep -n "import pdb" %s', - 'ignore_files': ['.*pre-commit', '.*/ve/.*', - '.*djangosherd/media/js/sherdjs'], - 'print_filename': True, - }, - { - 'output': 'Checking for print statements...', - 'command': 'grep -n print %s', - 'match_files': ['.*\.py$'], - 'ignore_files': ['.*migrations.*', '.*management/commands.*', - '.*manage.py', '.*/scripts/.*', '.*/ve/.*', - '.*settings_test.py', - '.*scripts/pre-commit\.py$', - '.*scripts/minify-js\.py$', - '.*scripts/minify-mustache\.py$', - '.*virtualenv\.py$'], - 'print_filename': True, - }, - { - 'output': 'Checking for console.log()...', - 'command': 'grep -n console.log %s', - 'match_files': ['.*\.js$'], - 'ignore_files': ['.*backbone-min\.js$', '.*jquery-.*\.js$', - '.*underscore.*\.js$', '.*/media/CACHE/.*', - '.*/ve/.*'], - 'print_filename': True, - }, - { - 'output': 'Checking for debugger...', - 'command': 'grep -n debugger %s', - 'match_files': ['.*\.js$', '.*/media/CACHE/.*'], - 'print_filename': True, - } -] - - -def matches_file(file_name, match_files): - return any(re.compile(match_file).match(file_name) - for match_file in match_files) - - -def check_files(files, check): - result = 0 - for file_name in files: - if (not 'match_files' in check or - matches_file(file_name, check['match_files'])): - if (not 'ignore_files' in check or - not matches_file(file_name, check['ignore_files'])): - process = subprocess.Popen(check['command'] % file_name, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, shell=True) - out, err = process.communicate() - if out or err: - if check['print_filename']: - prefix = '\t%s:' % file_name - else: - prefix = '\t' - output_lines = ['%s%s' % (prefix, line) - for line in out.splitlines()] - print '\n'.join(output_lines) - if err: - print err - result = 1 - return result - - -def main(all_files): - # Stash any changes to the working tree that are not going to be committed - subprocess.call(['git', 'stash', '--keep-index'], stdout=subprocess.PIPE) - - files = [] - if all_files: - for root, dirs, file_names in os.walk('.'): - for file_name in file_names: - files.append(os.path.join(root, file_name)) - else: - p = subprocess.Popen(['git', 'status', '--porcelain'], - stdout=subprocess.PIPE) - out, err = p.communicate() - for line in out.splitlines(): - match = modified.match(line) - if match: - files.append(match.group('name')) - - result = 0 - print 'Running Django Code Validator...' - return_code = subprocess.call('./manage.py validate', shell=True) - result = return_code or result - - for check in CHECKS: - print check['output'] - result = check_files(files, check) or result - - if result == 0: - print 'Running Flake8...' - return_code = subprocess.call( - 'flake8 --exclude=ve,media --ignore=F403 .', - shell=True) - result = return_code or result - - if result == 0: - print 'Running Unit Tests...' - return_code = subprocess.call( - './manage.py jenkins', - shell=True) - result = return_code or result - - # Unstash changes to the working tree that we had stashed - subprocess.call(['git', 'stash', 'pop', '--quiet', '--index'], - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - sys.exit(result) - - -if __name__ == '__main__': - all_files = False - if len(sys.argv) > 1 and sys.argv[1] == '--all-files': - all_files = True - main(all_files) diff --git a/scripts/reset-testdb-from-fixture.sh b/scripts/reset-testdb-from-fixture.sh deleted file mode 100755 index b7b1d67c2..000000000 --- a/scripts/reset-testdb-from-fixture.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -rm lettuce.db -./manage.py syncdb --settings=mediathread.settings_test --noinput -./manage.py migrate --settings=mediathread.settings_test --noinput -echo 'delete from django_content_type;' | sqlite3 lettuce.db -./manage.py loaddata mediathread/main/fixtures/sample_course.json --settings=mediathread.settings_test \ No newline at end of file From 05ed5e4450ceeccf8e0c8f87c4e1c0350a31af6e Mon Sep 17 00:00:00 2001 From: c0cky Date: Sun, 21 Dec 2014 17:35:26 -0500 Subject: [PATCH 23/58] fixed for spring2015 --- mediathread/assetmgr/views.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mediathread/assetmgr/views.py b/mediathread/assetmgr/views.py index 144f1f848..fae093b00 100644 --- a/mediathread/assetmgr/views.py +++ b/mediathread/assetmgr/views.py @@ -458,9 +458,9 @@ def test_dump(request): ar.Meta.excludes = ['added', 'modified', 'course', 'active'] lst = [] - notes = SherdNote.objects.get_related_notes(assets, user_id or None, [request.user.id]) + notes = SherdNote.objects.get_related_notes(assets, user_id or None, [request.user.id], True) - api_response = ar.render_list(request, [request.user.id], assets, notes) + api_response = ar.render_list(request, [request.user.id], [request.user.id], assets, notes) if len(api_response) == 0: return HttpResponse("There are no videos in your collection") for i in range(0, len(api_response)): From 9c20fdf58662359d5bf80ba0dd904ed6e91a9906 Mon Sep 17 00:00:00 2001 From: c0cky Date: Mon, 22 Dec 2014 20:35:40 -0500 Subject: [PATCH 24/58] fixed merge errors --- mediathread/djangosherd/models.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mediathread/djangosherd/models.py b/mediathread/djangosherd/models.py index 6371fa92a..bdda012e1 100755 --- a/mediathread/djangosherd/models.py +++ b/mediathread/djangosherd/models.py @@ -129,12 +129,10 @@ def get_related_notes(self, assets, record_owner, visible_authors, self = self.exclude(~Q(author=record_owner)) if len(visible_authors) > 0: -<<<<<<< HEAD # return global annotations & # regular selections authored by certain people self = self.filter(Q(author__id__in=visible_authors) | Q(range1__isnull=True)) -======= if not all_items_are_visible: # only return notes that are authored by certain people self = self.filter(author__id__in=visible_authors) @@ -143,7 +141,6 @@ def get_related_notes(self, assets, record_owner, visible_authors, # regular selections authored by certain people self = self.filter(Q(author__id__in=visible_authors) | Q(range1__isnull=True)) ->>>>>>> added spring 2015 files # filter by tag string, date, vocabulary self = self.filter_by_tags(tag_string) From 460d42faf11fe78e337cdd9601ef7f6cccb9ba5a Mon Sep 17 00:00:00 2001 From: c0cky Date: Mon, 22 Dec 2014 20:45:56 -0500 Subject: [PATCH 25/58] added URL sanitize code --- media/js/app/taxonomy/taxonomy.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 27b6a094a..0eafaa801 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -492,9 +492,27 @@ return item.display_name == thing; }); }; - function getTheOnomy(onomyURL, self) + function getTheOnomy(dirtyURL, self) { - + var test; + var onomyURL; + var onomy_index; + + //this is to sanitize the url entered by the user. + //checks to see if it contains onomy and json + test = dirtyURL.search(('onomy' | 'json')); + if (test != -1) + { + //grab the numbers from the url entered + onomy_index = /\d+/g.exec(dirtyURL); + } + else + { + //display error message + } + //all of the onomyURL's should fit this so i just strip the numbers from user + //input and add it to the format + onomyURL = 'http://onomy.org/published/' + onomy_index + '/json'); var vocabulary_id; vocabulary_id = self.selected.get('id'); jQuery.get(onomyURL, From f16eac1f03851fafc5a439beea3e9b52c6d481cc Mon Sep 17 00:00:00 2001 From: c0cky Date: Mon, 22 Dec 2014 21:00:33 -0500 Subject: [PATCH 26/58] fixed merge conflicts --- mediathread/templates/base.html | 3 --- 1 file changed, 3 deletions(-) diff --git a/mediathread/templates/base.html b/mediathread/templates/base.html index 45ca5291a..692647684 100644 --- a/mediathread/templates/base.html +++ b/mediathread/templates/base.html @@ -11,9 +11,7 @@ -<<<<<<< HEAD -======= {% if settings.FIREFOX %} @@ -21,7 +19,6 @@ {% endif %} ->>>>>>> added spring 2015 files From 9253de5fe1f5dac55bd46af152f4e9dff124c824 Mon Sep 17 00:00:00 2001 From: c0cky Date: Mon, 22 Dec 2014 21:01:11 -0500 Subject: [PATCH 27/58] fixed merge conflicts --- .../templates/djangosherd/annotator_resources_css.html | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mediathread/templates/djangosherd/annotator_resources_css.html b/mediathread/templates/djangosherd/annotator_resources_css.html index 817488319..bb51348e2 100644 --- a/mediathread/templates/djangosherd/annotator_resources_css.html +++ b/mediathread/templates/djangosherd/annotator_resources_css.html @@ -22,10 +22,7 @@ width:25px; } -<<<<<<< HEAD -======= ->>>>>>> added spring 2015 files - \ No newline at end of file + From f222c50a0e62912f850d0ead098486834fb3ca4e Mon Sep 17 00:00:00 2001 From: c0cky Date: Mon, 22 Dec 2014 21:01:35 -0500 Subject: [PATCH 28/58] fixed merge conflicts --- mediathread/djangosherd/tests/test_model.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mediathread/djangosherd/tests/test_model.py b/mediathread/djangosherd/tests/test_model.py index 353ff619a..3053a702c 100644 --- a/mediathread/djangosherd/tests/test_model.py +++ b/mediathread/djangosherd/tests/test_model.py @@ -337,7 +337,6 @@ def test_filter_by_visible_authors(self): notes = SherdNote.objects.get_related_notes(qs, None, -<<<<<<< HEAD visible_authors) self.assertEquals(notes.count(), 5) @@ -346,7 +345,6 @@ def test_filter_by_visible_authors(self): self.assertEquals(notes[2], self.ga2) self.assertEquals(notes[3], self.ga3) self.assertEquals(notes[4], self.note3) -======= visible_authors, True) self.assertEquals(notes.count(), 5) @@ -374,7 +372,6 @@ def test_filter_by_all_items_are_visible(self): self.assertEquals(notes[1], self.student_one_note) self.assertEquals(notes[2], self.instructor_one_ga) self.assertEquals(notes[3], self.instructor_one_note) ->>>>>>> added spring 2015 files def test_filter_by_tags(self): notes = SherdNote.objects.filter_by_tags('student_one_selection') From f454e8e4c090bd0b1a0e7915f3bf6c0ab9ff7ed0 Mon Sep 17 00:00:00 2001 From: c0cky Date: Mon, 22 Dec 2014 21:02:00 -0500 Subject: [PATCH 29/58] fixed merge conflicts --- requirements/js.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/requirements/js.txt b/requirements/js.txt index e037ba199..007b6676f 100644 --- a/requirements/js.txt +++ b/requirements/js.txt @@ -1,5 +1,2 @@ -<<<<<<< HEAD requirements/src/sherdjs-0.4.1.tar.gz -======= requirements/src/sherdjs-0.4.5.tar.gz ->>>>>>> added spring 2015 files From 412620f04cc70fa8ca5e79078105a7e2e0278f7d Mon Sep 17 00:00:00 2001 From: c0cky Date: Mon, 22 Dec 2014 21:12:15 -0500 Subject: [PATCH 30/58] fix git << head errors --- mediathread/djangosherd/tests/test_model.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/mediathread/djangosherd/tests/test_model.py b/mediathread/djangosherd/tests/test_model.py index 3053a702c..6b20eba62 100644 --- a/mediathread/djangosherd/tests/test_model.py +++ b/mediathread/djangosherd/tests/test_model.py @@ -337,17 +337,10 @@ def test_filter_by_visible_authors(self): notes = SherdNote.objects.get_related_notes(qs, None, - visible_authors) - self.assertEquals(notes.count(), 5) - - self.assertEquals(notes[0], self.ga1) - self.assertEquals(notes[1], self.note1) - self.assertEquals(notes[2], self.ga2) - self.assertEquals(notes[3], self.ga3) - self.assertEquals(notes[4], self.note3) visible_authors, - True) - self.assertEquals(notes.count(), 5) + True) + + self.assertEquals(notes.count(), 5) self.assertEquals(notes[0], self.student_one_ga) self.assertEquals(notes[1], self.student_one_note) From ad2ca0b37ce3d5df1ea2f706696598cf58f90184 Mon Sep 17 00:00:00 2001 From: c0cky Date: Mon, 22 Dec 2014 21:19:41 -0500 Subject: [PATCH 31/58] fixed syntax error --- media/js/app/taxonomy/taxonomy.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 0eafaa801..a8fb5bfd9 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -512,7 +512,7 @@ } //all of the onomyURL's should fit this so i just strip the numbers from user //input and add it to the format - onomyURL = 'http://onomy.org/published/' + onomy_index + '/json'); + onomyURL = 'http://onomy.org/published/' + onomy_index + '/json'; var vocabulary_id; vocabulary_id = self.selected.get('id'); jQuery.get(onomyURL, From e7cc98464c0423f21da617e3ce9597d1d840a923 Mon Sep 17 00:00:00 2001 From: c0cky Date: Mon, 22 Dec 2014 21:24:28 -0500 Subject: [PATCH 32/58] added onomy error message --- media/js/app/taxonomy/taxonomy.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index a8fb5bfd9..0752aea13 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -509,6 +509,8 @@ else { //display error message + showMessage("Enter a valid Onomy URL", undefined, "Error"); + return; } //all of the onomyURL's should fit this so i just strip the numbers from user //input and add it to the format From f99b6a4d0ef4ad77d68d4e074104b89b80fd3c99 Mon Sep 17 00:00:00 2001 From: c0cky Date: Fri, 26 Dec 2014 17:32:14 -0500 Subject: [PATCH 33/58] changed test name to mep_dump --- mediathread/assetmgr/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediathread/assetmgr/views.py b/mediathread/assetmgr/views.py index fae093b00..5ab8787c5 100644 --- a/mediathread/assetmgr/views.py +++ b/mediathread/assetmgr/views.py @@ -450,7 +450,7 @@ def final_cut_pro_xml(request, asset_id): status=503) #@waffle_switch('ONOMY_SWITCH') -def test_dump(request): +def mep_dump(request): user = request.user user_id = user.id assets = Asset.objects.filter(author_id=user_id) From 55d010381324a5aac0562a2cd83bae201928eb81 Mon Sep 17 00:00:00 2001 From: c0cky Date: Fri, 26 Dec 2014 17:32:28 -0500 Subject: [PATCH 34/58] changed test_dump to mep_dump --- mediathread/assetmgr/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediathread/assetmgr/urls.py b/mediathread/assetmgr/urls.py index 829df00a8..7ec7259fa 100644 --- a/mediathread/assetmgr/urls.py +++ b/mediathread/assetmgr/urls.py @@ -43,7 +43,7 @@ 'final_cut_pro_xml', name="final_cut_pro_xml"), - url(r'test/', 'test_dump', name='test_dump'), + url(r'MEPdump/', 'mep_dump', name='mep_dump'), # Asset workspace variations url(r'^$', AssetWorkspaceView.as_view(), {}, 'asset-collection-view'), ) From a932fe5627d334fef5dd03d76979c0036e887c55 Mon Sep 17 00:00:00 2001 From: c0cky Date: Sun, 28 Dec 2014 19:47:40 -0500 Subject: [PATCH 35/58] added refreshIcon for Onomy --- media/img/icons/refreshIcon.gif | Bin 0 -> 630 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 media/img/icons/refreshIcon.gif diff --git a/media/img/icons/refreshIcon.gif b/media/img/icons/refreshIcon.gif new file mode 100644 index 0000000000000000000000000000000000000000..0c415e9e209031dbcdaddf4c695638bc98af8f28 GIT binary patch literal 630 zcmZ?wbhEHb6k!lycoxJE85tQLA3u5Wf^YimJZrm6i z9zJc_w2+XH)YR0txVXK0_r}D;oIij5`t|F_j~`#Xdi9(+bKKnAwr}5l?AS4Xe}5kz zpS--hnwpxfuC56aCd{2X_sp3yM~@!O%*+f747_>s=Iq(Cw`|$6Z{NP$+}zUA(uor% zwzjseU%$Syv-8fKJLTo&ZEbA_4jibftE;ZAK6L2N=FOW63JNxD+O%ueuClVS88c=S z7Z+DmRh>F@sDGUXI%HfvU97SL^FW#M=0_XrI1Z($4MouMDdAkxey&tNsfMux?%#U+qa zxL-;pP^(2&L&vc4n{`1FIEDHifZhVOb=d7Y++gEB(7p0@#%A5pL-(%gEauTW5rhh literal 0 HcmV?d00001 From 225c9ef1dd64f060ec68c0785786fb339d71560b Mon Sep 17 00:00:00 2001 From: c0cky Date: Sun, 28 Dec 2014 20:15:21 -0500 Subject: [PATCH 36/58] added onomy UI fixes --- media/css/mediathread.css | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/media/css/mediathread.css b/media/css/mediathread.css index 45af97840..4994688b5 100644 --- a/media/css/mediathread.css +++ b/media/css/mediathread.css @@ -4518,7 +4518,7 @@ div#taxonomy input[type="text"] { display: inline-block; height: 20px; padding: 4px 6px; - margin: 5px 0 5px 5px; + margin: 5px 0 15px 5px; font-size: 14px; line-height: 20px; color: #555555; @@ -4687,6 +4687,26 @@ div#taxonomy div.terms a.create-term-submit { padding: 4px; } +div#taxonomy div.onomy a.refresh-button-submit { + margin: 0 0 0 5px; + padding: 6.5px; + padding-bottom: 4px; + padding-top: 8.5px; +} + +div#taxonomy div.onomy a.onomy-terms-submit img { + padding: 0; +} + +div#taxonomy div.onomy a.refresh-button-submit img { + padding: 0; +} + +div#taxonomy div.onomy a.onomy-terms-submit { + margin: 0 0 0 5px; + padding: 4px; +} + div#taxonomy div.terms a.create-term-submit img { padding: 0; } From 74aec42d9594b40aee2b7ee2f5738da83ba3683e Mon Sep 17 00:00:00 2001 From: c0cky Date: Sun, 28 Dec 2014 20:15:51 -0500 Subject: [PATCH 37/58] added fixes for onomy UI --- mediathread/templates/taxonomy/taxonomy.html | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/mediathread/templates/taxonomy/taxonomy.html b/mediathread/templates/taxonomy/taxonomy.html index 18e22281d..b2c2b4450 100644 --- a/mediathread/templates/taxonomy/taxonomy.html +++ b/mediathread/templates/taxonomy/taxonomy.html @@ -64,18 +64,16 @@ <% for (var i=0; i < vocabularies.length; i++) { %>

    - - -

    +

    - +

    <%=vocabularies[i].display_name%> Concept

    Terms

    From 4c4f9416aa3cb0e26b947ccedb07ee50b7fbb539 Mon Sep 17 00:00:00 2001 From: c0cky Date: Sun, 28 Dec 2014 20:49:22 -0500 Subject: [PATCH 38/58] fixed error with onomy input blur --- media/js/app/taxonomy/taxonomy.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 0752aea13..355989a51 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -111,7 +111,9 @@ 'focus input[name="display_name"]': 'focusVocabularyName', 'blur input[name="display_name"]': 'blurVocabularyName', 'focus input[name="term_name"]': 'focusTermName', + 'focus input[name="onomy_url"]': 'focusTermName', 'blur input[name="term_name"]': 'blurTermName', + 'blur input[name="onomy_url"]': 'blurTermName', 'click a.create-term-submit': 'createTerm', 'keypress input[name="term_name"]': 'keypressTermName', 'click a.edit-term-submit': 'updateTerm', @@ -439,10 +441,13 @@ } }, blurTermName: function (evt) { - if (jQuery(evt.currentTarget).attr("value") === '') { + if (jQuery(evt.currentTarget).attr("value") === '' && jQuery(evt.currentTarget).attr("name") !== 'onomy_url') { jQuery(evt.currentTarget).addClass("default"); jQuery(evt.currentTarget).attr("value", "Type new term name here"); - } + }else if(jQuery(evt.currentTarget).attr("value") === '' && jQuery(evt.currentTarget).attr("name") === "onomy_url") { + jQuery(evt.currentTarget).addClass("default"); + jQuery(evt.currentTarget).attr("value", "Enter an Onomy URL here"); + } } , createOnomyVocabulary: function (evt) { From 8ab3228ff7de92f6a49324ee2b21fa9d5abfa75a Mon Sep 17 00:00:00 2001 From: c0cky Date: Sun, 28 Dec 2014 20:49:52 -0500 Subject: [PATCH 39/58] fixed error with onomy input blur --- mediathread/templates/taxonomy/taxonomy.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mediathread/templates/taxonomy/taxonomy.html b/mediathread/templates/taxonomy/taxonomy.html index b2c2b4450..41c98712c 100644 --- a/mediathread/templates/taxonomy/taxonomy.html +++ b/mediathread/templates/taxonomy/taxonomy.html @@ -65,7 +65,7 @@ <% for (var i=0; i < vocabularies.length; i++) { %>
    - + submit From 798b71c6d6d39c9b919804b4b73b6b0c059156b1 Mon Sep 17 00:00:00 2001 From: c0cky Date: Wed, 7 Jan 2015 14:29:37 -0500 Subject: [PATCH 40/58] added onomy migration --- ...ay_name__add_field_vocabulary_onomy_url.py | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py diff --git a/mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py b/mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py new file mode 100644 index 000000000..6d70828ba --- /dev/null +++ b/mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py @@ -0,0 +1,65 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Term.display_name' + db.alter_column(u'taxonomy_term', 'display_name', self.gf('django.db.models.fields.CharField')(max_length=20)) + # Adding field 'Vocabulary.onomy_url' + db.add_column(u'taxonomy_vocabulary', 'onomy_url', + self.gf('django.db.models.fields.TextField')(null=True, blank=True), + keep_default=False) + + + def backwards(self, orm): + + # Changing field 'Term.display_name' + db.alter_column(u'taxonomy_term', 'display_name', self.gf('django.db.models.fields.CharField')(max_length=50)) + # Deleting field 'Vocabulary.onomy_url' + db.delete_column(u'taxonomy_vocabulary', 'onomy_url') + + + models = { + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'taxonomy.term': { + 'Meta': {'ordering': "['display_name', 'id']", 'unique_together': "(('name', 'vocabulary'),)", 'object_name': 'Term'}, + 'description': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), + 'display_name': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'ordinality': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}), + 'vocabulary': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['taxonomy.Vocabulary']"}) + }, + u'taxonomy.termrelationship': { + 'Meta': {'ordering': "['term__display_name', 'id']", 'unique_together': "(('term', 'content_type', 'object_id'),)", 'object_name': 'TermRelationship'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'term': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['taxonomy.Term']"}) + }, + u'taxonomy.vocabulary': { + 'Meta': {'ordering': "['display_name', 'id']", 'object_name': 'Vocabulary'}, + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'display_name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), + 'object_id': ('django.db.models.fields.PositiveIntegerField', [], {}), + 'onomy_url': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'single_select': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + } + } + + complete_apps = ['taxonomy'] \ No newline at end of file From 9c7cea9d192ddf22ae09efb626bc3613d6ccaf8c Mon Sep 17 00:00:00 2001 From: c0cky Date: Thu, 8 Jan 2015 12:05:56 -0500 Subject: [PATCH 41/58] changed the term length back to 50 --- ...eld_term_display_name__add_field_vocabulary_onomy_url.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py b/mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py index 6d70828ba..c3ffbd88b 100644 --- a/mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py +++ b/mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py @@ -10,7 +10,7 @@ class Migration(SchemaMigration): def forwards(self, orm): # Changing field 'Term.display_name' - db.alter_column(u'taxonomy_term', 'display_name', self.gf('django.db.models.fields.CharField')(max_length=20)) + db.alter_column(u'taxonomy_term', 'display_name', self.gf('django.db.models.fields.CharField')(max_length=50)) # Adding field 'Vocabulary.onomy_url' db.add_column(u'taxonomy_vocabulary', 'onomy_url', self.gf('django.db.models.fields.TextField')(null=True, blank=True), @@ -36,7 +36,7 @@ def backwards(self, orm): u'taxonomy.term': { 'Meta': {'ordering': "['display_name', 'id']", 'unique_together': "(('name', 'vocabulary'),)", 'object_name': 'Term'}, 'description': ('django.db.models.fields.CharField', [], {'max_length': '256', 'null': 'True', 'blank': 'True'}), - 'display_name': ('django.db.models.fields.CharField', [], {'max_length': '20'}), + 'display_name': ('django.db.models.fields.CharField', [], {'max_length': '50'}), u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50'}), 'ordinality': ('django.db.models.fields.IntegerField', [], {'default': '0', 'null': 'True', 'blank': 'True'}), @@ -62,4 +62,4 @@ def backwards(self, orm): } } - complete_apps = ['taxonomy'] \ No newline at end of file + complete_apps = ['taxonomy'] From c73c86a0f65de0aba774dfbe7456296276d48343 Mon Sep 17 00:00:00 2001 From: c0cky Date: Fri, 9 Jan 2015 13:20:33 -0500 Subject: [PATCH 42/58] removed commented code @ ln 46 --- mediathread/taxonomy/models.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/mediathread/taxonomy/models.py b/mediathread/taxonomy/models.py index 1b97e536a..81bb9f339 100644 --- a/mediathread/taxonomy/models.py +++ b/mediathread/taxonomy/models.py @@ -43,24 +43,6 @@ class VocabularyForm(forms.ModelForm): class Meta: model = Vocabulary -#class Onomy(models.Model): -# url = models.CharField(max_length=100) -# vocabulary = models.ForeignKey(Vocabulary) - -# def save(self, force_insert=False, force_update=False): -# self.name = slugify(self.display_name) -# super(Vocabulary, self).save(force_insert, force_update) - -# def to_json(self): -# return { -# 'onomy_url':self.onomy_url, -# 'vocabulary': self.vocabulary -# } - -#class OnomyForm(forms.ModelForm): -# class Meta: -# model = Onomy - class Term(models.Model): name = models.SlugField() From 0bd9a312fa4ede0c01ffc93a9230ade4b11279d9 Mon Sep 17 00:00:00 2001 From: c0cky Date: Fri, 9 Jan 2015 13:21:45 -0500 Subject: [PATCH 43/58] revert the DATABASES dict to its previous state --- mediathread/settings_shared.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mediathread/settings_shared.py b/mediathread/settings_shared.py index 51b1ca4bc..7bf4744ab 100644 --- a/mediathread/settings_shared.py +++ b/mediathread/settings_shared.py @@ -23,11 +23,11 @@ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': 'camron', + 'NAME': '', 'HOST': '', - 'PORT': '5432', - 'USER': 'camron', - 'PASSWORD': 'password', + 'PORT': '', + 'USER': '', + 'PASSWORD': '', } } From cd9bb64fb20ff1b8a0f00659d5d7db616a4b54d0 Mon Sep 17 00:00:00 2001 From: c0cky Date: Fri, 9 Jan 2015 14:20:47 -0500 Subject: [PATCH 44/58] added jshint compliancy --- media/js/app/taxonomy/taxonomy.js | 374 ++++++++++++++---------------- 1 file changed, 178 insertions(+), 196 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 355989a51..fd13876df 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -401,10 +401,7 @@ var self = this; var id = jQuery(evt.currentTarget).attr('href'); - console.log(jQuery(evt.currentTarget)); - console.log(id); var term = self.selected.get('term_set').getByDataId(id); - console.log(term); var msg = "Deleting the term " + term.get('display_name') + "" + " removes this term" + " from all associated course items."; @@ -445,231 +442,216 @@ jQuery(evt.currentTarget).addClass("default"); jQuery(evt.currentTarget).attr("value", "Type new term name here"); }else if(jQuery(evt.currentTarget).attr("value") === '' && jQuery(evt.currentTarget).attr("name") === "onomy_url") { - jQuery(evt.currentTarget).addClass("default"); - jQuery(evt.currentTarget).attr("value", "Enter an Onomy URL here"); - } + jQuery(evt.currentTarget).addClass("default"); + jQuery(evt.currentTarget).attr("value", "Enter an Onomy URL here"); + } } , createOnomyVocabulary: function (evt) { + var et; + var self; + var vocabulary_id; + var onomyURL; evt.preventDefault(); - var et = jQuery(evt.currentTarget).prev(); + et = jQuery(evt.currentTarget).prev(); - var self = this; - var vocabulary_id; + self = this; vocabulary_id = this.selected.get('id'); - //'http://www.corsproxy.com/' + - //this should be sanitized in the future. onomyURL = jQuery(et).attr("value").trim(); - console.log(onomyURL); getTheOnomy(onomyURL, self); }, - refreshOnomy: function(evt) - { + refreshOnomy: function (evt) { var self = this; - urlArray = _.map(self.collection.models, function(model){return model.attributes.onomy_url}); + var urlArray; + urlArray = _.map(self.collection.models, function (model) { + return model.attributes.onomy_url + }); var address; - for (var i = 0; i < urlArray.length; i++) - { + for (var i = 0; i < urlArray.length; i++) { address = urlArray[i].toString(); - if (!address == "") - { + if (!address == "") { getTheOnomy(address, self); } } -// console.log(self.collection); -// var urlcsv = self.selected.get('onomy_url'); -// url = urlcsv.split(','); -// url.push('http://testthis.com/allegedly/onomy'); -// //self.selected.get('onomy_url').add(urlcsv); -// console.log(self.selected.get('onomy_url')); } - - - - }); function findUtil(array, thing){ return jQuery.grep(array, function(item){ return item.display_name == thing; }); - }; - function getTheOnomy(dirtyURL, self) - { + } + function getTheOnomy(dirtyURL, self) { var test; var onomyURL; var onomy_index; - - //this is to sanitize the url entered by the user. - //checks to see if it contains onomy and json - test = dirtyURL.search(('onomy' | 'json')); - if (test != -1) - { - //grab the numbers from the url entered - onomy_index = /\d+/g.exec(dirtyURL); - } - else - { - //display error message - showMessage("Enter a valid Onomy URL", undefined, "Error"); - return; - } - //all of the onomyURL's should fit this so i just strip the numbers from user - //input and add it to the format - onomyURL = 'http://onomy.org/published/' + onomy_index + '/json'; + + //this is to sanitize the url entered by the user. + //checks to see if it contains onomy and json + test = dirtyURL.search(('onomy' | 'json')); + if (test != -1) { + //grab the numbers from the url entered + onomy_index = /\d+/g.exec(dirtyURL); + } + else { + //display error message + showMessage("Enter a valid Onomy URL", undefined, "Error"); + return; + } + //all of the onomyURL's should fit this so i just strip the numbers from user + //input and add it to the format + onomyURL = 'http://onomy.org/published/' + onomy_index + '/json'; var vocabulary_id; vocabulary_id = self.selected.get('id'); jQuery.get(onomyURL, - function (data) { - var x; - x = JSON.parse(data); - - var MAX; - var parents = []; - MAX = x.terms.length; //change to x.terms.length after done testing - for (var i = 0; i < MAX; i++) { - var pL; - var display; - pL = x.terms[i]['rdfs:parentLabel'].trim(); - display = x.terms[i]['rdfs:label'].trim(); - console.log(pL); - console.log(display); - //demorgans law - if (!(pL === undefined || pL.length < 1)) { - var search = undefined; - parents.forEach(function(a){if(a.display_name == pL){search = a;}}); - if(search === undefined) - { - //create the Vocabulary - temp = {'display_name': pL, 'term_set':[], 'self':undefined}; - parents.push(temp); - parents[parents.indexOf(temp)].term_set.push({'display_name': display}); - } - else - { - //add the term to the Vocabulary in parents - v = search; - parents[parents.indexOf(v)].term_set.push({'display_name': display}); - } - if(i == MAX -1) - { - var tempV; - for (var j = 0; j < parents.length; j++) - { - model_search = _.find(self.collection.models, function(model){return model.attributes.display_name == parents[j].display_name}); - if(model_search === undefined) - { - //if we cant find the vocab in the collection we create a new one. - - tempV = new Vocabulary({ - 'display_name': parents[j].display_name, - 'content_type_id': 14, - 'object_id': 1, - 'term_set': undefined, - 'onomy_url': onomyURL - }); - - tempV._save({},{ - success: function(it){ - self.selected = it; - console.log(parents); - parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self = self.selected; - self.collection.add(it); - console.log('it'); - console.log(it); - for (var z = 0; z < parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set.length; z ++) - { - tempT = new Term({ - 'display_name':parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set[z].display_name, - 'vocabulary_id': it.attributes['id'] - }); - tempT._save({},{ - success: function (itT) { - parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self.get('term_set').add(itT); - self.render(); - } - }); - } - }}); - } else { - //we do find the model. we just add the term to it. - self.selected = model_search; - var urlcsv; - var url; - urlcsv = self.selected.get('onomy_url'); - url = urlcsv.split(','); - if(!(_.contains(url, onomyURL))) - { - url.push(onomyURL); - model_search.save({'onomy_url': url.toString()}); - } - - for (var z = 0; z < parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set.length; z ++) - { - tempT = new Term({ - 'display_name':parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set[z].display_name, - 'vocabulary_id': model_search.attributes['id'] + function (data) { + var x; + x = JSON.parse(data); + + var MAX; + var parents = []; + MAX = x.terms.length; //change to x.terms.length after done testing + for (var i = 0; i < MAX; i++) { + var pL; + var display; + pL = x.terms[i]['rdfs:parentLabel'].trim(); + display = x.terms[i]['rdfs:label'].trim(); + console.log(pL); + console.log(display); + //demorgans law + if (!(pL === undefined || pL.length < 1)) { + var search = undefined; + parents.forEach(function (a) { + if (a.display_name == pL) { + search = a; + } + }); + if (search === undefined) { + //create the Vocabulary + var temp; + temp = {'display_name': pL, 'term_set': [], 'self': undefined}; + parents.push(temp); + parents[parents.indexOf(temp)].term_set.push({'display_name': display}); + } + else { + //add the term to the Vocabulary in parents + v = search; + parents[parents.indexOf(v)].term_set.push({'display_name': display}); + } + if (i == MAX - 1) { + var tempV; + var model_search; + for (var j = 0; j < parents.length; j++) { + model_search = _.find(self.collection.models, function (model) { + return model.attributes.display_name == parents[j].display_name + }); + if (model_search === undefined) { + //if we cant find the vocab in the collection we create a new one. + + tempV = new Vocabulary({ + 'display_name': parents[j].display_name, + 'content_type_id': 14, + 'object_id': 1, + 'term_set': undefined, + 'onomy_url': onomyURL + }); + + tempV._save({}, { + success: function (it) { + var tempT; + self.selected = it; + parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self = self.selected; + self.collection.add(it); + for (var z = 0; z < parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set.length; z++) { + tempT = new Term({ + 'display_name': parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set[z].display_name, + 'vocabulary_id': it.attributes['id'] + }); + tempT._save({}, { + success: function (itT) { + parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self.get('term_set').add(itT); + self.render(); + } + }); + } + } + }); + } else { + //we do find the model. we just add the term to it. + self.selected = model_search; + var urlcsv; + var url; + var tempT; + urlcsv = self.selected.get('onomy_url'); + url = urlcsv.split(','); + if (!(_.contains(url, onomyURL))) { + url.push(onomyURL); + model_search.save({'onomy_url': url.toString()}); + } + + for (var z = 0; z < parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set.length; z++) { + tempT = new Term({ + 'display_name': parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set[z].display_name, + 'vocabulary_id': model_search.attributes['id'] + }); + tempT._save({}, { + success: function (itT) { + self.selected.get('term_set').add(itT); + self.render(); + } + + }); + } + + } + + + } + } + + } else { + + if (display === undefined || display.length < 1) { + continue; + } + var id; + var v; + var urlcsv; + var url; + console.log(self.selected); + id = self.selected.attributes.id; + v = self.collection.getByDataId(id); + urlcsv = self.selected.get('onomy_url'); + url = urlcsv.split(','); + + //if this vocabulary doesn't contain the url we punched in + if (!(_.contains(url, onomyURL))) { + //add it to our array we made and save it in the vocab + url.push(onomyURL); + v.save({'onomy_url': url.toString()}); + } + //we create our term + var t; + t = new Term({ + 'display_name': display, + 'vocabulary_id': vocabulary_id }); - tempT._save({},{ - success: function (itT) { - self.selected.get('term_set').add(itT); - self.render(); - } - - }); - } - - } - - - - } - } - - } else { - - if (display === undefined || display.length < 1) { - continue; - } - var id; - var v; - var urlcsv; - var url; - console.log(self.selected); - id = self.selected.attributes.id; - v = self.collection.getByDataId(id); - urlcsv = self.selected.get('onomy_url'); - url = urlcsv.split(','); - - //if this vocabulary doesn't contain the url we punched in - if(!(_.contains(url, onomyURL))) - { - //add it to our array we made and save it in the vocab - url.push(onomyURL); - v.save({'onomy_url': url.toString()}); - } - //we create our term - var t; - t = new Term({ - 'display_name': display, - 'vocabulary_id': vocabulary_id - }); - //then save it with our overriden queued save - t._save({}, { - wait: true, - success: function (it) { - //add it to our term - self.selected.get('term_set').add(it); - self.render(); - } - }); - } - } - }); - }; + //then save it with our overriden queued save + t._save({}, { + wait: true, + success: function (it) { + //add it to our term + self.selected.get('term_set').add(it); + self.render(); + } + }); + } + } + }); + } }(jQuery)); From d09aaed7fe54612e4de2e40eb5ab571c10b6a1ab Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Wed, 14 Jan 2015 14:48:21 -0500 Subject: [PATCH 45/58] Code cleanup * removed console.logs * adding flake8 ignore to migration file --- media/js/app/taxonomy/taxonomy.js | 5 ----- ...ield_term_display_name__add_field_vocabulary_onomy_url.py | 1 + 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index fd13876df..479c84c79 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -211,13 +211,11 @@ 'term_set': undefined, 'onomy_url': "" }); - console.log(v); v.save({}, { success: function () { self.selected = v; self.collection.add(v); - console.log('success'); }, error: function (model, response) { var responseText = jQuery.parseJSON(response.responseText); @@ -519,8 +517,6 @@ var display; pL = x.terms[i]['rdfs:parentLabel'].trim(); display = x.terms[i]['rdfs:label'].trim(); - console.log(pL); - console.log(display); //demorgans law if (!(pL === undefined || pL.length < 1)) { var search = undefined; @@ -621,7 +617,6 @@ var v; var urlcsv; var url; - console.log(self.selected); id = self.selected.attributes.id; v = self.collection.getByDataId(id); urlcsv = self.selected.get('onomy_url'); diff --git a/mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py b/mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py index c3ffbd88b..6491fc598 100644 --- a/mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py +++ b/mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py @@ -1,3 +1,4 @@ +# flake8: noqa # -*- coding: utf-8 -*- from south.utils import datetime_utils as datetime from south.db import db From d6629236fbb8e66600c570ba18696fcf49b8fc83 Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Wed, 14 Jan 2015 15:18:31 -0500 Subject: [PATCH 46/58] Code formatting * replacing tabs with spaces * jshint'ing function signatures * fixing indents --- media/js/app/taxonomy/taxonomy.js | 168 ++++++++++++++---------------- 1 file changed, 80 insertions(+), 88 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 479c84c79..6c97f1e72 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -1,8 +1,8 @@ -(function (jQuery) { +(function(jQuery) { Term = Backbone.Model.extend({ urlRoot: '/api/term/', - toTemplate: function () { + toTemplate: function() { return _(this.attributes).clone(); } }); @@ -10,17 +10,17 @@ var TermList = Backbone.Collection.extend({ urlRoot: '/api/term/', model: Term, - comparator: function (obj) { + comparator: function(obj) { return obj.get("display_name"); }, - toTemplate: function () { + toTemplate: function() { var a = []; - this.forEach(function (item) { + this.forEach(function(item) { a.push(item.toTemplate()); }); return a; }, - getByDataId: function (id) { + getByDataId: function(id) { var internalId = this.urlRoot + id + '/'; return this.get(internalId); } @@ -28,7 +28,7 @@ Vocabulary = Backbone.Model.extend({ urlRoot: '/api/vocabulary/', - parse: function (response) { + parse: function(response) { if (response) { response.term_set = new TermList(response.term_set); @@ -36,7 +36,7 @@ return response; }, - toTemplate: function () { + toTemplate: function() { var json = _(this.attributes).clone(); json.term_set = this.get('term_set').toTemplate(); return json; @@ -75,25 +75,23 @@ } }; - - var VocabularyList = Backbone.Collection.extend({ urlRoot: '/api/vocabulary/', model: Vocabulary, - comparator: function (obj) { + comparator: function(obj) { return obj.get("display_name"); }, - parse: function (response) { + parse: function(response) { return response.objects || response; }, - toTemplate: function () { + toTemplate: function() { var a = []; - this.forEach(function (item) { + this.forEach(function(item) { a.push(item.toTemplate()); }); return a; }, - getByDataId: function (id) { + getByDataId: function(id) { var internalId = this.urlRoot + id + '/'; return this.get(internalId); } @@ -111,9 +109,9 @@ 'focus input[name="display_name"]': 'focusVocabularyName', 'blur input[name="display_name"]': 'blurVocabularyName', 'focus input[name="term_name"]': 'focusTermName', - 'focus input[name="onomy_url"]': 'focusTermName', + 'focus input[name="onomy_url"]': 'focusTermName', 'blur input[name="term_name"]': 'blurTermName', - 'blur input[name="onomy_url"]': 'blurTermName', + 'blur input[name="onomy_url"]': 'blurTermName', 'click a.create-term-submit': 'createTerm', 'keypress input[name="term_name"]': 'keypressTermName', 'click a.edit-term-submit': 'updateTerm', @@ -123,7 +121,7 @@ 'click a.onomy-terms-submit': 'createOnomyVocabulary', 'click a.refresh-button-submit' : 'refreshOnomy' }, - initialize: function (options) { + initialize: function(options) { _.bindAll(this, "render", "createVocabulary", @@ -148,13 +146,13 @@ this.collection.fetch(); }, - activateTab: function (evt, ui) { + activateTab: function(evt, ui) { jQuery(ui.oldTab).find("div.vocabulary-edit, div.vocabulary-create").hide(); jQuery(ui.oldTab).find("div.vocabulary-display").show(); var vid = jQuery(ui.newTab).data("id"); this.selected = this.collection.getByDataId(vid); }, - render: function () { + render: function() { this.context.vocabularies = this.collection.toTemplate(); var markup = this.vocabularyTemplate(this.context); jQuery(this.el).html(markup); @@ -175,20 +173,20 @@ this.selected = this.collection.at(0); } }, - toggleCreateVocabulary: function (evt) { + toggleCreateVocabulary: function(evt) { evt.preventDefault(); var parent = jQuery(evt.currentTarget).parents("li")[0]; jQuery(parent).find("div.vocabulary-display, div.vocabulary-create").toggle(); return false; }, - toggleEditVocabulary: function (evt) { + toggleEditVocabulary: function(evt) { evt.preventDefault(); var parent = jQuery(evt.currentTarget).parents("li")[0]; jQuery(parent).find("div.vocabulary-display, div.vocabulary-edit").toggle(); jQuery(parent).find("input[name='display_name']").focus(); return false; }, - createVocabulary: function (evt) { + createVocabulary: function(evt) { evt.preventDefault(); var self = this; var parent = jQuery(evt.currentTarget).parent(); @@ -212,12 +210,11 @@ 'onomy_url': "" }); v.save({}, { - - success: function () { + success: function() { self.selected = v; self.collection.add(v); }, - error: function (model, response) { + error: function(model, response) { var responseText = jQuery.parseJSON(response.responseText); showMessage(responseText.vocabulary.error_message, undefined, "Error"); } @@ -225,7 +222,7 @@ return false; }, - updateVocabulary: function (evt) { + updateVocabulary: function(evt) { evt.preventDefault(); var self = this; var parent = jQuery(evt.currentTarget).parent(); @@ -246,10 +243,10 @@ var v = this.collection.getByDataId(id); if (v.get('display_name') !== 'display_name') { v.save({'display_name': display_name}, { - success: function () { + success: function() { self.render(); }, - error: function (model, response) { + error: function(model, response) { var responseText = jQuery.parseJSON(response.responseText); showMessage(responseText.vocabulary.error_message, undefined, "Error"); } @@ -257,7 +254,7 @@ } return false; }, - deleteVocabulary: function (evt) { + deleteVocabulary: function(evt) { var self = this; var id = jQuery(evt.currentTarget).attr('href'); @@ -275,14 +272,14 @@ resizable: false, modal: true, title: "Are you sure?", - close: function (event, ui) { + close: function(event, ui) { jQuery(dom).removeClass('about-to-delete'); }, buttons: { - "Cancel": function () { + "Cancel": function() { jQuery(this).dialog("close"); }, - "OK": function () { + "OK": function() { jQuery(this).dialog("close"); self.collection.remove(vocabulary); vocabulary.destroy(); @@ -291,19 +288,19 @@ }); return false; }, - focusVocabularyName: function (evt) { + focusVocabularyName: function(evt) { if (jQuery(evt.currentTarget).hasClass("default")) { jQuery(evt.currentTarget).removeClass("default"); jQuery(evt.currentTarget).attr("value", ""); } }, - blurVocabularyName: function (evt) { + blurVocabularyName: function(evt) { if (jQuery(evt.currentTarget).attr("value") === '') { jQuery(evt.currentTarget).addClass("default"); jQuery(evt.currentTarget).attr("value", "Type concept name here"); } }, - showEditTerm: function (evt) { + showEditTerm: function(evt) { evt.preventDefault(); var container = jQuery(evt.currentTarget).parents("div.terms"); jQuery(container).find("div.term-display").show(); @@ -315,21 +312,21 @@ jQuery(parent).find("input[name='display_name']").focus(); return false; }, - hideEditTerm: function (evt) { + hideEditTerm: function(evt) { evt.preventDefault(); var parent = jQuery(evt.currentTarget).parents("div.term")[0]; jQuery(parent).find("div.term-display").show(); jQuery(parent).find("div.term-edit").hide(); return false; }, - keypressTermName: function (evt) { + keypressTermName: function(evt) { var self = this; if (evt.which == 13) { evt.preventDefault(); jQuery(evt.currentTarget).next().click(); } }, - createTerm: function (evt) { + createTerm: function(evt) { evt.preventDefault(); var self = this; var et = jQuery(evt.currentTarget).prev(); @@ -338,31 +335,30 @@ showMessage("Please enter a term name", undefined, "Error"); return; } - //if you want to create a term from user input - var display_name = jQuery(et).attr("value").trim(); - if (display_name === undefined || display_name.length < 1) { - showMessage("Please enter a term name.", undefined, "Error"); - return; - } - - var t = new Term({ - 'display_name': display_name, - 'vocabulary_id': this.selected.get('id') - }); - t.save({}, { - success: function () { - self.selected.get('term_set').add(t); - self.render(); - }, - error: function (model, response) { - var responseText = jQuery.parseJSON(response.responseText); - showMessage(responseText.term.error_message, undefined, "Error"); - } - }); - return false; + //if you want to create a term from user input + var display_name = jQuery(et).attr("value").trim(); + if (display_name === undefined || display_name.length < 1) { + showMessage("Please enter a term name.", undefined, "Error"); + return; + } + var t = new Term({ + 'display_name': display_name, + 'vocabulary_id': this.selected.get('id') + }); + t.save({}, { + success: function() { + self.selected.get('term_set').add(t); + self.render(); + }, + error: function(model, response) { + var responseText = jQuery.parseJSON(response.responseText); + showMessage(responseText.term.error_message, undefined, "Error"); + } + }); + return false; }, - updateTerm: function (evt) { + updateTerm: function(evt) { evt.preventDefault(); var self = this; var elt = jQuery(evt.currentTarget).prevAll("input[type='text']"); @@ -383,10 +379,10 @@ if (term.get('display_name') !== 'display_name') { term.set('display_name', display_name); term.save({}, { - success: function () { + success: function() { self.render(); }, - error: function (model, response) { + error: function(model, response) { var responseText = jQuery.parseJSON(response.responseText); showMessage(responseText.term.error_message, undefined, "Error"); } @@ -394,7 +390,7 @@ } return false; }, - deleteTerm: function (evt) { + deleteTerm: function(evt) { evt.preventDefault(); var self = this; @@ -412,14 +408,14 @@ resizable: false, modal: true, title: "Are you sure?", - close: function (event, ui) { + close: function(event, ui) { jQuery(dom).removeClass('about-to-delete'); }, buttons: { - "Cancel": function () { + "Cancel": function() { jQuery(this).dialog("close"); }, - "OK": function () { + "OK": function() { jQuery(this).dialog("close"); self.selected.get('term_set').remove(term); term.destroy(); @@ -429,23 +425,22 @@ }); return false; }, - focusTermName: function (evt) { + focusTermName: function(evt) { if (jQuery(evt.currentTarget).hasClass("default")) { jQuery(evt.currentTarget).removeClass("default"); jQuery(evt.currentTarget).attr("value", ""); } }, - blurTermName: function (evt) { + blurTermName: function(evt) { if (jQuery(evt.currentTarget).attr("value") === '' && jQuery(evt.currentTarget).attr("name") !== 'onomy_url') { jQuery(evt.currentTarget).addClass("default"); jQuery(evt.currentTarget).attr("value", "Type new term name here"); }else if(jQuery(evt.currentTarget).attr("value") === '' && jQuery(evt.currentTarget).attr("name") === "onomy_url") { - jQuery(evt.currentTarget).addClass("default"); - jQuery(evt.currentTarget).attr("value", "Enter an Onomy URL here"); - } - } - , - createOnomyVocabulary: function (evt) { + jQuery(evt.currentTarget).addClass("default"); + jQuery(evt.currentTarget).attr("value", "Enter an Onomy URL here"); + } + }, + createOnomyVocabulary: function(evt) { var et; var self; var vocabulary_id; @@ -459,10 +454,10 @@ getTheOnomy(onomyURL, self); }, - refreshOnomy: function (evt) { + refreshOnomy: function(evt) { var self = this; var urlArray; - urlArray = _.map(self.collection.models, function (model) { + urlArray = _.map(self.collection.models, function(model) { return model.attributes.onomy_url }); var address; @@ -471,11 +466,8 @@ if (!address == "") { getTheOnomy(address, self); } - } - } - }); function findUtil(array, thing){ return jQuery.grep(array, function(item){ @@ -505,7 +497,7 @@ var vocabulary_id; vocabulary_id = self.selected.get('id'); jQuery.get(onomyURL, - function (data) { + function(data) { var x; x = JSON.parse(data); @@ -520,7 +512,7 @@ //demorgans law if (!(pL === undefined || pL.length < 1)) { var search = undefined; - parents.forEach(function (a) { + parents.forEach(function(a) { if (a.display_name == pL) { search = a; } @@ -541,7 +533,7 @@ var tempV; var model_search; for (var j = 0; j < parents.length; j++) { - model_search = _.find(self.collection.models, function (model) { + model_search = _.find(self.collection.models, function(model) { return model.attributes.display_name == parents[j].display_name }); if (model_search === undefined) { @@ -556,7 +548,7 @@ }); tempV._save({}, { - success: function (it) { + success: function(it) { var tempT; self.selected = it; parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self = self.selected; @@ -567,7 +559,7 @@ 'vocabulary_id': it.attributes['id'] }); tempT._save({}, { - success: function (itT) { + success: function(itT) { parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self.get('term_set').add(itT); self.render(); } @@ -594,7 +586,7 @@ 'vocabulary_id': model_search.attributes['id'] }); tempT._save({}, { - success: function (itT) { + success: function(itT) { self.selected.get('term_set').add(itT); self.render(); } @@ -637,7 +629,7 @@ //then save it with our overriden queued save t._save({}, { wait: true, - success: function (it) { + success: function(it) { //add it to our term self.selected.get('term_set').add(it); self.render(); From 078b9818bcb3d37acfaad75ba0396c1825cf7759 Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Wed, 14 Jan 2015 15:22:42 -0500 Subject: [PATCH 47/58] declaring variables inline. --- media/js/app/taxonomy/taxonomy.js | 76 ++++++++++--------------------- 1 file changed, 25 insertions(+), 51 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 6c97f1e72..01b8c9d59 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -441,28 +441,20 @@ } }, createOnomyVocabulary: function(evt) { - var et; - var self; - var vocabulary_id; - var onomyURL; evt.preventDefault(); - et = jQuery(evt.currentTarget).prev(); + var et = jQuery(evt.currentTarget).prev(); - self = this; - vocabulary_id = this.selected.get('id'); - onomyURL = jQuery(et).attr("value").trim(); - getTheOnomy(onomyURL, self); + var vocabulary_id = this.selected.get('id'); + var onomyURL = jQuery(et).attr("value").trim(); + getTheOnomy(onomyURL, this); }, refreshOnomy: function(evt) { - var self = this; - var urlArray; - urlArray = _.map(self.collection.models, function(model) { + var urlArray = _.map(self.collection.models, function(model) { return model.attributes.onomy_url }); - var address; for (var i = 0; i < urlArray.length; i++) { - address = urlArray[i].toString(); + var address = urlArray[i].toString(); if (!address == "") { getTheOnomy(address, self); } @@ -475,40 +467,32 @@ }); } function getTheOnomy(dirtyURL, self) { - var test; - var onomyURL; var onomy_index; //this is to sanitize the url entered by the user. //checks to see if it contains onomy and json - test = dirtyURL.search(('onomy' | 'json')); - if (test != -1) { + var test = dirtyURL.search(('onomy' | 'json')); + if (test !== -1) { //grab the numbers from the url entered onomy_index = /\d+/g.exec(dirtyURL); - } - else { + } else { //display error message showMessage("Enter a valid Onomy URL", undefined, "Error"); return; } //all of the onomyURL's should fit this so i just strip the numbers from user //input and add it to the format - onomyURL = 'http://onomy.org/published/' + onomy_index + '/json'; - var vocabulary_id; - vocabulary_id = self.selected.get('id'); + var onomyURL = 'http://onomy.org/published/' + onomy_index + '/json'; + var vocabulary_id = self.selected.get('id'); jQuery.get(onomyURL, function(data) { - var x; - x = JSON.parse(data); + var x = JSON.parse(data); - var MAX; var parents = []; - MAX = x.terms.length; //change to x.terms.length after done testing + var MAX = x.terms.length; //change to x.terms.length after done testing for (var i = 0; i < MAX; i++) { - var pL; - var display; - pL = x.terms[i]['rdfs:parentLabel'].trim(); - display = x.terms[i]['rdfs:label'].trim(); + var pL = x.terms[i]['rdfs:parentLabel'].trim(); + var display = x.terms[i]['rdfs:label'].trim(); //demorgans law if (!(pL === undefined || pL.length < 1)) { var search = undefined; @@ -530,7 +514,6 @@ parents[parents.indexOf(v)].term_set.push({'display_name': display}); } if (i == MAX - 1) { - var tempV; var model_search; for (var j = 0; j < parents.length; j++) { model_search = _.find(self.collection.models, function(model) { @@ -539,7 +522,7 @@ if (model_search === undefined) { //if we cant find the vocab in the collection we create a new one. - tempV = new Vocabulary({ + var tempV = new Vocabulary({ 'display_name': parents[j].display_name, 'content_type_id': 14, 'object_id': 1, @@ -549,12 +532,11 @@ tempV._save({}, { success: function(it) { - var tempT; self.selected = it; parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self = self.selected; self.collection.add(it); for (var z = 0; z < parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set.length; z++) { - tempT = new Term({ + var tempT = new Term({ 'display_name': parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set[z].display_name, 'vocabulary_id': it.attributes['id'] }); @@ -570,18 +552,15 @@ } else { //we do find the model. we just add the term to it. self.selected = model_search; - var urlcsv; - var url; - var tempT; - urlcsv = self.selected.get('onomy_url'); - url = urlcsv.split(','); + var urlcsv = self.selected.get('onomy_url'); + var url = urlcsv.split(','); if (!(_.contains(url, onomyURL))) { url.push(onomyURL); model_search.save({'onomy_url': url.toString()}); } for (var z = 0; z < parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set.length; z++) { - tempT = new Term({ + var tempT = new Term({ 'display_name': parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set[z].display_name, 'vocabulary_id': model_search.attributes['id'] }); @@ -605,14 +584,10 @@ if (display === undefined || display.length < 1) { continue; } - var id; - var v; - var urlcsv; - var url; - id = self.selected.attributes.id; - v = self.collection.getByDataId(id); - urlcsv = self.selected.get('onomy_url'); - url = urlcsv.split(','); + var id = self.selected.attributes.id; + var v = self.collection.getByDataId(id); + var urlcsv = self.selected.get('onomy_url'); + var url = urlcsv.split(','); //if this vocabulary doesn't contain the url we punched in if (!(_.contains(url, onomyURL))) { @@ -621,8 +596,7 @@ v.save({'onomy_url': url.toString()}); } //we create our term - var t; - t = new Term({ + var t = new Term({ 'display_name': display, 'vocabulary_id': vocabulary_id }); From 9317d0ad053dd6aa52443a2190d67b3831aa19f6 Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Wed, 14 Jan 2015 15:57:05 -0500 Subject: [PATCH 48/58] spacing + flake8 --- media/js/app/taxonomy/taxonomy.js | 250 ++++++++++++++---------------- mediathread/assetmgr/urls.py | 1 + mediathread/assetmgr/views.py | 68 ++++---- 3 files changed, 159 insertions(+), 160 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 01b8c9d59..764b2c883 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -484,137 +484,127 @@ //input and add it to the format var onomyURL = 'http://onomy.org/published/' + onomy_index + '/json'; var vocabulary_id = self.selected.get('id'); - jQuery.get(onomyURL, - function(data) { - var x = JSON.parse(data); - - var parents = []; - var MAX = x.terms.length; //change to x.terms.length after done testing - for (var i = 0; i < MAX; i++) { - var pL = x.terms[i]['rdfs:parentLabel'].trim(); - var display = x.terms[i]['rdfs:label'].trim(); - //demorgans law - if (!(pL === undefined || pL.length < 1)) { - var search = undefined; - parents.forEach(function(a) { - if (a.display_name == pL) { - search = a; - } - }); - if (search === undefined) { - //create the Vocabulary - var temp; - temp = {'display_name': pL, 'term_set': [], 'self': undefined}; - parents.push(temp); - parents[parents.indexOf(temp)].term_set.push({'display_name': display}); - } - else { - //add the term to the Vocabulary in parents - v = search; - parents[parents.indexOf(v)].term_set.push({'display_name': display}); - } - if (i == MAX - 1) { - var model_search; - for (var j = 0; j < parents.length; j++) { - model_search = _.find(self.collection.models, function(model) { - return model.attributes.display_name == parents[j].display_name - }); - if (model_search === undefined) { - //if we cant find the vocab in the collection we create a new one. - - var tempV = new Vocabulary({ - 'display_name': parents[j].display_name, - 'content_type_id': 14, - 'object_id': 1, - 'term_set': undefined, - 'onomy_url': onomyURL - }); - - tempV._save({}, { - success: function(it) { - self.selected = it; - parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self = self.selected; - self.collection.add(it); - for (var z = 0; z < parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set.length; z++) { - var tempT = new Term({ - 'display_name': parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set[z].display_name, - 'vocabulary_id': it.attributes['id'] - }); - tempT._save({}, { - success: function(itT) { - parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self.get('term_set').add(itT); - self.render(); - } - }); - } - } - }); - } else { - //we do find the model. we just add the term to it. - self.selected = model_search; - var urlcsv = self.selected.get('onomy_url'); - var url = urlcsv.split(','); - if (!(_.contains(url, onomyURL))) { - url.push(onomyURL); - model_search.save({'onomy_url': url.toString()}); - } - - for (var z = 0; z < parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set.length; z++) { - var tempT = new Term({ - 'display_name': parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set[z].display_name, - 'vocabulary_id': model_search.attributes['id'] - }); - tempT._save({}, { - success: function(itT) { - self.selected.get('term_set').add(itT); - self.render(); - } - - }); - } - - } - - - } - } - - } else { - - if (display === undefined || display.length < 1) { - continue; - } - var id = self.selected.attributes.id; - var v = self.collection.getByDataId(id); - var urlcsv = self.selected.get('onomy_url'); - var url = urlcsv.split(','); - - //if this vocabulary doesn't contain the url we punched in - if (!(_.contains(url, onomyURL))) { - //add it to our array we made and save it in the vocab - url.push(onomyURL); - v.save({'onomy_url': url.toString()}); - } - //we create our term - var t = new Term({ - 'display_name': display, - 'vocabulary_id': vocabulary_id - }); - //then save it with our overriden queued save - t._save({}, { - wait: true, - success: function(it) { - //add it to our term - self.selected.get('term_set').add(it); - self.render(); - } - }); - } - } - }); + jQuery.get(onomyURL, function(data) { + var x = JSON.parse(data); + + var parents = []; + var MAX = x.terms.length; //change to x.terms.length after done testing + for (var i = 0; i < MAX; i++) { + var pL = x.terms[i]['rdfs:parentLabel'].trim(); + var display = x.terms[i]['rdfs:label'].trim(); + + //demorgans law + if (!(pL === undefined || pL.length < 1)) { + var search = undefined; + parents.forEach(function(a) { + if (a.display_name == pL) { + search = a; + } + }); + if (search === undefined) { + //create the Vocabulary + var temp = {'display_name': pL, 'term_set': [], 'self': undefined}; + parents.push(temp); + parents[parents.indexOf(temp)].term_set.push({'display_name': display}); + } else { + //add the term to the Vocabulary in parents + v = search; + parents[parents.indexOf(v)].term_set.push({'display_name': display}); + } + + if (i == MAX - 1) { + for (var j = 0; j < parents.length; j++) { + var model_search = _.find(self.collection.models, function(model) { + return model.attributes.display_name == parents[j].display_name + }); + if (model_search === undefined) { + if we cant find the vocab in the collection we create a new one. + + var tempV = new Vocabulary({ + 'display_name': parents[j].display_name, + 'content_type_id': 14, + 'object_id': 1, + 'term_set': undefined, + 'onomy_url': onomyURL + }); + + tempV._save({}, { + success: function(it) { + self.selected = it; + parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self = self.selected; + self.collection.add(it); + for (var z = 0; z < parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set.length; z++) { + var tempT = new Term({ + 'display_name': parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set[z].display_name, + 'vocabulary_id': it.attributes['id'] + }); + tempT._save({}, { + success: function(itT) { + parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self.get('term_set').add(itT); + self.render(); + } + }); + } + } + }); + } else { + //we do find the model. we just add the term to it. + self.selected = model_search; + var urlcsv = self.selected.get('onomy_url'); + var url = urlcsv.split(','); + if (!(_.contains(url, onomyURL))) { + url.push(onomyURL); + model_search.save({'onomy_url': url.toString()}); + } + + for (var z = 0; z < parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set.length; z++) { + var tempT = new Term({ + 'display_name': parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set[z].display_name, + 'vocabulary_id': model_search.attributes['id'] + }); + tempT._save({}, { + success: function(itT) { + self.selected.get('term_set').add(itT); + self.render(); + } + }); + } + } + } + } + } else { + if (display === undefined || display.length < 1) { + continue; + } + var id = self.selected.attributes.id; + var v = self.collection.getByDataId(id); + var urlcsv = self.selected.get('onomy_url'); + var url = urlcsv.split(','); + + //if this vocabulary doesn't contain the url we punched in + if (!(_.contains(url, onomyURL))) { + //add it to our array we made and save it in the vocab + url.push(onomyURL); + v.save({'onomy_url': url.toString()}); + } + //we create our term + var t = new Term({ + 'display_name': display, + 'vocabulary_id': vocabulary_id + }); + //then save it with our overriden queued save + t._save({}, { + wait: true, + success: function(it) { + //add it to our term + self.selected.get('term_set').add(it); + self.render(); + } + }); + } + } + }); } - - }(jQuery)); diff --git a/mediathread/assetmgr/urls.py b/mediathread/assetmgr/urls.py index 7ec7259fa..4775bcaae 100644 --- a/mediathread/assetmgr/urls.py +++ b/mediathread/assetmgr/urls.py @@ -44,6 +44,7 @@ name="final_cut_pro_xml"), url(r'MEPdump/', 'mep_dump', name='mep_dump'), + # Asset workspace variations url(r'^$', AssetWorkspaceView.as_view(), {}, 'asset-collection-view'), ) diff --git a/mediathread/assetmgr/views.py b/mediathread/assetmgr/views.py index d40a64386..1659de8b7 100644 --- a/mediathread/assetmgr/views.py +++ b/mediathread/assetmgr/views.py @@ -448,7 +448,7 @@ def final_cut_pro_xml(request, asset_id): return HttpResponse('Not Implemented: No Final Cut Pro Xmeml support', status=503) -#@waffle_switch('ONOMY_SWITCH') + def mep_dump(request): user = request.user user_id = user.id @@ -457,56 +457,61 @@ def mep_dump(request): ar.Meta.excludes = ['added', 'modified', 'course', 'active'] lst = [] - notes = SherdNote.objects.get_related_notes(assets, user_id or None, [request.user.id], True) + notes = SherdNote.objects.get_related_notes(assets, user_id or None, + [request.user.id], True) - api_response = ar.render_list(request, [request.user.id], [request.user.id], assets, notes) + api_response = ar.render_list(request, [request.user.id], + [request.user.id], assets, notes) if len(api_response) == 0: return HttpResponse("There are no videos in your collection") for i in range(0, len(api_response)): data = api_response[i] - #we need to turn the blob into json so we can grab data easier utf_blob = data.get('metadata_blob') jsonmetadata_blob = dict() if utf_blob is not None: utf_blob = utf_blob.encode('UTF-8', 'ignore') jsonmetadata_blob = json.loads(utf_blob) - #this maps the xmlns paths and all that fun stuff + # this maps the xmlns paths and all that fun stuff NS_MAP = {"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - "rdfs": "http://www.w3.org/2000/01/rdf-schema#", - "art": "http://simile.mit.edu/2003/10/ontologies/artstor#", - "foaf": "http://xmlns.com/foaf/0.1/", - "dcterms": "http://purl.org/dc/terms/", - "sioc": "http://rdfs.org/sioc/ns#", - "oa": "http://www.openannotation.org/ns/"} - #short hands the ns + "rdfs": "http://www.w3.org/2000/01/rdf-schema#", + "art": "http://simile.mit.edu/2003/10/ontologies/artstor#", + "foaf": "http://xmlns.com/foaf/0.1/", + "dcterms": "http://purl.org/dc/terms/", + "sioc": "http://rdfs.org/sioc/ns#", + "oa": "http://www.openannotation.org/ns/"} + # short hands the ns RDF = "{%s}" % NS_MAP['rdf'] RDFS = "{%s}" % NS_MAP['rdfs'] ART = "{%s}" % NS_MAP['art'] FOAF = "{%s}" % NS_MAP['foaf'] DCTERMS = "{%s}" % NS_MAP['dcterms'] - SIOC = "{%s}" % NS_MAP['sioc'] OA = "{%s}" % NS_MAP['oa'] - #rdf is the 'root' + # rdf is the 'root' rdf = ET.Element(RDF + "RDF", nsmap=NS_MAP) - #creating the main rdf for the video - description = ET.SubElement(rdf, "{%s}" % NS_MAP['rdf'] + "Description") + # creating the main rdf for the video + description = ET.SubElement(rdf, "{%s}" % NS_MAP['rdf'] + + "Description") description.attrib[RDF + 'about'] = data.get('local_url') rdf_type = ET.SubElement(description, RDF + "type") rdf_type.attrib[RDF + 'resource'] = data.get('primary_type') art_thumb = ET.SubElement(description, ART + 'thumbnail') - art_thumb.attrib[RDF + 'resource'] = data.get('sources').get('thumb')['url'] + art_thumb.attrib[RDF + 'resource'] = \ + data.get('sources').get('thumb')['url'] art_url = ET.SubElement(description, ART + 'url') - art_url.attrib[RDF + 'resource'] = data.get('sources').get('url')['url'] + art_url.attrib[RDF + 'resource'] = \ + data.get('sources').get('url')['url'] art_source = ET.SubElement(description, ART + 'sourceLocation') - art_source.attrib[RDF + 'resource'] = data.get('sources').get('youtube')['url'] + art_source.attrib[RDF + 'resource'] = \ + data.get('sources').get('youtube')['url'] dc_title = ET.SubElement(description, DCTERMS + 'title') dc_title.text = data.get('title') dc_desc = ET.SubElement(description, DCTERMS + 'description') dc_desc.text = jsonmetadata_blob.get('description')[0] dc_vers = ET.SubElement(description, DCTERMS + 'isVersionOf') - dc_vers.attrib[RDF + 'resource'] = data.get('sources').get('url')['url'] + dc_vers.attrib[RDF + 'resource'] = \ + data.get('sources').get('url')['url'] dc_pub = ET.SubElement(description, DCTERMS + 'publisher') dc_pub.text = jsonmetadata_blob.get('author')[0] dc_cntb = ET.SubElement(description, DCTERMS + 'contributor') @@ -516,7 +521,8 @@ def mep_dump(request): dc_form = ET.SubElement(description, DCTERMS + 'format') dc_form.text = data.get('media_type_label') - #this can be found if the video has annotations but if it doesnt it doesnt show up + # this can be found if the video has annotations but + # if it doesnt it doesnt show up dc_ext = ET.SubElement(description, DCTERMS + 'extent') dc_ext.text = "CANNOT BE FOUND" @@ -528,19 +534,20 @@ def mep_dump(request): dc_rel.text = jsonmetadata_blob.get('category')[0] vocab = [] - #now we do annotations and tags etc. + # now we do annotations and tags etc. for i in range(0, data.get('annotation_count')): anno = ET.SubElement(rdf, RDF + 'Description') anno.attrib[RDF + 'about'] = data.get('annotations')[i]['url'] anno_resource = ET.SubElement(anno, OA + 'hasBody') - anno_resource.attrib[RDF + 'resource'] = data.get('annotations')[i]['url'] + anno_resource.attrib[RDF + 'resource'] = \ + data.get('annotations')[i]['url'] vocab.append(data.get('annotations')[i]['vocabulary']) for j in range(0, len(vocab)): - #create the sub elements for the annotations + # create the sub elements for the annotations anno_vocab = ET.SubElement(anno, OA + 'hasBody') anno_vocab.attrib[RDF + 'nodeID'] = vocab[j][0]['display_name'] - #create the description for that vocab while we are at it + # create the description for that vocab while we are at it for t in vocab[j][0]['terms']: term = ET.SubElement(rdf, RDF + 'Description') term.attrib[RDF + 'nodeID'] = t['name'] @@ -549,16 +556,17 @@ def mep_dump(request): foaf_page = ET.SubElement(term, FOAF + "page") foaf_page.attrib[RDF + 'resource'] = t['resource_uri'] - anno_author = ET.SubElement(anno, OA + 'annotatedBy') - anno_author.attrib[RDF + 'resource'] = data.get('annotations')[i]['author']['username'] + anno_author.attrib[RDF + 'resource'] = \ + data.get('annotations')[i]['author']['username'] anno_time = ET.SubElement(anno, OA + 'annotatedAt') anno_time.text = data.get('annotations')[i]['metadata']['modified'] foaf_name = ET.SubElement(anno, FOAF + "name") - foaf_name.text = data.get('annotations')[i]['author']['public_name'] + foaf_name.text = \ + data.get('annotations')[i]['author']['public_name'] - lst.append(ET.tostring(rdf, pretty_print=True, xml_declaration=True, encoding="UTF-8")) - print ET.tostring(rdf, pretty_print=True, xml_declaration=True, encoding="UTF-8") + lst.append(ET.tostring(rdf, pretty_print=True, + xml_declaration=True, encoding="UTF-8")) return HttpResponse(lst) From 633bec453bdb3249ba210207d0ad000fa3f6b09c Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Wed, 28 Jan 2015 15:58:30 -0500 Subject: [PATCH 49/58] Fix onomyUrl validation --- media/js/app/taxonomy/taxonomy.js | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 764b2c883..95e2d4275 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -447,7 +447,6 @@ var vocabulary_id = this.selected.get('id'); var onomyURL = jQuery(et).attr("value").trim(); getTheOnomy(onomyURL, this); - }, refreshOnomy: function(evt) { var urlArray = _.map(self.collection.models, function(model) { @@ -466,23 +465,15 @@ return item.display_name == thing; }); } - function getTheOnomy(dirtyURL, self) { - var onomy_index; - - //this is to sanitize the url entered by the user. - //checks to see if it contains onomy and json - var test = dirtyURL.search(('onomy' | 'json')); - if (test !== -1) { - //grab the numbers from the url entered - onomy_index = /\d+/g.exec(dirtyURL); - } else { + function getTheOnomy(onomyURL, self) { + var the_regex = /onomy.org\/published\/(\d+)\/json/g; + var match = the_regex.exec(onomyUrl); + if (match.length < 0) { //display error message showMessage("Enter a valid Onomy URL", undefined, "Error"); return; } - //all of the onomyURL's should fit this so i just strip the numbers from user - //input and add it to the format - var onomyURL = 'http://onomy.org/published/' + onomy_index + '/json'; + var vocabulary_id = self.selected.get('id'); jQuery.get(onomyURL, function(data) { var x = JSON.parse(data); @@ -518,7 +509,7 @@ return model.attributes.display_name == parents[j].display_name }); if (model_search === undefined) { - if we cant find the vocab in the collection we create a new one. + // if we cant find the vocab in the collection we create a new one. var tempV = new Vocabulary({ 'display_name': parents[j].display_name, From dd6c3cc6b306dacf28a77475390ca6cbb4145f19 Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Wed, 28 Jan 2015 16:00:45 -0500 Subject: [PATCH 50/58] Scoping onomy functionality within the backbone view class --- media/js/app/taxonomy/taxonomy.js | 258 +++++++++++++++--------------- 1 file changed, 130 insertions(+), 128 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 95e2d4275..023f2daa4 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -133,6 +133,7 @@ "deleteTerm", "createOnomyVocabulary", "refreshOnomy", + "getTheOnomy", "activateTab"); this.context = options; @@ -446,7 +447,7 @@ var vocabulary_id = this.selected.get('id'); var onomyURL = jQuery(et).attr("value").trim(); - getTheOnomy(onomyURL, this); + this.getTheOnomy(onomyURL, this); }, refreshOnomy: function(evt) { var urlArray = _.map(self.collection.models, function(model) { @@ -455,147 +456,148 @@ for (var i = 0; i < urlArray.length; i++) { var address = urlArray[i].toString(); if (!address == "") { - getTheOnomy(address, self); + this.getTheOnomy(address, self); } } + }, + findUtil: function(array, thing){ + return jQuery.grep(array, function(item){ + return item.display_name == thing; + }); } - }); - function findUtil(array, thing){ - return jQuery.grep(array, function(item){ - return item.display_name == thing; - }); - } - function getTheOnomy(onomyURL, self) { - var the_regex = /onomy.org\/published\/(\d+)\/json/g; - var match = the_regex.exec(onomyUrl); - if (match.length < 0) { - //display error message - showMessage("Enter a valid Onomy URL", undefined, "Error"); - return; - } - - var vocabulary_id = self.selected.get('id'); - jQuery.get(onomyURL, function(data) { - var x = JSON.parse(data); - - var parents = []; - var MAX = x.terms.length; //change to x.terms.length after done testing - for (var i = 0; i < MAX; i++) { - var pL = x.terms[i]['rdfs:parentLabel'].trim(); - var display = x.terms[i]['rdfs:label'].trim(); - - //demorgans law - if (!(pL === undefined || pL.length < 1)) { - var search = undefined; - parents.forEach(function(a) { - if (a.display_name == pL) { - search = a; + getTheOnomy: function(onomyURL) { + var self = this; + var the_regex = /onomy.org\/published\/(\d+)\/json/g; + var match = the_regex.exec(onomyURL); + if (match.length < 0) { + //display error message + showMessage("Enter a valid Onomy URL", undefined, "Error"); + return; + } + + var vocabulary_id = self.selected.get('id'); + jQuery.get(onomyURL, function(data) { + var x = JSON.parse(data); + + var parents = []; + var MAX = x.terms.length; //change to x.terms.length after done testing + for (var i = 0; i < MAX; i++) { + var pL = x.terms[i]['rdfs:parentLabel'].trim(); + var display = x.terms[i]['rdfs:label'].trim(); + + //demorgans law + if (!(pL === undefined || pL.length < 1)) { + var search = undefined; + parents.forEach(function(a) { + if (a.display_name == pL) { + search = a; + } + }); + if (search === undefined) { + //create the Vocabulary + var temp = {'display_name': pL, 'term_set': [], 'self': undefined}; + parents.push(temp); + parents[parents.indexOf(temp)].term_set.push({'display_name': display}); + } else { + //add the term to the Vocabulary in parents + v = search; + parents[parents.indexOf(v)].term_set.push({'display_name': display}); } - }); - if (search === undefined) { - //create the Vocabulary - var temp = {'display_name': pL, 'term_set': [], 'self': undefined}; - parents.push(temp); - parents[parents.indexOf(temp)].term_set.push({'display_name': display}); - } else { - //add the term to the Vocabulary in parents - v = search; - parents[parents.indexOf(v)].term_set.push({'display_name': display}); - } - - if (i == MAX - 1) { - for (var j = 0; j < parents.length; j++) { - var model_search = _.find(self.collection.models, function(model) { - return model.attributes.display_name == parents[j].display_name - }); - if (model_search === undefined) { - // if we cant find the vocab in the collection we create a new one. - - var tempV = new Vocabulary({ - 'display_name': parents[j].display_name, - 'content_type_id': 14, - 'object_id': 1, - 'term_set': undefined, - 'onomy_url': onomyURL + + if (i == MAX - 1) { + for (var j = 0; j < parents.length; j++) { + var model_search = _.find(self.collection.models, function(model) { + return model.attributes.display_name == parents[j].display_name }); + if (model_search === undefined) { + // if we cant find the vocab in the collection we create a new one. + + var tempV = new Vocabulary({ + 'display_name': parents[j].display_name, + 'content_type_id': 14, + 'object_id': 1, + 'term_set': undefined, + 'onomy_url': onomyURL + }); - tempV._save({}, { - success: function(it) { - self.selected = it; - parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self = self.selected; - self.collection.add(it); - for (var z = 0; z < parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set.length; z++) { - var tempT = new Term({ - 'display_name': parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].term_set[z].display_name, - 'vocabulary_id': it.attributes['id'] - }); - tempT._save({}, { - success: function(itT) { - parents[parents.indexOf(findUtil(parents, it.attributes['display_name'])[0])].self.get('term_set').add(itT); - self.render(); - } - }); + tempV._save({}, { + success: function(it) { + self.selected = it; + parents[parents.indexOf(self.findUtil(parents, it.attributes['display_name'])[0])].self = self.selected; + self.collection.add(it); + for (var z = 0; z < parents[parents.indexOf(self.findUtil(parents, it.attributes['display_name'])[0])].term_set.length; z++) { + var tempT = new Term({ + 'display_name': parents[parents.indexOf(self.findUtil(parents, it.attributes['display_name'])[0])].term_set[z].display_name, + 'vocabulary_id': it.attributes['id'] + }); + tempT._save({}, { + success: function(itT) { + parents[parents.indexOf(self.findUtil(parents, it.attributes['display_name'])[0])].self.get('term_set').add(itT); + self.render(); + } + }); + } } + }); + } else { + //we do find the model. we just add the term to it. + self.selected = model_search; + var urlcsv = self.selected.get('onomy_url'); + var url = urlcsv.split(','); + if (!(_.contains(url, onomyURL))) { + url.push(onomyURL); + model_search.save({'onomy_url': url.toString()}); } - }); - } else { - //we do find the model. we just add the term to it. - self.selected = model_search; - var urlcsv = self.selected.get('onomy_url'); - var url = urlcsv.split(','); - if (!(_.contains(url, onomyURL))) { - url.push(onomyURL); - model_search.save({'onomy_url': url.toString()}); - } - for (var z = 0; z < parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set.length; z++) { - var tempT = new Term({ - 'display_name': parents[parents.indexOf(findUtil(parents, model_search.attributes['display_name'])[0])].term_set[z].display_name, - 'vocabulary_id': model_search.attributes['id'] - }); - tempT._save({}, { - success: function(itT) { - self.selected.get('term_set').add(itT); - self.render(); - } - }); + for (var z = 0; z < parents[parents.indexOf(self.findUtil(parents, model_search.attributes['display_name'])[0])].term_set.length; z++) { + var tempT = new Term({ + 'display_name': parents[parents.indexOf(self.findUtil(parents, model_search.attributes['display_name'])[0])].term_set[z].display_name, + 'vocabulary_id': model_search.attributes['id'] + }); + tempT._save({}, { + success: function(itT) { + self.selected.get('term_set').add(itT); + self.render(); + } + }); + } } } } - } - } else { - if (display === undefined || display.length < 1) { - continue; - } - var id = self.selected.attributes.id; - var v = self.collection.getByDataId(id); - var urlcsv = self.selected.get('onomy_url'); - var url = urlcsv.split(','); - - //if this vocabulary doesn't contain the url we punched in - if (!(_.contains(url, onomyURL))) { - //add it to our array we made and save it in the vocab - url.push(onomyURL); - v.save({'onomy_url': url.toString()}); - } - //we create our term - var t = new Term({ - 'display_name': display, - 'vocabulary_id': vocabulary_id - }); - //then save it with our overriden queued save - t._save({}, { - wait: true, - success: function(it) { - //add it to our term - self.selected.get('term_set').add(it); - self.render(); + } else { + if (display === undefined || display.length < 1) { + continue; + } + var id = self.selected.attributes.id; + var v = self.collection.getByDataId(id); + var urlcsv = self.selected.get('onomy_url'); + var url = urlcsv.split(','); + + //if this vocabulary doesn't contain the url we punched in + if (!(_.contains(url, onomyURL))) { + //add it to our array we made and save it in the vocab + url.push(onomyURL); + v.save({'onomy_url': url.toString()}); } - }); + //we create our term + var t = new Term({ + 'display_name': display, + 'vocabulary_id': vocabulary_id + }); + //then save it with our overriden queued save + t._save({}, { + wait: true, + success: function(it) { + //add it to our term + self.selected.get('term_set').add(it); + self.render(); + } + }); + } } - } - }); - } + }); + } + }); }(jQuery)); From 5a06324db48a09976af6e50c7d96e38617c9ab22 Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Wed, 28 Jan 2015 20:06:50 -0500 Subject: [PATCH 51/58] Python Polish * Fix "not in" syntax in views.py * Fix comment in api --- mediathread/taxonomy/api.py | 4 ++-- mediathread/taxonomy/views.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mediathread/taxonomy/api.py b/mediathread/taxonomy/api.py index 4c1ae0293..6e146cdd0 100644 --- a/mediathread/taxonomy/api.py +++ b/mediathread/taxonomy/api.py @@ -1,4 +1,4 @@ -#pylint: disable-msg=R0904 +# pylint: disable-msg=R0904 from courseaffils.models import Course from django.contrib.contenttypes.models import ContentType from django.db.models import Count @@ -17,7 +17,7 @@ def is_valid(self, bundle, request=None): display_name=bundle.data['display_name'], vocabulary_id=bundle.data['vocabulary_id']) - if len(a) > 0: # vocabulary exists with this name + if len(a) > 0: # term exists with this name if 'pk' not in bundle.data or a[0].pk != int(bundle.data['pk']): # a vocabulary already exists with this name msg = 'A %s term already exists. Please choose another name' \ diff --git a/mediathread/taxonomy/views.py b/mediathread/taxonomy/views.py index 739fd03e6..a96b44362 100644 --- a/mediathread/taxonomy/views.py +++ b/mediathread/taxonomy/views.py @@ -41,8 +41,8 @@ def update_vocabulary_terms(request, content_object): for a in associations: vocabulary_id = str(a.term.vocabulary.id) term_id = str(a.term.id) - if (not vocabulary_id in concepts or - not term_id in concepts[vocabulary_id]): + if (vocabulary_id not in concepts or + term_id not in concepts[vocabulary_id]): a.delete() content_type = ContentType.objects.get_for_model(content_object) From 6c81c24b725026d9293d71e4832fb35c0c675c94 Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Fri, 30 Jan 2015 14:13:13 -0500 Subject: [PATCH 52/58] Adding Styles --- media/css/mediathread.css | 26 +++++-- media/js/app/taxonomy/taxonomy.js | 75 ++++++++++---------- mediathread/templates/taxonomy/taxonomy.html | 48 ++++++++----- 3 files changed, 91 insertions(+), 58 deletions(-) diff --git a/media/css/mediathread.css b/media/css/mediathread.css index 4994688b5..a6bf8a88d 100644 --- a/media/css/mediathread.css +++ b/media/css/mediathread.css @@ -4636,6 +4636,20 @@ div#taxonomy .ui-tabs-vertical .ui-tabs-panel { width: 65%; } +div#taxonomy .import-vocabulary-open { + font-size: .75em; + float: right; +} + +div#taxonomy .vocabulary-import { + margin: 0 -1px .4em 0; + outline: 0; + height: 40px; + padding: 10px; + background-color: #ededed; + border-radius: 5px; +} + div#taxonomy div.vocabulary-display a.create-vocabulary-open { width: 94%; height: 24px; @@ -4651,6 +4665,8 @@ div#taxonomy div.term-container a.edit-term-close, div#taxonomy div.term-container a.edit-term-submit, div#taxonomy div.vocabulary-edit a.edit-vocabulary-close, div#taxonomy div.vocabulary-edit a.edit-vocabulary-submit, +div#taxonomy div.vocabulary-import a.import-vocabulary-close, +div#taxonomy div.vocabulary-import a.import-vocabulary-submit, div#taxonomy div.vocabulary-create a.create-vocabulary-close, div#taxonomy div.vocabulary-create a.create-vocabulary-submit { float: right; @@ -4662,6 +4678,7 @@ div#taxonomy div.vocabulary-create a.create-vocabulary-submit { div#taxonomy div.term-container a.edit-term-close, div#taxonomy div.vocabulary-edit a.edit-vocabulary-close, +div#taxonomy div.vocabulary-import a.import-vocabulary-close, div#taxonomy div.vocabulary-create a.create-vocabulary-close { margin: 0 5px 0 5px; } @@ -4687,11 +4704,9 @@ div#taxonomy div.terms a.create-term-submit { padding: 4px; } -div#taxonomy div.onomy a.refresh-button-submit { - margin: 0 0 0 5px; - padding: 6.5px; - padding-bottom: 4px; - padding-top: 8.5px; +div#taxonomy div.terms a.refresh-button-submit { + padding: 5px 0 5px 5px; + float: right; } div#taxonomy div.onomy a.onomy-terms-submit img { @@ -4723,7 +4738,6 @@ div#taxonomy div.term-container { border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px; - width: 73%; margin: 5px 0 5px 5px; } diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 023f2daa4..80092ad96 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -102,6 +102,8 @@ 'click a.delete-vocabulary': 'deleteVocabulary', 'click a.create-vocabulary-open': 'toggleCreateVocabulary', 'click a.create-vocabulary-close': 'toggleCreateVocabulary', + 'click a.import-vocabulary-open': 'toggleImportVocabulary', + 'click a.import-vocabulary-close': 'toggleImportVocabulary', 'click a.edit-vocabulary-open': 'toggleEditVocabulary', 'click a.edit-vocabulary-close': 'toggleEditVocabulary', 'click a.create-vocabulary-submit': 'createVocabulary', @@ -109,32 +111,24 @@ 'focus input[name="display_name"]': 'focusVocabularyName', 'blur input[name="display_name"]': 'blurVocabularyName', 'focus input[name="term_name"]': 'focusTermName', - 'focus input[name="onomy_url"]': 'focusTermName', 'blur input[name="term_name"]': 'blurTermName', - 'blur input[name="onomy_url"]': 'blurTermName', 'click a.create-term-submit': 'createTerm', 'keypress input[name="term_name"]': 'keypressTermName', 'click a.edit-term-submit': 'updateTerm', 'click a.edit-term-open': 'showEditTerm', 'click a.edit-term-close': 'hideEditTerm', 'click a.delete-term': 'deleteTerm', - 'click a.onomy-terms-submit': 'createOnomyVocabulary', - 'click a.refresh-button-submit' : 'refreshOnomy' + 'click a.import-vocabulary-submit': 'createOnomyVocabulary', + 'click a.refresh-button-submit': 'refreshOnomy' }, initialize: function(options) { _.bindAll(this, "render", - "createVocabulary", - "updateVocabulary", - "deleteVocabulary", - "createTerm", - "keypressTermName", - "updateTerm", - "deleteTerm", - "createOnomyVocabulary", - "refreshOnomy", - "getTheOnomy", - "activateTab"); + "createVocabulary", "updateVocabulary", "deleteVocabulary", + "createTerm", "keypressTermName", "updateTerm", "deleteTerm", + "createOnomyVocabulary", "refreshOnomy", "getTheOnomy", + "activateTab", + "toggleCreateVocabulary", "toggleImportVocabulary"); this.context = options; this.vocabularyTemplate = @@ -174,16 +168,21 @@ this.selected = this.collection.at(0); } }, + toggleImportVocabulary: function(evt) { + evt.preventDefault(); + jQuery(this.el).find(".vocabulary-import").toggle(); + return false; + }, toggleCreateVocabulary: function(evt) { evt.preventDefault(); var parent = jQuery(evt.currentTarget).parents("li")[0]; - jQuery(parent).find("div.vocabulary-display, div.vocabulary-create").toggle(); + jQuery(parent).find(".vocabulary-display, .vocabulary-create").toggle(); return false; }, toggleEditVocabulary: function(evt) { evt.preventDefault(); var parent = jQuery(evt.currentTarget).parents("li")[0]; - jQuery(parent).find("div.vocabulary-display, div.vocabulary-edit").toggle(); + jQuery(parent).find(".vocabulary-display, .vocabulary-edit").toggle(); jQuery(parent).find("input[name='display_name']").focus(); return false; }, @@ -433,30 +432,41 @@ } }, blurTermName: function(evt) { - if (jQuery(evt.currentTarget).attr("value") === '' && jQuery(evt.currentTarget).attr("name") !== 'onomy_url') { + if (jQuery(evt.currentTarget).attr("value") === '') { jQuery(evt.currentTarget).addClass("default"); jQuery(evt.currentTarget).attr("value", "Type new term name here"); - }else if(jQuery(evt.currentTarget).attr("value") === '' && jQuery(evt.currentTarget).attr("name") === "onomy_url") { - jQuery(evt.currentTarget).addClass("default"); - jQuery(evt.currentTarget).attr("value", "Enter an Onomy URL here"); } }, createOnomyVocabulary: function(evt) { evt.preventDefault(); - var et = jQuery(evt.currentTarget).prev(); + var elt = jQuery(this.el).find('input[name="onomy_url"]'); + + var onomyUrl = jQuery(elt).val().trim(); + if (onomyUrl.length < 1) { + showMessage("Please enter a valid Onomy JSON url.", undefined, "Error"); + return; + } + + var the_regex = /onomy.org\/published\/(\d+)\/json/g; + var match = the_regex.exec(onomyUrl); + if (match.length < 0) { + //display error message + showMessage("Please enter a valid Onomy JSON Url", undefined, "Error"); + return; + } + + // save the onomyUrl to the selected vocabulary - var vocabulary_id = this.selected.get('id'); - var onomyURL = jQuery(et).attr("value").trim(); - this.getTheOnomy(onomyURL, this); + this.getTheOnomy(onomyUrl); }, refreshOnomy: function(evt) { - var urlArray = _.map(self.collection.models, function(model) { + var urlArray = _.map(this.collection.models, function(model) { return model.attributes.onomy_url }); for (var i = 0; i < urlArray.length; i++) { var address = urlArray[i].toString(); if (!address == "") { - this.getTheOnomy(address, self); + this.getTheOnomy(address); } } }, @@ -464,18 +474,11 @@ return jQuery.grep(array, function(item){ return item.display_name == thing; }); - } + }, getTheOnomy: function(onomyURL) { var self = this; - var the_regex = /onomy.org\/published\/(\d+)\/json/g; - var match = the_regex.exec(onomyURL); - if (match.length < 0) { - //display error message - showMessage("Enter a valid Onomy URL", undefined, "Error"); - return; - } - var vocabulary_id = self.selected.get('id'); + jQuery.get(onomyURL, function(data) { var x = JSON.parse(data); diff --git a/mediathread/templates/taxonomy/taxonomy.html b/mediathread/templates/taxonomy/taxonomy.html index 41c98712c..dde30be8f 100644 --- a/mediathread/templates/taxonomy/taxonomy.html +++ b/mediathread/templates/taxonomy/taxonomy.html @@ -64,23 +64,39 @@ <% for (var i=0; i < vocabularies.length; i++) { %>
    -
    - - - submit - - - refresh - -
    -
    -

    <%=vocabularies[i].display_name%> Concept

    -

    Terms

    -
    - - - Create term +

    + <%=vocabularies[i].display_name%> Concept + <% if (vocabularies[i].onomy_url.length < 1) { %>Import from Onomy<% } %> +

    + <% if (vocabularies[i].onomy_url.length < 1) { %> + + <% } %> +
    + <% if (vocabularies[i].onomy_url.length > 0) { %> + Onomy Url: + <%=vocabularies[i].onomy_url%> + + refresh + +


    + <% } %> +

    Terms

    + + + Create term + +
    <% if (vocabularies[i].term_set.length > 0) { %> From 42237847db065cb737404f22b75c34f7c5b90ae5 Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Fri, 30 Jan 2015 19:05:51 -0500 Subject: [PATCH 53/58] Remove Hard-Coded content_type & course_id. --- media/js/app/taxonomy/taxonomy.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 80092ad96..9d6b3ed49 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -204,8 +204,8 @@ var v = new Vocabulary({ 'display_name': display_name, - 'content_type_id': jQuery(parent).find('input[name="content_type_id"]').attr("value"), - 'object_id': jQuery(parent).find('input[name="object_id"]').attr("value"), + 'content_type_id': this.context.content_type_id, + 'object_id': this.context.course_id, 'term_set': undefined, 'onomy_url': "" }); @@ -498,7 +498,11 @@ }); if (search === undefined) { //create the Vocabulary - var temp = {'display_name': pL, 'term_set': [], 'self': undefined}; + var temp = {'display_name': pL, + 'term_set': [], + 'content_type_id': self.context.content_type_id, + 'object_id': self.context.course_id, + 'self': undefined}; parents.push(temp); parents[parents.indexOf(temp)].term_set.push({'display_name': display}); } else { @@ -517,8 +521,8 @@ var tempV = new Vocabulary({ 'display_name': parents[j].display_name, - 'content_type_id': 14, - 'object_id': 1, + 'content_type_id': self.context.content_type_id, + 'object_id': self.context.course_id, 'term_set': undefined, 'onomy_url': onomyURL }); From 1221c27c2a944281670a73dd113b60c62abf9706 Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Sat, 31 Jan 2015 11:28:37 -0500 Subject: [PATCH 54/58] Refresh From Parent Only The refresh feature does something a bit wonky when refreshing from a child. For the moment, only allow refreshes from the parent level. "child" onomies are set to a "child" onomy_url to differentiate them. --- media/img/onomy_logo.png | Bin 0 -> 1273 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 media/img/onomy_logo.png diff --git a/media/img/onomy_logo.png b/media/img/onomy_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..362d3b3320b1cd3dae85457af2bf195d0192f56e GIT binary patch literal 1273 zcmVu+7Eq01RZ(sovK19x~}vJ&?|%Q%8Y2fh&Pd>-=yaR}I_+ zRAths4(oiBNvGxkSBpW_Km*X%+TPQfNvDYiMyIy>@N(~*8+Qjd})^Tr2$WGLVl*vst}j~ zxFE87fa?PtCj*%dpe5k#2P!1D_5y=Ot6Pj#jli$g`EPP|L>?Uk%H!k>=&y0)BkO#O z-SR+gD)48PWImJB2^}HN%L2Q#z-yDKTGs$y0F~m@eZVH`{OmZ)z!IQMs!jwfvCg*{ zt-b}e0&hzJ`5AZs_zZXpNXZ`HQ{WTe4eR`f(P{vA2-ppb0yBXw`4|@h{r^?5{^5ui zyHON=EznqLoo_K(?E-EGTBNeK4gR$vaWANUej1TV|UyL0A9mwf5L#CeJIr!Fz1 zQXtkD=Zo~A#ll}Kb*I$x0YWY4kR7@$^u#2V$t>V04v@R)V}D3Af}7x=s&Z+A$w+5v2rneW_0 zWFiqutfyP&2gZtO(DX`Wh>U<0!0Xoes`Kv1WZ@X_u9jclUSKuwrgeVH|Jo2IIsP=u jZ}AZDv~_+ Date: Sat, 31 Jan 2015 11:29:39 -0500 Subject: [PATCH 55/58] Refresh From Parent Only The refresh feature does something a bit wonky when refreshing from a child. For the moment, only allow refreshes from the parent level. "child" onomies are set to a "child" onomy_url to differentiate them. --- media/css/mediathread.css | 16 +++++-- media/js/app/taxonomy/taxonomy.js | 20 ++++---- mediathread/templates/taxonomy/taxonomy.html | 48 +++++++++++++------- 3 files changed, 54 insertions(+), 30 deletions(-) diff --git a/media/css/mediathread.css b/media/css/mediathread.css index a6bf8a88d..efe7530db 100644 --- a/media/css/mediathread.css +++ b/media/css/mediathread.css @@ -4537,6 +4537,10 @@ div#taxonomy input[type="text"].name { width: 60%; } +div#taxonomy input[name='onomy_url'] { + width: 87%; +} + div#taxonomy a.icon-link { float: right; margin: .5em 0; @@ -4636,16 +4640,20 @@ div#taxonomy .ui-tabs-vertical .ui-tabs-panel { width: 65%; } -div#taxonomy .import-vocabulary-open { - font-size: .75em; +div#taxonomy .btn-primary.onomy { + font-size: .65em; float: right; + margin-right: 5px; +} + +div#taxonomy .btn-primary.onomy img { + vertical-align: middle; } div#taxonomy .vocabulary-import { margin: 0 -1px .4em 0; outline: 0; - height: 40px; - padding: 10px; + padding: 10px 20px; background-color: #ededed; border-radius: 5px; } diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 9d6b3ed49..8e3dca355 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -119,15 +119,16 @@ 'click a.edit-term-close': 'hideEditTerm', 'click a.delete-term': 'deleteTerm', 'click a.import-vocabulary-submit': 'createOnomyVocabulary', - 'click a.refresh-button-submit': 'refreshOnomy' + 'click a.refresh-onomy': 'refreshOnomy', + 'click a.edit-onomy-urls': 'createOnomyVocabulary' }, initialize: function(options) { _.bindAll(this, "render", "createVocabulary", "updateVocabulary", "deleteVocabulary", "createTerm", "keypressTermName", "updateTerm", "deleteTerm", - "createOnomyVocabulary", "refreshOnomy", "getTheOnomy", - "activateTab", + "createOnomyVocabulary", "refreshOnomy", + "getTheOnomy", "activateTab", "toggleCreateVocabulary", "toggleImportVocabulary"); this.context = options; @@ -144,6 +145,7 @@ activateTab: function(evt, ui) { jQuery(ui.oldTab).find("div.vocabulary-edit, div.vocabulary-create").hide(); jQuery(ui.oldTab).find("div.vocabulary-display").show(); + jQuery(this.el).find(".vocabulary-import").hide(); var vid = jQuery(ui.newTab).data("id"); this.selected = this.collection.getByDataId(vid); }, @@ -439,7 +441,7 @@ }, createOnomyVocabulary: function(evt) { evt.preventDefault(); - var elt = jQuery(this.el).find('input[name="onomy_url"]'); + var elt = jQuery(evt.currentTarget).prevAll("input[name='onomy_url']")[0]; var onomyUrl = jQuery(elt).val().trim(); if (onomyUrl.length < 1) { @@ -450,13 +452,11 @@ var the_regex = /onomy.org\/published\/(\d+)\/json/g; var match = the_regex.exec(onomyUrl); if (match.length < 0) { - //display error message + // display error message showMessage("Please enter a valid Onomy JSON Url", undefined, "Error"); return; } - // save the onomyUrl to the selected vocabulary - this.getTheOnomy(onomyUrl); }, refreshOnomy: function(evt) { @@ -497,11 +497,12 @@ } }); if (search === undefined) { - //create the Vocabulary + // create the Vocabulary var temp = {'display_name': pL, 'term_set': [], 'content_type_id': self.context.content_type_id, 'object_id': self.context.course_id, + 'onomy_url': 'child', 'self': undefined}; parents.push(temp); parents[parents.indexOf(temp)].term_set.push({'display_name': display}); @@ -524,7 +525,7 @@ 'content_type_id': self.context.content_type_id, 'object_id': self.context.course_id, 'term_set': undefined, - 'onomy_url': onomyURL + 'onomy_url': 'child' }); tempV._save({}, { @@ -553,7 +554,6 @@ var url = urlcsv.split(','); if (!(_.contains(url, onomyURL))) { url.push(onomyURL); - model_search.save({'onomy_url': url.toString()}); } for (var z = 0; z < parents[parents.indexOf(self.findUtil(parents, model_search.attributes['display_name'])[0])].term_set.length; z++) { diff --git a/mediathread/templates/taxonomy/taxonomy.html b/mediathread/templates/taxonomy/taxonomy.html index dde30be8f..245e431f8 100644 --- a/mediathread/templates/taxonomy/taxonomy.html +++ b/mediathread/templates/taxonomy/taxonomy.html @@ -13,7 +13,13 @@
  • - <%=vocabularies[i].display_name%> + + <% if (vocabularies[i].onomy_url.length > 0) { %> +   + <% } %> + + <%=vocabularies[i].display_name%> +
    @@ -63,15 +69,32 @@ <% } %> <% for (var i=0; i < vocabularies.length; i++) { %> -
    +

    <%=vocabularies[i].display_name%> Concept - <% if (vocabularies[i].onomy_url.length < 1) { %>Import from Onomy<% } %> + <% if (vocabularies[i].onomy_url.length < 1) { %> + + Import + + <% } else if (vocabularies[i].onomy_url.length > 0 && vocabularies[i].onomy_url !== 'child') { %> + + Edit + + + Refresh + + + <% } %>

    - <% if (vocabularies[i].onomy_url.length < 1) { %> +
    - <% } %> -
    - <% if (vocabularies[i].onomy_url.length > 0) { %> - Onomy Url: - <%=vocabularies[i].onomy_url%> - - refresh - -


    - <% } %> +
    +

    Terms

    From adea9ad10db091cac2f04046d6967f8306b5b594 Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Sat, 31 Jan 2015 13:39:07 -0500 Subject: [PATCH 56/58] BugFix: Resolving issue where comma was prepended to the parent onomy url Multiple Urls: Accept multiple urls separated by commas BugFix: the Backbone override changed the signature of error handlers. Patching. --- media/js/app/taxonomy/taxonomy.js | 132 ++++++++++++++++-------------- 1 file changed, 71 insertions(+), 61 deletions(-) diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index 8e3dca355..955788bf5 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -23,6 +23,12 @@ getByDataId: function(id) { var internalId = this.urlRoot + id + '/'; return this.get(internalId); + }, + getByDisplayName: function(name) { + var filtered = this.filter(function(term) { + return term.get("display_name") === name; + }); + return filtered; } }); @@ -40,6 +46,20 @@ var json = _(this.attributes).clone(); json.term_set = this.get('term_set').toTemplate(); return json; + }, + getOnomyUrls: function() { + // the onomy url field was conceived as a comma-delimited list + // of urls. reconstitute as an array here for easier searching + // and adding new urls + var urls = []; + var str = this.get('onomy_url'); + if (str.length > 0) { + urls = str.split(','); + } + return urls; + }, + hasTerm: function(termName) { + return this.get('term_set').getByDisplayName(termName).length > 0; } }); @@ -353,7 +373,8 @@ self.selected.get('term_set').add(t); self.render(); }, - error: function(model, response) { + error: function(args) { + var response = args[1]; var responseText = jQuery.parseJSON(response.responseText); showMessage(responseText.term.error_message, undefined, "Error"); } @@ -384,7 +405,8 @@ success: function() { self.render(); }, - error: function(model, response) { + error: function(args) { + var response = args[1]; var responseText = jQuery.parseJSON(response.responseText); showMessage(responseText.term.error_message, undefined, "Error"); } @@ -443,31 +465,33 @@ evt.preventDefault(); var elt = jQuery(evt.currentTarget).prevAll("input[name='onomy_url']")[0]; - var onomyUrl = jQuery(elt).val().trim(); - if (onomyUrl.length < 1) { - showMessage("Please enter a valid Onomy JSON url.", undefined, "Error"); - return; - } + var value = jQuery(elt).val().trim(); - var the_regex = /onomy.org\/published\/(\d+)\/json/g; - var match = the_regex.exec(onomyUrl); - if (match.length < 0) { - // display error message - showMessage("Please enter a valid Onomy JSON Url", undefined, "Error"); - return; + // split the url. + var urls = value.split(','); + for (var i= 0; i < urls.length; i++) { + if (urls[i].length < 1) { + showMessage("Please enter a valid Onomy JSON url.", undefined, "Error"); + return; + } + + var the_regex = /onomy.org\/published\/(\d+)\/json/g; + var match = the_regex.exec(urls[i]); + if (match.length < 0) { + // display error message + showMessage(urls[i] + " is not valid. Please enter an Onomy JSON Url.", undefined, "Error"); + return; + } } - this.getTheOnomy(onomyUrl); + for (var i= 0; i < urls.length; i++) { + this.getTheOnomy(urls[i], this.selected); + } }, refreshOnomy: function(evt) { - var urlArray = _.map(this.collection.models, function(model) { - return model.attributes.onomy_url - }); - for (var i = 0; i < urlArray.length; i++) { - var address = urlArray[i].toString(); - if (!address == "") { - this.getTheOnomy(address); - } + var urls = this.selected.getOnomyUrls(); + for (var i = 0; i < urls.length; i++) { + this.getTheOnomy(urls[i], this.selected); } }, findUtil: function(array, thing){ @@ -475,9 +499,8 @@ return item.display_name == thing; }); }, - getTheOnomy: function(onomyURL) { + getTheOnomy: function(onomyURL, selectedVocabulary) { var self = this; - var vocabulary_id = self.selected.get('id'); jQuery.get(onomyURL, function(data) { var x = JSON.parse(data); @@ -530,8 +553,7 @@ tempV._save({}, { success: function(it) { - self.selected = it; - parents[parents.indexOf(self.findUtil(parents, it.attributes['display_name'])[0])].self = self.selected; + parents[parents.indexOf(self.findUtil(parents, it.attributes['display_name'])[0])].self = it; self.collection.add(it); for (var z = 0; z < parents[parents.indexOf(self.findUtil(parents, it.attributes['display_name'])[0])].term_set.length; z++) { var tempT = new Term({ @@ -548,14 +570,6 @@ } }); } else { - //we do find the model. we just add the term to it. - self.selected = model_search; - var urlcsv = self.selected.get('onomy_url'); - var url = urlcsv.split(','); - if (!(_.contains(url, onomyURL))) { - url.push(onomyURL); - } - for (var z = 0; z < parents[parents.indexOf(self.findUtil(parents, model_search.attributes['display_name'])[0])].term_set.length; z++) { var tempT = new Term({ 'display_name': parents[parents.indexOf(self.findUtil(parents, model_search.attributes['display_name'])[0])].term_set[z].display_name, @@ -563,7 +577,7 @@ }); tempT._save({}, { success: function(itT) { - self.selected.get('term_set').add(itT); + model_search.get('term_set').add(itT); self.render(); } }); @@ -571,35 +585,31 @@ } } } - } else { - if (display === undefined || display.length < 1) { - continue; - } - var id = self.selected.attributes.id; - var v = self.collection.getByDataId(id); - var urlcsv = self.selected.get('onomy_url'); - var url = urlcsv.split(','); - + } else if (display !== undefined && display.length > 0) { + var urls = selectedVocabulary.getOnomyUrls(); + //if this vocabulary doesn't contain the url we punched in - if (!(_.contains(url, onomyURL))) { + if (!_.contains(urls, onomyURL)) { //add it to our array we made and save it in the vocab - url.push(onomyURL); - v.save({'onomy_url': url.toString()}); + urls.push(onomyURL); + selectedVocabulary.save({'onomy_url': urls.toString()}); + } + //we create our term if it doesn't already exist + if (!selectedVocabulary.hasTerm(display)) { + var t = new Term({ + 'display_name': display, + 'vocabulary_id': selectedVocabulary.get('id') + }); + //then save it with our overriden queued save + t._save({}, { + wait: true, + success: function(newTerm) { + //add it to the term set + selectedVocabulary.get('term_set').add(newTerm); + self.render(); + } + }); } - //we create our term - var t = new Term({ - 'display_name': display, - 'vocabulary_id': vocabulary_id - }); - //then save it with our overriden queued save - t._save({}, { - wait: true, - success: function(it) { - //add it to our term - self.selected.get('term_set').add(it); - self.render(); - } - }); } } }); From 83829c887a26c179bda7afa3e7aecfa43534540c Mon Sep 17 00:00:00 2001 From: Susan Dreher Date: Sat, 31 Jan 2015 14:12:23 -0500 Subject: [PATCH 57/58] migrating the selenium/test db --- scripts/lettuce_base.db | Bin 479232 -> 479232 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/scripts/lettuce_base.db b/scripts/lettuce_base.db index f33efdcef4249781303998ef2eb36e92e1da4dc0..a7f83b127c58dccfe221bda95d7fa3277189f1ed 100644 GIT binary patch delta 890 zcmY*XQEXFH7{34QxqsW;wzn``x9Q+IGuO=(Zz;1$mAGz{LAD@cHNix=yQ6h$=-QjM z6RI)kbqov!vTiGX*~VNJ0?`L#Bil7RC?-7nLQG72&?gh$Y?PP~qTaG6lJh0sf4+16 z@8tVWX|1ia);8C=8w8HyrY-0ez`}aD=Kv4F#@H7UrmcGL)J#pfRFlB0Nqpm&t;K1O zUFUX#p`@`Yha9FuiEAnQoX!HKw zWj$vXbaT)R_xUY0E39DNKIGWv_}BS!!=`xIrMmmvPdppar?cX~;AivK1oWut>;T)zN3(kmm)^*y=yTA4vm+}p>wu^j#{l?6{Jp{`rle&xn>brrhO^IawOfIDtQrXPTsV-x$ zVWY|-t}?sM_0Z)d^oxeWK)Wv%0;7Y(Di$04?ezh-cNvX_up_)#7h32F;fpXzeN}9) za|b;lY^BmYeh;Tm);AieHyL>r=gD`5hx?-gLux!W8h@_H4V;Wc<1slN?H`WG&q=cXlx($O+cJn= oIJ>pW{T}V@57{||hcHZY~XCG&NZWzuvsi|057G6lCsc2>vh(S38c`RWHUgDjJIX1^sukNj>qIhGc~X^}{2k0yQba|Ih_v?Rlj3^zgt`DW!gRgIt)Hr1~+vs%BdO)S|ty7vz&?Hnl#*e<&I z+}*amZc9bTQqLPs9U-A+)Fi=#?-oFbw->-jn(#yttl5H9V=lJ@{7$FM=Lop)rGqnJ zrGuLiW$Nu6bDy`{IfNtyrMMXZBeo@>9E(z5&6TY_hu`T9{P|8E>M=&_+}uA9a`pcI zK}a&hg!+MxTVAauoL=IpDXF%y>kPZDnBH}ilD?{fot&DhDm(u#w8^X9MqMTF`z8~H~KCR)V6?fR5Y$sV_T8SspP$YGv zpqJoM1kR%^33=Ig4k4R_Cum=Qd+1KUHftKin^8z{p$j8Cew~9DS`$z}%^h9s?YQQo z#dtO$TkGcFDMn}Eks|*xAD@MoO6BJb%{3%sMOzGpm2&D{hsSA=2I4>oUYUXKigpi= zRWW%lg)@+Y(md#KbQ*N{!a>WjZF6udNCZ4S54(15>I8^fz&RAa-BCD Date: Sat, 31 Jan 2015 14:37:02 -0500 Subject: [PATCH 58/58] Edit Item Form Scroll -- in case of many taxonomies. --- media/css/mediathread.css | 8 ++++++++ media/js/app/assetmgr/assetpanel.js | 1 + 2 files changed, 9 insertions(+) diff --git a/media/css/mediathread.css b/media/css/mediathread.css index efe7530db..41a6bfb01 100644 --- a/media/css/mediathread.css +++ b/media/css/mediathread.css @@ -4017,6 +4017,14 @@ div#asset-view-details { height: 100%; } +form#edit-global-annotation-form { + overflow-y: auto; + overflow-x: hidden; + margin-right: -10px; + padding-right: 20px; + padding-bottom: 10px; +} + form#edit-annotation-form { overflow-y: auto; overflow-x: hidden; diff --git a/media/js/app/assetmgr/assetpanel.js b/media/js/app/assetmgr/assetpanel.js index 7e975eb80..0ad69c479 100644 --- a/media/js/app/assetmgr/assetpanel.js +++ b/media/js/app/assetmgr/assetpanel.js @@ -245,6 +245,7 @@ AssetPanelHandler.prototype.resize = function () { jQuery(self.el).find('.ui-tabs-panel').css('height', (visible - 10) + "px"); jQuery(self.el).find('form#edit-annotation-form').css('height', (visible - 56) + "px"); + jQuery(self.el).find('form#edit-global-annotation-form').css('height', (visible - 66) + "px"); visible -= jQuery("div#asset-global-annotation").outerHeight(); jQuery(self.el).find('div#annotations-organized').css('height', (visible - 5) + "px");
  • {% with "help_homepage_instructor_column" as help_id %} - {% with help_homepage_instructor_column as default_help_state %} {% include "help/help_contentarea.html" %}

    - From Your Instructor + {{information_title}}
    {% include "help/help_button.html" %}

    - - + {% if faculty_feed %}

    - Information + {{information_title}}

    -
      +
        {% for project in faculty_feed %}
      • {% if is_faculty %} @@ -186,14 +186,13 @@

        Discussions

    - {% endwith %}{% endwith %} + {% endwith %}
    {% with "help_homepage_classwork_column" as help_id %} - {% with help_homepage_classwork_column as default_help_state %} {% include "help/help_contentarea.html" %}
    @@ -229,13 +228,12 @@

    - {% endwith %}{% endwith %} + {% endwith %}
    {% with "help_homepage_explore_column" as help_id %} - {% with help_homepage_explore_column as default_help_state %} {% include "help/help_contentarea.html" %}
    @@ -371,7 +369,7 @@

    Recommended Sources

    {% endif %}
    - {% endwith %}{% endwith %} + {% endwith %}