diff --git a/media/css/mediathread.css b/media/css/mediathread.css index 45af97840..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; @@ -4518,7 +4526,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; @@ -4537,6 +4545,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,6 +4648,24 @@ div#taxonomy .ui-tabs-vertical .ui-tabs-panel { width: 65%; } +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; + padding: 10px 20px; + background-color: #ededed; + border-radius: 5px; +} + div#taxonomy div.vocabulary-display a.create-vocabulary-open { width: 94%; height: 24px; @@ -4651,6 +4681,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 +4694,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,6 +4720,24 @@ div#taxonomy div.terms a.create-term-submit { padding: 4px; } +div#taxonomy div.terms a.refresh-button-submit { + padding: 5px 0 5px 5px; + float: right; +} + +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; } @@ -4703,7 +4754,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/img/icons/refreshIcon.gif b/media/img/icons/refreshIcon.gif new file mode 100644 index 000000000..0c415e9e2 Binary files /dev/null and b/media/img/icons/refreshIcon.gif differ diff --git a/media/img/onomy_logo.png b/media/img/onomy_logo.png new file mode 100644 index 000000000..362d3b332 Binary files /dev/null and b/media/img/onomy_logo.png differ 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"); diff --git a/media/js/app/taxonomy/taxonomy.js b/media/js/app/taxonomy/taxonomy.js index f89e1a4cc..955788bf5 100644 --- a/media/js/app/taxonomy/taxonomy.js +++ b/media/js/app/taxonomy/taxonomy.js @@ -1,46 +1,100 @@ -(function (jQuery) { - +(function(jQuery) { + Term = Backbone.Model.extend({ urlRoot: '/api/term/', toTemplate: function() { return _(this.attributes).clone(); - } - }); + } + }); var TermList = Backbone.Collection.extend({ urlRoot: '/api/term/', model: Term, comparator: function(obj) { return obj.get("display_name"); - }, + }, 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); + }, + getByDisplayName: function(name) { + var filtered = this.filter(function(term) { + return term.get("display_name") === name; + }); + return filtered; } }); - + Vocabulary = Backbone.Model.extend({ urlRoot: '/api/vocabulary/', parse: function(response) { + if (response) { response.term_set = new TermList(response.term_set); } + return response; - }, + }, toTemplate: function() { 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; + } }); - + + 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, @@ -52,22 +106,24 @@ }, 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); } }); - + 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.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', @@ -80,34 +136,36 @@ '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.import-vocabulary-submit': 'createOnomyVocabulary', + 'click a.refresh-onomy': 'refreshOnomy', + 'click a.edit-onomy-urls': 'createOnomyVocabulary' }, initialize: function(options) { _.bindAll(this, - "render", - "createVocabulary", - "updateVocabulary", - "deleteVocabulary", - "createTerm", - "keypressTermName", - "updateTerm", - "deleteTerm", - "activateTab"); - + "render", + "createVocabulary", "updateVocabulary", "deleteVocabulary", + "createTerm", "keypressTermName", "updateTerm", "deleteTerm", + "createOnomyVocabulary", "refreshOnomy", + "getTheOnomy", "activateTab", + "toggleCreateVocabulary", "toggleImportVocabulary"); + 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) { 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); }, @@ -115,7 +173,7 @@ 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,49 +181,55 @@ // 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); - } + } + }, + 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(); - return false; + 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; + return false; }, 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"), - 'object_id': jQuery(parent).find('input[name="object_id"]').attr("value"), - 'term_set': undefined + 'content_type_id': this.context.content_type_id, + 'object_id': this.context.course_id, + 'term_set': undefined, + 'onomy_url': "" }); v.save({}, { success: function() { @@ -177,25 +241,26 @@ showMessage(responseText.vocabulary.error_message, undefined, "Error"); } }); + return false; }, 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') { @@ -213,18 +278,18 @@ }, 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, @@ -234,7 +299,7 @@ }, buttons: { "Cancel": function() { - jQuery(this).dialog("close"); + jQuery(this).dialog("close"); }, "OK": function() { jQuery(this).dialog("close"); @@ -262,42 +327,43 @@ 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; - }, + 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; - }, + return false; + }, keypressTermName: function(evt) { var self = this; if (evt.which == 13) { - evt.preventDefault(); + evt.preventDefault(); jQuery(evt.currentTarget).next().click(); } }, 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 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') @@ -307,12 +373,13 @@ 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"); } }); - return false; + return false; }, updateTerm: function(evt) { evt.preventDefault(); @@ -322,45 +389,45 @@ 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() { 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"); } }); } - return false; + return false; }, 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, @@ -370,7 +437,7 @@ }, buttons: { "Cancel": function() { - jQuery(this).dialog("close"); + jQuery(this).dialog("close"); }, "OK": function() { jQuery(this).dialog("close"); @@ -393,7 +460,161 @@ jQuery(evt.currentTarget).addClass("default"); jQuery(evt.currentTarget).attr("value", "Type new term name here"); } + }, + createOnomyVocabulary: function(evt) { + evt.preventDefault(); + var elt = jQuery(evt.currentTarget).prevAll("input[name='onomy_url']")[0]; + + var value = jQuery(elt).val().trim(); + + // 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; + } + } + + for (var i= 0; i < urls.length; i++) { + this.getTheOnomy(urls[i], this.selected); + } + }, + refreshOnomy: function(evt) { + var urls = this.selected.getOnomyUrls(); + for (var i = 0; i < urls.length; i++) { + this.getTheOnomy(urls[i], this.selected); + } + }, + findUtil: function(array, thing){ + return jQuery.grep(array, function(item){ + return item.display_name == thing; + }); + }, + getTheOnomy: function(onomyURL, selectedVocabulary) { + var self = this; + + 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': [], + '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}); + } 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': self.context.content_type_id, + 'object_id': self.context.course_id, + 'term_set': undefined, + 'onomy_url': 'child' + }); + + tempV._save({}, { + success: function(it) { + 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({ + '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 { + 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) { + model_search.get('term_set').add(itT); + self.render(); + } + }); + } + } + } + } + } else if (display !== undefined && display.length > 0) { + var urls = selectedVocabulary.getOnomyUrls(); + + //if this vocabulary doesn't contain the url we punched in + if (!_.contains(urls, onomyURL)) { + //add it to our array we made and save it in the vocab + 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(); + } + }); + } + } + } + }); } }); - -}(jQuery)); \ No newline at end of file +}(jQuery)); + + diff --git a/mediathread/assetmgr/urls.py b/mediathread/assetmgr/urls.py index 04c776ee0..4775bcaae 100644 --- a/mediathread/assetmgr/urls.py +++ b/mediathread/assetmgr/urls.py @@ -43,6 +43,8 @@ 'final_cut_pro_xml', 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 f2a233c09..1659de8b7 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 @@ -448,6 +449,128 @@ def final_cut_pro_xml(request, asset_id): status=503) +def mep_dump(request): + user = request.user + user_id = user.id + assets = Asset.objects.filter(author_id=user_id) + ar = AssetResource(include_annotations=True) + ar.Meta.excludes = ['added', 'modified', 'course', 'active'] + lst = [] + + 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) + 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] + 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/"} + # 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'] + 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('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")) + + return HttpResponse(lst) + + class AssetReferenceView(LoggedInMixin, RestrictedMaterialsMixin, AjaxRequiredMixin, JSONResponseMixin, View): diff --git a/mediathread/settings_shared.py b/mediathread/settings_shared.py index cedd266d2..595baf580 100644 --- a/mediathread/settings_shared.py +++ b/mediathread/settings_shared.py @@ -23,7 +23,7 @@ DATABASES = { 'default': { 'ENGINE': 'django.db.backends.postgresql_psycopg2', - 'NAME': 'mediathread', + 'NAME': '', 'HOST': '', 'PORT': '', 'USER': '', 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/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..6491fc598 --- /dev/null +++ b/mediathread/taxonomy/migrations/0004_auto__chg_field_term_display_name__add_field_vocabulary_onomy_url.py @@ -0,0 +1,66 @@ +# flake8: noqa +# -*- 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=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), + 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': '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'}), + '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'] diff --git a/mediathread/taxonomy/models.py b/mediathread/taxonomy/models.py index 3071a12c3..81bb9f339 100644 --- a/mediathread/taxonomy/models.py +++ b/mediathread/taxonomy/models.py @@ -21,12 +21,11 @@ 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() content_object = generic.GenericForeignKey('content_type', 'object_id') - objects = GenericRelationshipManager() class Meta: 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) diff --git a/mediathread/templates/djangosherd/annotator_resources_css.html b/mediathread/templates/djangosherd/annotator_resources_css.html index c14a64aee..bb51348e2 100644 --- a/mediathread/templates/djangosherd/annotator_resources_css.html +++ b/mediathread/templates/djangosherd/annotator_resources_css.html @@ -25,4 +25,4 @@ - \ No newline at end of file + diff --git a/mediathread/templates/taxonomy/taxonomy.html b/mediathread/templates/taxonomy/taxonomy.html index 102427734..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%> +
  • <% } %> - <% if (vocabularies.length < 3) { %> + <% if (vocabularies.length < 100) { %>
  • @@ -63,14 +69,50 @@ <% } %> <% for (var i=0; i < vocabularies.length; i++) { %> -
    -

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

    -

    Terms

    -
    - - - Create term +
    +

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

    +
    + +
    +
    +

    Terms

    + + + Create term + +
    <% if (vocabularies[i].term_set.length > 0) { %> @@ -109,7 +151,7 @@
    <%= vocabularies[i].term_set[j].display_name %>
    <% } %> - <% if (vocabularies.length < 3) { %> + <% if (vocabularies.length < 100) { %>
    <% } %>
    diff --git a/scripts/lettuce_base.db b/scripts/lettuce_base.db index f33efdcef..a7f83b127 100644 Binary files a/scripts/lettuce_base.db and b/scripts/lettuce_base.db differ