# HG changeset patch # User durandn # Date 1428429784 -7200 # Node ID 746d486f7c4235cdc49a2aad73d2fdfaecd1513c # Parent 018094c4045374a62250aa70da2c945512a9fdd1 Added support for custom properties per repository + added doc in the readme for this (config.py PROPERTY_LIST entry has changed a bit) diff -r 018094c40453 -r 746d486f7c42 Readme.md --- a/Readme.md Tue Mar 31 15:32:56 2015 +0200 +++ b/Readme.md Tue Apr 07 20:03:04 2015 +0200 @@ -48,23 +48,32 @@ ## Additional/Advanced informations ## -** Changing the property list : ** If you want to change the property list available to users editing/creating categories, you have to edit the following entry in config.py +** Changing the property list : ** If you want to change the default property list available to users editing/creating categories, you have to edit the following entry in config.py * PROPERTY_LIST : My list of properties ... The list of property has a fixed structure. The property list is a dict of python dicts, each following the fixed structure: - "propertyKey" : { + "propertyURI" : { "descriptive_label_fr" : "mylabelfr", "descriptive_label_en" : "mylabelen", "object_type": "my-object-type", "rdflib_class": some-RDFLib-concept, - "object_rdflib_class": some-RDFLib-class, + "usable_in_editor: boolean so the property appears or not in the editor } +* "propertyURI" is the complete URI of the property, for a rdflib property that will be the string you get when calling NAMESPACE.property.toPython() * "object_type" : "my-object-type", -> either "literal", "uriref-category" or "uriref-link" at the moment -* "rdflib_class" : some-RDFLib-concept, -> a rdflib class of some sort with its namespace, examples: SKOS.related or RDF.Type representing the predicate -* "object_rdflib_class" : some-RDFLib-class: -> a rdflib class representing the object, either Literal or URIRef +* "usable_in_editor": if true, the property will appear in the editor and users will be able to apply it to categories, if false they won't be able to. +* "rdflib_class" : some-RDFLib-concept, -> a rdflib namespaced property with its namespace, examples: SKOS.related or RDF.Type representing the predicate + +** Custom property list ** + +To setup a custom set of property you need to add a properties/properties.json file in the repository that has 2 values: +* namespace_title is the string that defines the namespace of your custom properties for this repository (not really used right now but will be useful in the future) +* property_list is a dict that follows the exact same format as the config.py property list, of course in json format, so the propertyURI that is used as a key must be written explicitely + +If you make the property list evolve, don't forget to keep the old properties you don't want your users to use anymore to ensure backward compatibility. You just need to set the "usable_in_editor" key to false in the property you don't want your users to have access to. Note this won't affect existing categories with this property, but only ensure that they will be readable. Then it's up to the users to "clean up" if a deprecated property is still used. ## Testing Protocol for CatEdit (development) ## diff -r 018094c40453 -r 746d486f7c42 src/catedit/config.py.tmpl --- a/src/catedit/config.py.tmpl Tue Mar 31 15:32:56 2015 +0200 +++ b/src/catedit/config.py.tmpl Tue Apr 07 20:03:04 2015 +0200 @@ -72,34 +72,34 @@ "GITHUB_CLIENT_ID" : "github-client-placeholder", "GITHUB_CLIENT_SECRET" : "github-secret-placeholder", } - + PROPERTY_LIST = { - "subClassOf": { + RDFS.subClassOf.toPython(): { "descriptive_label_fr": "Sous-classe de", "descriptive_label_en": "Subclass of", "object_type": "uriref-category", + "usable_in_editor": True, "rdflib_class": RDFS.subClassOf, - "object_rdflib_class": URIRef, }, - "comment": { + RDFS.comment.toPython(): { "descriptive_label_fr": "Commentaire", "descriptive_label_en": "Comment", "object_type": "literal", + "usable_in_editor": True, "rdflib_class": RDFS.comment, - "object_rdflib_class": Literal, }, - "resource": { + RDFS.Resource.toPython(): { "descriptive_label_fr": "Ressource", "descriptive_label_en": "Resource", "object_type": "uriref-link", + "usable_in_editor": True, "rdflib_class": RDFS.Resource, - "object_rdflib_class": URIRef, }, - "related": { + SKOS.related.toPython(): { "descriptive_label_fr": "En relation avec", "descriptive_label_en": "Related to", "object_type": "uriref-category", + "usable_in_editor": True, "rdflib_class": SKOS.related, - "object_rdflib_class": URIRef, } } diff -r 018094c40453 -r 746d486f7c42 src/catedit/models.py --- a/src/catedit/models.py Tue Mar 31 15:32:56 2015 +0200 +++ b/src/catedit/models.py Tue Apr 07 20:03:04 2015 +0200 @@ -5,13 +5,15 @@ * helper classes to manage category life cycle """ +from catedit import app +from io import StringIO +import logging +from uuid import uuid4 + from rdflib import Graph, RDF, RDFS, Literal, URIRef from rdflib.compare import to_isomorphic, graph_diff -from uuid import uuid4 -from io import StringIO from slugify import slugify -import logging -from catedit import app + logger = logging.getLogger(__name__) @@ -38,8 +40,7 @@ # cat_id = .hex - Alternate method of generating ids cat_id = slugify(label)+"_"+str(uuid4())[:8] self.cat_graph = Graph() - self.this_category = URIRef(app.config["CATEGORY_NAMESPACE"] + - cat_id) + self.this_category = app.config["CATEGORY_NAMESPACE"][cat_id] self.cat_graph.add((self.this_category, RDF.ID, Literal(cat_id))) if label: @@ -53,27 +54,12 @@ if other_properties: for (predicate, obj) in other_properties: - rdf_obj = "" - if (app.config["PROPERTY_LIST"] - [predicate] - ["object_type"] == "uriref-category"): - rdf_obj = app.config["CATEGORY_NAMESPACE"] + obj - else: - rdf_obj = obj - self.cat_graph.add((self.this_category, - app.config["PROPERTY_LIST"] - [predicate] - ["rdflib_class"], - app.config["PROPERTY_LIST"] - [predicate] - ["object_rdflib_class"] - (rdf_obj))) + self.cat_graph.add((self.this_category, predicate, obj)) else: self.cat_graph = graph - # Warning: not foolproof - self.this_category = next(self.cat_graph - .subjects(predicate=RDF.ID)) + # Warning: not foolproof, if loading a Graph with multiple IDs (should not happen) + self.this_category = next(self.cat_graph.subjects(predicate=RDF.ID)) @property def label(self): @@ -110,16 +96,25 @@ """ Returns category property list """ - property_list = [] - for key in app.config["PROPERTY_LIST"]: - for obj in self.cat_graph \ - .objects(subject=self.this_category, - predicate=app.config["PROPERTY_LIST"] - [key] - ["rdflib_class"]): - property_list.append((key, obj.toPython())) - return property_list - + print(str( + [ + predicate_object_tuple for predicate_object_tuple in self.cat_graph.predicate_objects() + if ( + predicate_object_tuple[0]!=RDF.ID and + predicate_object_tuple[0]!=RDFS.label and + predicate_object_tuple[0]!=RDF.Description + ) + ] + )) + return [ + predicate_object_tuple for predicate_object_tuple in self.cat_graph.predicate_objects() + if ( + predicate_object_tuple[0]!=RDF.ID and + predicate_object_tuple[0]!=RDFS.label and + predicate_object_tuple[0]!=RDF.Description + ) + ] + def edit_category(self, new_label="", new_description="", new_other_properties=None): @@ -159,28 +154,15 @@ if new_other_properties is not None and \ (new_other_properties != self.properties): - for key in app.config["PROPERTY_LIST"]: - self.cat_graph.remove((self.this_category, - app.config["PROPERTY_LIST"] - [key] - ["rdflib_class"], - None)) + for (predicate, object) in self.cat_graph.predicate_objects(): + if predicate not in [RDF.ID, RDF.Description, RDFS.label]: + self.cat_graph.remove( + (self.this_category, predicate, None) + ) for (predicate, obj) in new_other_properties: - rdf_obj = "" - if (app.config["PROPERTY_LIST"] - [predicate] - ["object_type"] == "uriref-category"): - rdf_obj = app.config["CATEGORY_NAMESPACE"] + obj - else: - rdf_obj = obj - self.cat_graph.add((self.this_category, - app.config["PROPERTY_LIST"] - [predicate] - ["rdflib_class"], - app.config["PROPERTY_LIST"] - [predicate] - ["object_rdflib_class"] - (rdf_obj))) + self.cat_graph.add( + (self.this_category, predicate, obj) + ) class CategoryManager(object): diff -r 018094c40453 -r 746d486f7c42 src/catedit/resources.py --- a/src/catedit/resources.py Tue Mar 31 15:32:56 2015 +0200 +++ b/src/catedit/resources.py Tue Apr 07 20:03:04 2015 +0200 @@ -6,14 +6,17 @@ """ -from rdflib import Graph -from flask.ext.restful import Resource, reqparse -from flask import session from catedit import app, cache, github from catedit.models import Category, CategoryManager import catedit.persistence from io import StringIO +from flask import session +from rdflib import Graph, URIRef, Literal + +from flask.ext.restful import Resource, reqparse + + logger = app.logger cat_parser = reqparse.RequestParser() @@ -135,10 +138,25 @@ cat = Category(graph=cat_graph) else: cat = cat_manager_instance.load_category(cat_id) - + + new_property_list=[] + for (predicate, obj) in cat_data["properties"]: + if session["properties"][repository][predicate]["object_type"] == "uriref-category": + new_property_list.append( + (URIRef(session["properties"][repository][predicate]["rdflib_class"]), app.config["CATEGORY_NAMESPACE"][obj]) + ) + elif session["properties"][repository][predicate]["object_type"] == "uriref-link": + new_property_list.append( + (URIRef(session["properties"][repository][predicate]["rdflib_class"]), URIRef(obj)) + ) + else: + new_property_list.append( + (URIRef(session["properties"][repository][predicate]["rdflib_class"]), Literal(obj)) + ) + cat.edit_category(new_description=cat_data["description"], new_label=cat_data["label"], - new_other_properties=cat_data["properties"]) + new_other_properties=new_property_list) session["modified_categories"][repository][cat.cat_id] = str( cat.cat_graph.serialize(format="turtle"), "utf-8" @@ -168,12 +186,29 @@ } List of predicate is available in config.py, key PROPERTY_LIST """ + property_list = [] + for (predicate, obj) in cat_data["properties"]: + if session["properties"][repository][predicate]["object_type"] == "uriref-category": + property_list.append( + (URIRef(session["properties"][repository][predicate]["rdflib_class"]), app.config["CATEGORY_NAMESPACE"][obj]) + ) + elif session["properties"][repository][predicate]["object_type"] == "uriref-link": + property_list.append( + (URIRef(session["properties"][repository][predicate]["rdflib_class"]), URIRef(obj)) + ) + else: + property_list.append( + (URIRef(session["properties"][repository][predicate]["rdflib_class"]), Literal(obj)) + ) + cat = Category( label=cat_data["label"], description=cat_data["description"], - other_properties=cat_data["properties"] + other_properties=property_list ) - + + + if cat.cat_id not in session["modified_categories"][repository].keys(): session["modified_categories"][repository][cat.cat_id] = str( cat.cat_graph.serialize(format="turtle"), "utf-8" @@ -249,21 +284,16 @@ format="turtle" ) modified_cat = Category(graph=modified_cat_graph) - if (modified_cat.cat_id != - app.config["CATEGORY_NAMESPACE"] + deleted_cat_id): + if (modified_cat.cat_id != app.config["CATEGORY_NAMESPACE"][deleted_cat_id]): new_property_list = [] for (predicate, obj) in modified_cat.properties: if not ( - app.config["PROPERTY_LIST"] - [predicate] - ["object_type"] + session["properties"] + [repository] + [predicate.toPython()] + ["object_type"] == "uriref-category" - and ( - obj == ( - app.config["CATEGORY_NAMESPACE"] - + deleted_cat_id - ) - ) + and (obj == app.config["CATEGORY_NAMESPACE"][deleted_cat_id]) ): new_property_list.append((predicate, obj)) @@ -292,15 +322,12 @@ new_property_list = [] for (predicate, obj) in cat.properties: if not ( - app.config["PROPERTY_LIST"] - [predicate] - ["object_type"] + session["properties"] + [repository] + [predicate.toPython()] + ["object_type"] == "uriref-category" - and ( - obj == - app.config["CATEGORY_NAMESPACE"] - + deleted_cat_id - ) + and (obj == app.config["CATEGORY_NAMESPACE"][deleted_cat_id]) ): new_property_list.append((predicate, obj)) diff -r 018094c40453 -r 746d486f7c42 src/catedit/settings.py --- a/src/catedit/settings.py Tue Mar 31 15:32:56 2015 +0200 +++ b/src/catedit/settings.py Tue Apr 07 20:03:04 2015 +0200 @@ -2,6 +2,8 @@ settings.py: Contains all settings that are needed but are not to be changed """ +from rdflib import Namespace + class AppSettings(object): @@ -11,5 +13,5 @@ # RDF Namespace and prefixes - Must end with # - ONTOLOGY_NAMESPACE = "http://ld.iri-research.org/ontology/categorisation#" - CATEGORY_NAMESPACE = "http://ld.iri-research.org/ontology/categorisation/category#" + BASE_NAMESPACE = "http://ld.iri-research.org/ontology/categorisation" + CATEGORY_NAMESPACE = Namespace("http://ld.iri-research.org/ontology/categorisation/category#") \ No newline at end of file diff -r 018094c40453 -r 746d486f7c42 src/catedit/templates/categories/editor.html --- a/src/catedit/templates/categories/editor.html Tue Mar 31 15:32:56 2015 +0200 +++ b/src/catedit/templates/categories/editor.html Tue Apr 07 20:03:04 2015 +0200 @@ -63,8 +63,10 @@ - {% for predicate in config["PROPERTY_LIST"] %} - + {% for predicate in session["properties"][current_repository] %} + {% if session["properties"][current_repository][predicate]["usable_in_editor"] %} + + {% endif %} {% endfor %} @@ -89,14 +91,14 @@ {% for property in form.properties %}