next version of import_rdf
authorymh <ymh.work@gmail.com>
Wed, 18 Feb 2015 01:53:34 +0100
changeset 443 27f71b0a772d
parent 442 3d54acec55d6
child 444 ca7df977504f
next version of import_rdf
.settings/org.eclipse.core.resources.prefs
src/hdabo/fixtures/initial_data.yaml.bz2
src/hdabo/fixtures/not_initial_data.yaml.bz2
src/hdabo/management/commands/import_rdf.py
src/hdabo/migrations/0004_index_tag.py
src/hdabo/migrations/0009_auto__add_field_tag_natural_key.py
src/hdabo/migrations/0010_tag_natural_key.py
src/hdabo/migrations/0011_auto__chg_field_tag_natural_key.py
src/hdabo/migrations/0012_auto__add_field_domain_natural_key__add_field_timeperiod_natural_key.py
src/hdabo/migrations/0013_domain_period_natural_key.py
src/hdabo/migrations/0014_auto__chg_field_domain_natural_key__chg_field_timeperiod_natural_key.py
src/hdabo/migrations/0015_sanitize_organisation_hda_id.py
src/hdabo/migrations/0016_auto__add_field_tagcategory_natural_key.py
src/hdabo/migrations/0017_calculate_category_natural_key.py
src/hdabo/migrations/0018_auto__chg_field_tagcategory_natural_key__add_index_tagcategory_natural.py
src/hdabo/models.py
src/hdabo/utils.py
src/hdalab/management/commands/import_hda_insee_csv.py
src/hdalab/management/commands/import_hdabo_db.py
src/hdalab/views/ajax.py
virtualenv/res/lib/lib_create_env.py
virtualenv/res/src/Unidecode-0.04.17.tar.gz
virtualenv/res/src/pytz-2014.10.tar.bz2
virtualenv/res/src/rdflib-4.2-dev.tar.gz
virtualenv/res/src/rdflib-4.2.0-dev.tar.gz
virtualenv/web/res/requirements.txt
--- a/.settings/org.eclipse.core.resources.prefs	Wed Dec 10 10:05:38 2014 +0100
+++ b/.settings/org.eclipse.core.resources.prefs	Wed Feb 18 01:53:34 2015 +0100
@@ -11,10 +11,20 @@
 encoding//src/hdabo/migrations/0001_initial.py=utf-8
 encoding//src/hdabo/migrations/0002_backport_hdabo_sf.py=utf-8
 encoding//src/hdabo/migrations/0003_update_redirection.py=utf-8
+encoding//src/hdabo/migrations/0004_index_tag.py=utf-8
 encoding//src/hdabo/migrations/0005_auto__chg_field_datasheet_organisation.py=utf-8
 encoding//src/hdabo/migrations/0006_auto__add_user__chg_field_datasheet_validator.py=utf-8
 encoding//src/hdabo/migrations/0007_auto__add_folder.py=utf-8
 encoding//src/hdabo/migrations/0008_to_dbpedia_fr.py=utf-8
+encoding//src/hdabo/migrations/0009_auto__add_field_tag_natural_key.py=utf-8
+encoding//src/hdabo/migrations/0010_tag_natural_key.py=utf-8
+encoding//src/hdabo/migrations/0011_auto__chg_field_tag_natural_key.py=utf-8
+encoding//src/hdabo/migrations/0012_auto__add_field_domain_natural_key__add_field_timeperiod_natural_key.py=utf-8
+encoding//src/hdabo/migrations/0013_domain_period_natural_key.py=utf-8
+encoding//src/hdabo/migrations/0014_auto__chg_field_domain_natural_key__chg_field_timeperiod_natural_key.py=utf-8
+encoding//src/hdabo/migrations/0015_sanitize_organisation_hda_id.py=utf-8
+encoding//src/hdabo/migrations/0016_auto__add_field_tagcategory_natural_key.py=utf-8
+encoding//src/hdabo/migrations/0017_calculate_category_natural_key.py=utf-8
 encoding//src/hdabo/models.py=utf-8
 encoding//src/hdabo/search/french_whoosh_backend.py=utf-8
 encoding//src/hdabo/tests/models.py=utf-8
Binary file src/hdabo/fixtures/initial_data.yaml.bz2 has changed
Binary file src/hdabo/fixtures/not_initial_data.yaml.bz2 has changed
--- a/src/hdabo/management/commands/import_rdf.py	Wed Dec 10 10:05:38 2014 +0100
+++ b/src/hdabo/management/commands/import_rdf.py	Wed Feb 18 01:53:34 2015 +0100
@@ -3,11 +3,11 @@
 Created on May 25, 2011
 
 
-Compilation: 
+Compilation:
 
 #Install librdf
 # install raptor2 : configure --prefix=<path to venv> + make + make install
-# install librasql : PKG_CONFIG_PATH=/Users/ymh/dev/venvs/hdalab/lib/pkgconfig RAPTOR2_LIBS="-L/Users/ymh/dev/venvs/hdalab/lib -lraptor2" RAPTOR2_CFLAGS="-I/Users/ymh/dev/venvs/hdalab/include/raptor2" RAPTOR_VERSION=2.0.15 ./configure --prefix=/Users/ymh/dev/venvs/hdalab 
+# install librasql : PKG_CONFIG_PATH=/Users/ymh/dev/venvs/hdalab/lib/pkgconfig RAPTOR2_LIBS="-L/Users/ymh/dev/venvs/hdalab/lib -lraptor2" RAPTOR2_CFLAGS="-I/Users/ymh/dev/venvs/hdalab/include/raptor2" RAPTOR_VERSION=2.0.15 ./configure --prefix=/Users/ymh/dev/venvs/hdalab
 
 
 raptor2-2.0.15
@@ -80,145 +80,215 @@
 make
 make install
 
+
+- after `import_rdf` call commands `import_hdabo_db -c` then `rebuild_index`
+
 @author: ymh
 '''
 
-# hda:Domaine -> Domain
-# 
-# hda:SousDomaine -> Domain
-# 
-# hda:Institution -> Organisation ?
-# 
-# hda:Notice -> DataSheet
-# 
-# hda:Periode -> TimePeriod
-# 
-# hda:Site -> Organisation ?
-# 
-# hda:Theme -> Domain
-# 
-# hda:Ville -> Location
-# 
-# hda:DocumentTag -> TaggedSheet
-# 
-# hda:Tag -> Tag
-# 
-# hda:Category -> TagCategory
+import base64
+import collections
+import logging
+import math
+from optparse import make_option
+import os
+import shutil
+import sys
+import tempfile
+
+import RDF
+from django.core.management.base import BaseCommand, CommandError
+from django.core.management.color import no_style
+from django.db import connections, DEFAULT_DB_ALIAS
+from django.db import models
+from django.db import transaction
+import isodate
+import pytz
+
+from hdabo.models import (Datasheet, DocumentFormat, Domain, Organisation,
+    Tag, TaggedSheet, TimePeriod, Location, TagCategory)
 
 
+logger = logging.getLogger(__name__)
+
+RDF_EXT_MAP = {
+    '.xml': 'rdfxm',
+    '.rdf': 'rdfxm',
+    '.ttl': 'turtle',
+    '.nt': 'ntriples'
+}
+
+class ProcessedObjects(models.Model):
+    obj_name = models.CharField(max_length=1024, db_index=True, null=False, blank=False)
+    obj_id = models.IntegerField(db_index=True, null=False, blank=False)
+
+    def __unicode__(self):
+        return "ProcessedObject -> id : %d, obj_name: %s, obj_id: %d" % (self.id or "None", self.obj_name, self.obj_id)
+    class Meta:
+        app_label = 'import_rdf'
 
 
-from django.core.management.base import BaseCommand, CommandError
-from django.db import transaction
-from hdabo.models import (Author, Datasheet, DocumentFormat, Domain, Organisation,
-    Tag, TaggedSheet, TimePeriod, Location)
-from hdabo.wp_utils import normalize_tag
-from optparse import make_option
-import csv
-import datetime
-import math
-import sys
-import os
-import shutil
-import rdflib
-import tempfile
+class KeyDefaultdict(collections.defaultdict):
+    def __missing__(self, key):
+        if self.default_factory:
+            dict.__setitem__(self, key, self.default_factory(key))
+            return self[key]
+        else:
+            collections.defaultdict.__missing__(self, key)
+
+
+class GraphCacheMap(collections.defaultdict):
+    def __init__(self, key=[]):
+        self.key = key
+        super(GraphCacheMap, self).__init__(GraphCacheMap)
+        self.key_index = 0
+
+    def get_index(self):
+        self.key_index += 1
+        return "%016x" % self.key_index
 
 class GraphCache(object):
-    
+
     def __init__(self, temp_folder=tempfile.tempdir, fmt='turtle'):
         self.base_temp_folder = temp_folder
         self.temp_folder = None
-        self.obj_cache = {}
         self.format = fmt
-
+        self.filename_map = GraphCacheMap()
 
-    def clear(self, keys=None):
-        """Clear from cache (memory)
-        
+    def clear_filename_cache(self, keys=None):
+        """Clear from filename cache (memory)
+
         :param tuple keys: the key to clear. ``None`` will clear all.
         """
         if not keys:
-            self.obj_cache.clear()
+            self.filename_map.clear()
             return
-        if not isinstance(keys, tuple):
+        if not isinstance(keys, (list,tuple)):
             keys = (keys,)
-        current_cache_dict = self.obj_cache
+        current_cache_dict = self.filename_map
         for k in keys[:-1]:
             current_cache_dict = current_cache_dict.get(k)
             if not isinstance(current_cache_dict,dict):
                 raise KeyError("%r not found " % keys)
         current_cache_dict.pop(keys[-1])
 
-    def purge(self, keys=None):
-        """Clear from disk, not from cache
-        
+
+    def clear(self, keys=None):
+        """Clear from disk cache
+
         :param tuple keys: the key to clear. ``None`` will clear all.
         """
         if not keys:
             path = self.temp_folder
             self.temp_folder = None
+            self.filename_map.clear()
         else:
             path = self.__build_path(keys)
 
         if not os.path.exists(path):
             raise KeyError("%r not found" % keys)
-        
         if os.path.isfile(path):
             os.remove(path)
         else:
             shutil.rmtree(path)
-
+        self.clear_filename_cache(keys)
 
-    def get_from_cache(self, keys):
-        """get graph from memory cache.
+    def get_from_filename_cache(self, keys):
+        """get from filename cache.
         :raises: KeyError if not found
         """
         if not keys:
             raise KeyError("Keys is None or empty")
         if not isinstance(keys, (tuple,list)):
             keys = (keys,)
+        #print("FILENAME MAP : " + repr(self.filename_map))
         try:
-            return reduce(lambda d,k: d[k], keys, self.obj_cache)
+            res = reduce(lambda d,k: d[k], keys, self.filename_map)
+            if(isinstance(res,GraphCacheMap)):
+                return res.key
+            else:
+                return res
         except:
             KeyError("%r not found" % keys)
 
+
     def __build_path(self, keys):
         if not keys:
             raise KeyError("Keys is None or empty")
         if not isinstance(keys, (tuple,list)):
             keys = (keys,)
+        file_parts = self.get_from_filename_cache(keys)
+        if file_parts:
+            return os.path.join(self.temp_folder,os.path.join(*file_parts))
+        else:
+            return self.temp_folder
 
-        return os.path.join(self.temp_folder,os.path.join(*keys))
+    def get_len(self, keys):
+        if not isinstance(keys, (tuple,list)):
+            keys = [keys,]
+        elif isinstance(keys, tuple):
+            keys = list(keys)
+        res = reduce(lambda d,k: d[k], keys, self.filename_map)
+        return len(res)
 
-    def get_from_disk(self, keys):
+    #TODO: treat errors
+    def get_iter(self,keys):
+        if not isinstance(keys, (tuple,list)):
+            keys = [keys,]
+        elif isinstance(keys, tuple):
+            keys = list(keys)
+        res = reduce(lambda d,k: d[k], keys, self.filename_map)
+
+        for graphfile_key in res.keys():
+            g = RDF.Model()
+            parser=RDF.Parser(name=self.format)
+            parser.parse_into_model(g, "file://"+self.__build_path(keys + [graphfile_key,]))
+            yield g
+
+    def get(self, keys, default=None):
         """get graph from disk cache
         :raises: KeyError if file not found"""
         path = self.__build_path(keys)
-        
+
         if not os.path.exists(path):
-            raise KeyError("%r not found" % keys)
-        
+            return default
+
         if not os.path.isfile(path):
             raise KeyError("%r found but not a file" % keys)
-        
-        g = rdflib.Graph()
+
+        #g = rdflib.Graph()
+        g = RDF.Model()
         try:
-            g.parse(path, format=self.format)
+        #    g.parse(path, format=self.format)
+            parser=RDF.Parser(name=self.format)
+            parser.parse_into_model(g, "file://"+path)
         except Exception as e:
             raise KeyError("Bad key %r. Error when reading file %r" % (keys, e))
         return g
 
 
-    def put_in_cache(self, keys, g):
+    def get_content(self, keys, default=[]):
+        """get dir content from disk cache
+        :returns: list of (keys, name, is_dir) with is_dir = True when item is a sub folder. Default when nothing is found.
+        :raises: KeyError if dir exists but is not folder"""
+
+        vir_folder = self.get_from_filename_cache(keys)
+        if not isinstance(vir_folder, dict):
+            raise KeyError("Bad key %r. path is not a folder" % keys)
+
+        return [(tuple(keys)+(k,),k,isinstance(v, dict)) for k,v in vir_folder.items()]
+
+
+    def put_in_filename_cache(self, keys, val):
         if not keys:
             raise KeyError("Keys is None or empty")
         if not isinstance(keys, (tuple,list)):
             keys = (keys,)
 
-        reduce(lambda d,k: d.setdefault(k,{}), keys[:-1], self.obj_cache)[keys[-1]] = g
+        reduce(lambda d,k: d.setdefault(k,{}), keys[:-1], self.obj_cache)[keys[-1]] = val
 
-    
-    def put_on_disk(self, keys, g):
+
+    def put(self, keys, g):
         if g is None:
             raise Exception("Null graph")
         if not self.temp_folder or not os.path.exists(self.temp_folder):
@@ -226,230 +296,714 @@
         if not os.path.isdir(self.temp_folder):
             raise Exception("Temp folder for disk cache is not a dir")
 
+        path_parts = []
+        current_map = self.filename_map
+        for k in keys[:-1]:
+            if k in current_map:
+                current_map = current_map[k]
+            else:
+                new_key = current_map.key + [current_map.get_index(),]
+                current_map = current_map.setdefault(k, GraphCacheMap(new_key))
+
+            path_parts.append(current_map.key[-1])
+        current_index = current_map.get_index()
+        path_parts.append(current_index)
+        current_map[keys[-1]] = path_parts;
+
         if len(keys)>1:
             path_dir = self.__build_path(keys[:-1])
             if not os.path.isdir(path_dir):
                 os.makedirs(path_dir)
         path = self.__build_path(keys)
-        g.serialise(path)
+        ser = RDF.Serializer(name=self.format)
+        ser.serialize_model_to_file(path, g)
+
+HDA_ONTOLOGY_BASE_URL = 'http://data.culture.fr/ontologies/hda/0.1#'
+
+TYPES_LIST = [
+    'Categorie',
+    'Tag',
+    'Site',
+    'Ville',
+    'Institution',
+    'Theme',
+    'Domaine',
+    'SousDomaine',
+    'Periode',
+    'Notice'
+]
+
+TARGET_TYPE_LIST = [
+    TaggedSheet,
+    Datasheet,
+    Domain,
+    TimePeriod,
+    Tag,
+    TagCategory,
+    Organisation,
+    Location,
+    DocumentFormat
+]
+
+RDF_NS = RDF.NS('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
+RDFS_NS = RDF.NS('http://www.w3.org/2000/01/rdf-schema#')
+HDA_NS = RDF.NS(HDA_ONTOLOGY_BASE_URL)
+DC_NS = RDF.NS("http://purl.org/dc/terms/")
+INSEE_NS = RDF.NS("http://rdf.insee.fr/def/geo#")
+XMLSCHEMA_NS = RDF.NS("http://www.w3.org/2001/XMLSchema#")
+FOAF_NS = RDF.NS("http://xmlns.com/foaf/0.1/")
+DBPEDIA_NS = RDF.NS("http://dbpedia.org/ontology/")
+
+def show_progress(current_line, total_line, width, message=""):
+
+    percent = (float(current_line) / float(total_line)) * 100.0
+
+    marks = math.floor(width * (percent / 100.0))
+    spaces = math.floor(width - marks)
+
+    loader = '[' + ('=' * int(marks)) + (' ' * int(spaces)) + ']'
+
+    message_fill = " " * max(0,width - len(message))
+
+    sys.stdout.write("%s %d%% %d/%d %s%s\r" % (loader, percent, current_line, total_line, message, message_fill)) #takes the header into account
+    if percent >= 100:
+        sys.stdout.write("\n")
+    sys.stdout.flush()
+
+
+class RdfImporter(object):
+
+    importer_stats = KeyDefaultdict(lambda k: {'object_name': k, 'created': 0, 'updated': 0, 'untouched': 0, 'deleted': 0})
+
+    def __init__(self, g, obj_type):
+        self.graph = g
+        self.obj_type = obj_type
+        self.obj_uri_node = self.graph.get_source(RDF_NS.type, HDA_NS[self.obj_type])
+        if self.obj_uri_node is None:
+            raise Exception("For importer %s type node is not foud" % self.obj_type)
+        self.attr_rdf_map = {'id': DC_NS.identifier}
+        self.attr_cache = {}
+
+    def import_graph(self):
+        for obj_instance, created, dirty in self.do_import_graph():
+            obj_name = type(obj_instance).__name__
+            ProcessedObjects.objects.create(obj_name = obj_name, obj_id = obj_instance.pk)
+            obj_stat = self.importer_stats[obj_name]
+            if created:
+                obj_stat['created'] += 1
+            elif dirty:
+                obj_stat['updated'] += 1
+            else:
+                obj_stat['untouched'] += 1
+
+    def do_import_graph(self):
+        pass
+    
+    def _process_attr_node(self, attr_node):
+        if attr_node is None:
+            return None
+
+        if attr_node.is_resource():
+            return str(attr_node.uri)
+        
+        if attr_node.is_blank():
+            new_g = RDF.Model()
+            qs = RDF.Statement(subject=attr_node, predicate=None, object=None)
+            new_g.add_statements(self.graph.find_statements(qs))
+            return new_g
+
+        #attr_node is litteral
+        lit_val = attr_node.literal_value
+        res_type = lit_val['datatype']
+        res_raw = lit_val['string']
+        res = res_raw
+        if res_type == XMLSCHEMA_NS.dateTime.uri:
+            res = isodate.parse_datetime(res_raw)
+        elif res_type == XMLSCHEMA_NS.integer.uri:
+            res = int(res_raw)
+        return res
+
+
+    def __getattr__(self, name):
+        if name in ('attr_cache'):
+            raise AttributeError, name
+        if name in self.attr_cache:
+            return self.attr_cache[name]
+        if name not in self.attr_rdf_map:
+            raise AttributeError("%s not in attributes", name)
+        
+        res = None
+        if isinstance(self.attr_rdf_map[name], (list, tuple, set)):
+            res = reduce(
+                lambda r, pred: r + [self._process_attr_node(attr_node) for attr_node in self.graph.get_targets(self.obj_uri_node, pred)],
+                self.attr_rdf_map[name],
+                []
+            )
+        else:
+            attr_node = self.graph.get_target(self.obj_uri_node, self.attr_rdf_map[name])
+            res = self._process_attr_node(attr_node)
+
+        self.attr_cache[name] = res
+        return res
+
+def RdfImporterFactory(type_name):
+    newImporter = type(type_name+"RdfImporter", (RdfImporter,), {"__init__": lambda self,g: RdfImporter.__init__(self, g, type_name)})
+    return newImporter
 
-    def commit_cache(self):
-        folder_stack = []
-        folder_stack.append(([], self.obj_cache))
-        while len(folder_stack) > 0:
-            keys, obj_dict = folder_stack.pop()
-            for k,v in obj_dict.iteritems():
-                new_keys = keys + [k]
-                if isinstance(v, dict):
-                    folder_stack.append((new_keys, v))
-                else:
-                    self.put_on_disk(new_keys, v)
+class VilleImporter(RdfImporter):
+
+    def __init__(self, g):
+        super(VilleImporter, self).__init__(g, 'Ville')
+        self.attr_rdf_map.update({
+            'codeINSEE': INSEE_NS.codeINSEE,
+            'nom': INSEE_NS.nom
+        })
+
+    def do_import_graph(self):
+        loc, created = Location.objects.get_or_create(insee=self.codeINSEE)
+        dirty = False
+        if loc.name != self.nom:
+            loc.name = self.nom
+            dirty = True
+        if dirty:
+            loc.save()
+        return [(loc, created, dirty), ]
+
+
+class CategorieImporter(RdfImporter):
+
+    def __init__(self, g):
+        super(CategorieImporter, self).__init__(g, 'Categorie')
+        self.attr_rdf_map.update({
+            'label': RDFS_NS.label
+        })
+
+    def do_import_graph(self):
+        cat, created = TagCategory.objects.get_or_create(label=self.label)
+        dirty = True
+        val = self.id
+        if cat.natural_key != val:
+            cat.natural_key = val
+            dirty = True
+        
+        if dirty :
+            cat.save()
+            
+        return [(cat, created, dirty), ]
+
+
+class TagImporter(RdfImporter):
+
+    def __init__(self, g):
+        super(TagImporter, self).__init__(g, 'Tag')
+        self.attr_rdf_map.update({
+            "label": RDFS_NS.label,
+            "alternative_label": HDA_NS.alternativeLabel,
+            "normalized_label": HDA_NS.normalizedLabel,
+            "original_label": HDA_NS.originalLabel,
+            "alias": HDA_NS.alias,
+            "category": HDA_NS.category,
+            "wikipedia_url": HDA_NS.wikipediaURL,
+            "wikipedia_pageid": HDA_NS.wikipediaPageId,
+            "alternative_wikipedia_url": HDA_NS.alternativeWikipediaURL,
+            "alternative_wikipedia_pageid": HDA_NS.alternativeWikipediaPageId,
+            "url_status": HDA_NS.urlStatus,
+            "dbpedia_uri": HDA_NS.dbpediaURI,
+            "popularity": HDA_NS.popularity,
+            "created_at": DC_NS.created
+        })
+
+    def do_import_graph(self):
+        
+        if self.id == "___":
+            return []
+        
+        tag, created = Tag.objects.get_or_create(label=self.label, original_label=self.original_label, url_status=self.url_status, defaults={'natural_key': self.id})
+        
+        dirty = False
+        tag.force_natural_key = True
+        tag.natural_key = self.id
+        for field in [f for f in self.attr_rdf_map.keys() if f not in ['label', 'original_label', 'url_status']]:
+            if field == 'id':
+                val = self.id
+                if tag.natural_key != val:
+                    dirty = True
+                    tag.natural_key = val
+            elif field == 'created_at':
+                val = self.created_at
+                if tag.created_at.replace(tzinfo=pytz.UTC) != val:
+                    dirty = True
+                    tag.created_at = val
+            elif field in ['alternative_wikipedia_pageid', 'wikipedia_pageid']:
+                val = getattr(self, field)
+                if val == '':
+                    val = None
+                if getattr(tag, field) != val:
+                    dirty = True
+                    setattr(tag, field, val)
+            elif field != 'category':
+                val = getattr(self, field)
+                if getattr(tag, field) != val:
+                    dirty = True
+                    setattr(tag, field, val)
+            elif self.category:
+                catid = self.category.split("/")[-1]
+                newCat = TagCategory.objects.filter(natural_key=catid).first()
+                if tag.category != newCat:
+                    tag.category = newCat
+                    dirty = True
+        if dirty:
+            tag.save()
+
+        return [(tag, created, dirty), ]
+
+
+class DomaineImporter(RdfImporter):
+    def __init__(self, g, obj_type='Domaine'):
+        super(DomaineImporter, self).__init__(g, obj_type)
+        self.attr_rdf_map.update({
+            'label': RDFS_NS.label,
+            'value': RDF_NS.value
+        })
+
+    def do_import_graph(self):
+        
+        domain, created = Domain.objects.get_or_create(natural_key=self.id, defaults={'label': self.value, 'school_period': Domain.DOMAIN_PERIOD_DICT[u'Global']})
+
+        dirty = False
+        if domain.label != self.value:
+            domain.label=self.value
+            dirty = True
+        if domain.school_period != Domain.DOMAIN_PERIOD_DICT[u'Global']:
+            domain.school_period = Domain.DOMAIN_PERIOD_DICT[u'Global']
+            dirty = True
+        
+        if dirty:
+            domain.save()
+
+        return [(domain, created, dirty),]
+
+
+class SousDomaineImporter(DomaineImporter):
+    def __init__(self, g):
+        super(SousDomaineImporter, self).__init__(g, 'SousDomaine')
 
 
-    def get(self, keys):
-        if not keys:
-            return None
+class ThemeImporter(DomaineImporter):
+    LEVEL_MAP = {
+        1: Domain.DOMAIN_PERIOD_DICT[u'Primaire'],
+        2: Domain.DOMAIN_PERIOD_DICT[u'Collège'],
+        3: Domain.DOMAIN_PERIOD_DICT[u'Lycée']
+    }
+
+    def __init__(self, g):
+        super(ThemeImporter, self).__init__(g, 'Theme')
+        self.attr_rdf_map.update({
+            'level': HDA_NS.niveau
+        })
+
+
+    def do_import_graph(self):
+        domain, created = Domain.objects.get_or_create(natural_key=self.id, defaults={'label': self.value, 'school_period': ThemeImporter.LEVEL_MAP[self.level]})
+
+        dirty = False
+        if domain.label != self.value:
+            domain.label=self.value
+            dirty = True
+        if domain.school_period != ThemeImporter.LEVEL_MAP[self.level]:
+            domain.school_period = ThemeImporter.LEVEL_MAP[self.level]
+            dirty = True
         
-        try:
-            return self.get_from_cache(keys)
-        except KeyError:
-            value = self.get_from_disk(keys)
-            self.put_in_cache(keys, value)
-            return value
+        if dirty:
+            domain.save()
+
+
+        return [(domain, created, dirty),]
+
+
+class PeriodeImporter(RdfImporter):
+    LEVEL_MAP = {
+        1: Domain.DOMAIN_PERIOD_DICT[u'Primaire'],
+        2: Domain.DOMAIN_PERIOD_DICT[u'Collège'],
+        3: Domain.DOMAIN_PERIOD_DICT[u'Lycée']
+    }
+
+    def __init__(self, g):
+        super(PeriodeImporter, self).__init__(g, 'Periode')
+        self.attr_rdf_map.update({
+            'value': RDF_NS.value,
+            'level': HDA_NS.niveau
+        })
+
+
+    def do_import_graph(self):
+
+        period, created = TimePeriod.objects.get_or_create(natural_key=self.id, defaults={'label': self.value, 'school_period': ThemeImporter.LEVEL_MAP[self.level]})
+
+        dirty = False
+        if period.label != self.value:
+            period.label=self.value
+            dirty = True
+        if period.school_period != ThemeImporter.LEVEL_MAP[self.level]:
+            period.school_period = ThemeImporter.LEVEL_MAP[self.level]
+            dirty = True
+        
+        if dirty:
+            period.save()
+
+
+        return [(period, created, dirty),]
+
+
+class SiteImporter(RdfImporter):
     
+    def __init__(self, g):
+        super(SiteImporter, self).__init__(g, 'Site')
+        self.attr_rdf_map.update({
+            'name': RDFS_NS.label,
+            'website': FOAF_NS.homepage,
+        })
     
-    def put(self, keys, value, on_disk=True):
-        self.put_in_cache(keys, value)
-        if on_disk:
-            self.put_on_disk(keys, value)
+    def do_import_graph(self):
+        
+        org, created = Organisation.objects.get_or_create(hda_id = self.id)
+        
+        dirty = False
+        if self.name != org.name:
+            org.name = self.name
+            dirty = True
+        if self.website != org.website:
+            org.website = self.website
+            dirty = True
+
+        if dirty:
+            org.save()
+
+        return [(org, created, dirty),]
 
 
+class InstitutionImporter(RdfImporter):
+    
+    def __init__(self, g):
+        super(InstitutionImporter, self).__init__(g, 'Institution')
+        self.attr_rdf_map.update({
+            'name': RDFS_NS.label,
+            'website': FOAF_NS.homepage,
+            'location': HDA_NS.ville
+        })
+    
+    def do_import_graph(self):
+        
+        org, created = Organisation.objects.get_or_create(hda_id = self.id)
+        
+        dirty = False
+        if self.name != org.name:
+            org.name = self.name
+            dirty = True
+        if self.website != org.website:
+            org.website = self.website
+            dirty = True
+        ville = self.location
+        if ville:
+            ville_id = ville.split("/")[-1]
+            location = Location.objects.filter(insee=ville_id).first()
+            if location and location.name != org.location:
+                org.location = location.name
+                dirty = True
 
-RDF_IMPORTERS = {}
+        if dirty:
+            org.save()
+
+        return [(org, created, dirty),]
 
-class RdfImporter(object):
+class DocumentTagImporter(RdfImporter):
+    def __init__(self, g, datasheet):
+        super(DocumentTagImporter, self).__init__(g, 'DocumentTag')
+        self.datasheet = datasheet
+        self.attr_rdf_map.update({
+            'created_at': DC_NS.created,
+            'order': HDA_NS.order,
+            'original_order': HDA_NS.originalOrder,
+            'index_note': HDA_NS.indexNote,
+            'wikipedia_revision_id': DBPEDIA_NS.wikiPageRevisionID,
+            'tag': HDA_NS.tag
+        })
+    
+    def do_import_graph(self):
+        tag = Tag.objects.filter(natural_key=self.tag.split("/")[-1]).first()
+        if tag is None:
+            logger.warn("Tag %r not found for datasheet %r", self.tag.split("/")[-1], self.datasheet.hda_id)
+        ts = TaggedSheet.objects.filter(tag=tag, datasheet=self.datasheet).first()
+        created = False
+        if ts is None:
+            ts = TaggedSheet.objects.create(tag=tag, datasheet=self.datasheet)
+            created = True
+        
+        dirty = False
+        
+        val = self.created_at
+        if ts.created_at.replace(tzinfo=pytz.UTC) != val:
+            dirty = True
+            ts.created_at = val
+
+        for field in ['order', 'original_order', 'index_note', 'wikipedia_revision_id']:
+            val = getattr(self, field)
+            if val == '':
+                val = None
+            if getattr(ts, field) != val:
+                setattr(ts, field, val)
+                dirty = True
+
+        if dirty:
+            ts.save()
+        
+        return [(ts,created,dirty),]
+
+
+class NoticeImporter(RdfImporter):
+
     def __init__(self, g):
-        self.graph = g
-    def import_graph(self):
-        raise NotImplementedError() 
+        super(NoticeImporter, self).__init__(g, 'Notice')
+        #print("NOTICE IMPORTER")
+        #ser = RDF.Serializer(name='turtle')
+        #print(ser.serialize_model_to_string(g))
+        
+        self.attr_rdf_map.update({
+            'title': DC_NS.title,
+            'description': DC_NS.description,
+            'url': HDA_NS.url,
+            'organisation': HDA_NS.institution,
+            'original_creation_date': DC_NS.created,
+            'original_modification_date': DC_NS.issued,
+            'town': HDA_NS.ville,
+            'format': DC_NS.format,
+            'domains': [HDA_NS.domaine, HDA_NS.sousDomainePrimaire],
+            'primary_periods': [HDA_NS.periodePrimaire,],
+            'college_periods': [HDA_NS.periodeCollege,],
+            'highschool_periods': [HDA_NS.periodeLycee,],
+            'primary_themes': [HDA_NS.themePrimaire,],
+            'college_themes': [HDA_NS.themeCollege,],
+            'highschool_themes': [HDA_NS.themeLycee,],
+            'tags': [HDA_NS.documentTag,]
+        })
+
+    @transaction.atomic
+    def do_import_graph(self):
+        
+        
+        ds, created = Datasheet.objects.get_or_create(hda_id=self.id, defaults={'original_creation_date': self.original_creation_date, 'original_modification_date': self.original_modification_date})
+        dirty=False
+        
+        res = []
+
+        for field in ['title', 'description', 'url']:
+            val = getattr(self, field)
+            if getattr(ds, field) != val:
+                setattr(ds, field, val)
+                dirty = True
+        for field in [ 'original_creation_date', 'original_modification_date']:
+            val = getattr(self, field).date()
+            if getattr(ds, field) != val:
+                setattr(ds, field, val)
+                dirty = True
+        
+        org_url = self.organisation
+        org = None
+        if org_url:
+            org_id = org_url.split("/")[-1]
+            org = Organisation.objects.filter(hda_id=org_id).first()
+        
+        if org != ds.organisation:
+            ds.organisation = org
+            dirty = True
 
+        town_url = self.town
+        town = None
+        if town_url:
+            town_id = town_url.split("/")[-1]
+            town = Location.objects.filter(insee=town_id).first()
+        if town != ds.town:
+            ds.town = town
+            dirty = True
+        
+        fmt = self.format
+        format_obj = None
+        if fmt:
+            format_obj, f_created = DocumentFormat.objects.get_or_create(label=fmt)
+            res.append((format_obj, f_created, False))
+
+        if format_obj != ds.format:
+            ds.format = format_obj
+            dirty = True
+            
+        if not ds.validated:
+            ds.validated = True
+            dirty = True
+        
+        for (field, ObjKlass) in [('domains',Domain), ('primary_periods', TimePeriod), ('college_periods', TimePeriod), ('highschool_periods', TimePeriod), ('primary_themes', Domain), ('college_themes', Domain), ('highschool_themes', Domain)]:
+            tgt_obj_ids = [obj_url.split("/")[-1] for obj_url in getattr(self, field)]
+            tgt_obj_ids.sort()
+            
+            ds_objs_ids = [d.natural_key for d in getattr(ds, field).all()]
+            ds_objs_ids.sort()
+            if ds_objs_ids !=  tgt_obj_ids:
+                #get trought class
+                ThroughKlass = getattr(Datasheet, field).through
+                ThroughKlass.objects.filter(datasheet=ds).delete()
+                for i,tgt_obj in enumerate(ObjKlass.objects.filter(natural_key__in = tgt_obj_ids)):
+                    link_args = {'datasheet': ds, ObjKlass.__name__.lower(): tgt_obj, 'sort_value': i}
+                    ThroughKlass.objects.create(**link_args)
+                dirty = True
+        
+        if dirty:
+            ds.save()
+        
+        res.append((ds, created, dirty))
+        
+        #create TaggedSheet
+        for tagged_sheet_graph in self.tags:
+            importer = DocumentTagImporter(tagged_sheet_graph, ds)
+            importer.import_graph()
+        
+        return res
+
+
+RDF_IMPORTERS = KeyDefaultdict(RdfImporterFactory, {
+    'Ville': VilleImporter,
+    'Categorie': CategorieImporter,
+    'Tag': TagImporter,
+    'Domaine': DomaineImporter,
+    'SousDomaine': SousDomaineImporter,
+    'Theme': ThemeImporter,
+    'Periode': PeriodeImporter,
+    'Site': SiteImporter,
+    'Institution': InstitutionImporter,
+    'Notice': NoticeImporter
+})
 
 class Command(BaseCommand):
     '''
     Command to import csvfile
     '''
     args = '<path_to_csv_file path_to_csv_file ...>'
-    options = '[--ignore-existing] [--lines] [--encoding]'
+    options = '[--type TYPE]'
     help = """Import of a csv file for hdabo
 Options:
     --ignore-existing : ignore existing datasheets
     --lines : max number of lines to load (for each file). 0 means all.
 """
-    
-    option_list = BaseCommand.option_list + (
-        make_option('--lines',
-            action='store',
-            type='int',
-            dest='lines',
-            default=0,
-            help='Number of lines to read. 0 means all.'),
-        make_option('--ignore-existing',
-            action='store_true',
-            dest='ignore_existing',
-            default=False,
-            help='force insertion'),
-        
-        )
-    
-    def show_progress(self, current_line, total_line, width):
-
-        percent = (float(current_line) / float(total_line)) * 100.0
-
-        marks = math.floor(width * (percent / 100.0))
-        spaces = math.floor(width - marks)
-    
-        loader = '[' + ('=' * int(marks)) + (' ' * int(spaces)) + ']'
-    
-        sys.stdout.write("%s %d%% %d/%d\r" % (loader, percent, current_line - 1, total_line - 1)) #takes the header into account
-        if percent >= 100:
-            sys.stdout.write("\n")
-        sys.stdout.flush()
 
-    
-    def create_domain_period(self, row_value, klass, school_period):
-        res_list = []
-        if not row_value:
-            return res_list
-        for label_str in [dstr.strip() for dstr in row_value.split('\x0b')]:
-            if label_str:
-                res_obj, created = klass.objects.get_or_create(label=label_str, school_period=school_period, defaults={"label":label_str, "school_period":school_period}) #@UnusedVariable
-                res_list.append(res_obj)
-        return res_list
-    
-    def create_datasheet(self, row):
-        
-        if self.ignore_existing and Datasheet.objects.filter(hda_id=row[u"ID"]).count() > 0:
-            return
-        
-        author_str = row[u'Auteur']
-        if author_str:
-            author_array = author_str.split(" ")
-            if len(author_array) == 0:
-                firstname = ""
-                lastname = ""
-            elif len(author_array) == 1:
-                firstname = ""
-                lastname = author_array[0]
-            elif len(author_array) == 2:
-                firstname = author_array[0]
-                lastname = author_array[1]
-                
-            author, created = Author.objects.get_or_create(hda_id=author_str, defaults={"firstname":firstname, "lastname":lastname}) #@UnusedVariable
-        else:
-            author = None
-        
-        org_str = row[u"Org"]    
-        if org_str:
-            url_str = row[u'Org_Home']
-            if url_str is not None:
-                url_str = url_str.strip()
-            org, created = Organisation.objects.get_or_create(hda_id=org_str, defaults={"name":org_str, "website" : url_str}) #@UnusedVariable
-        else:
-            org = None
-            
-        town_str = row[u"Ville"]
-        if town_str:
-            insee_str = row[u'Insee'].strip() if row[u'Insee'] else row[u'Insee']
-            if len(insee_str) > 5:
-                insee_str = "" 
-            loc, created = Location.objects.get_or_create(insee=insee_str, defaults={"name": town_str, "insee": insee_str}) #@UnusedVariable
-        else:
-            loc = None
-            
-        format_str = row[u"Format"]
-        if format_str:
-            format, created = DocumentFormat.objects.get_or_create(label=format_str, defaults={"label": format_str}) #@UnusedVariable
-        else:
-            format = None
-        
-        domains = self.create_domain_period(row[u"Domaine"], Domain, Domain.DOMAIN_PERIOD_DICT[u'Global'])
-                                        
-        primary_periods = self.create_domain_period(row[u"Periode1"], TimePeriod, TimePeriod.TIME_PERIOD_DICT[u'Primaire'])
-        college_periods = self.create_domain_period(row[u"Periode2"], TimePeriod, TimePeriod.TIME_PERIOD_DICT[u'Collège'])
-        highschool_periods = self.create_domain_period(row[u"Periode3"], TimePeriod, TimePeriod.TIME_PERIOD_DICT[u'Lycée'])
-                    
-        primary_themes = self.create_domain_period(row[u"Sousdom"], Domain, Domain.DOMAIN_PERIOD_DICT[u'Primaire'])
-        college_themes = self.create_domain_period(row[u"Theme2"], Domain, Domain.DOMAIN_PERIOD_DICT[u'Collège'])
-        highschool_themes = self.create_domain_period(row[u"Theme3"], Domain, Domain.DOMAIN_PERIOD_DICT[u'Lycée'])
-        
-        url = row[u"Url"]
-        if url is not None:
-            url = url.strip()
-        
-        datasheet = Datasheet.objects.create(
-            hda_id=row[u"ID"],
-            author=author,
-            organisation=org,
-            title=row[u"Titre"],
-            description=row[u"Desc"],
-            url=url,
-            town=loc,
-            format=format,
-            original_creation_date=datetime.datetime.strptime(row[u"Datcre"], "%d/%m/%Y").date(),
-            original_modification_date=datetime.datetime.strptime(row[u"Datmaj"], "%d/%m/%Y").date(),
-            validated=False
+    option_list = BaseCommand.option_list + (
+        make_option('-t', '--type',
+            action='append',
+            type='string',
+            dest='types',
+            default=[],
+            help='types to process.'),
         )
-        
-        datasheet.save()
-        
-        datasheet.set_domains(domains)
-        datasheet.set_primary_periods(primary_periods)
-        datasheet.set_college_periods(college_periods)
-        datasheet.set_highschool_periods(highschool_periods)
-        datasheet.set_primary_themes(primary_themes)
-        datasheet.set_college_themes(college_themes)
-        datasheet.set_highschool_themes(highschool_themes)
+
+
 
-        
-        if row[u'Tag']:
-            for i, tag in enumerate([t.strip() for t in row[u'Tag'].split(u";")]):
-                if len(tag) == 0:
-                    continue
-                tag_label = normalize_tag(tag)
-                tag_obj = None
-                for t in Tag.objects.filter(label__iexact=tag_label):
-                    if tag_obj is None or t.url_status != Tag.TAG_URL_STATUS_DICT['null_result']:
-                        tag_obj = t
-                        if tag_obj.url_status != Tag.TAG_URL_STATUS_DICT['null_result']:
-                            break
- 
-                if tag_obj is None:
-                    tag_obj = Tag(label=tag_label, original_label=tag)
-                    tag_obj.save()
-
-                tagged_ds = TaggedSheet(datasheet=datasheet, tag=tag_obj, original_order=i + 1, order=i + 1)
-                tagged_ds.save()
-        
+    def get_entity_cache_keys(self, s):
+        return [base64.urlsafe_b64encode(p) for p in s.rstrip("/").rsplit("/",1)]
 
     def handle(self, *args, **options):
-        
+
         if len(args) == 0:
             raise CommandError("Gives at last one rdf file to import")
-        
-        lines = options.get('lines', 0)
-        self.ignore_existing = options.get('ignore_existing', False)
-        
+
+        self.types_list = options.get('types', TYPES_LIST) or TYPES_LIST
+
+        if any([t not in TYPES_LIST for t in self.types_list]):
+            raise CommandError("Types is %r : all types mus be in %r" % (self.types_list, TYPES_LIST))
+
+        models.register_models('import_rdf', ProcessedObjects)
+        connection = connections[DEFAULT_DB_ALIAS]
+        sql_temp, _ = connection.creation.sql_create_model(ProcessedObjects, no_style(), [])
+        cursor = connection.cursor()
+        for stmt in sql_temp:
+            cursor.execute(stmt.replace("CREATE TABLE", "CREATE TEMPORARY TABLE"))
+
+        transaction.set_dirty()
+        transaction.commit()
+        #lines = options.get('lines', 0)
+        #self.ignore_existing = options.get('ignore_existing', False)
+
         #open rdf file
-        graph_iterator = None
-        
-        #getting sizes and splitting rdf in "cache"
-        
-        #iterate on 1 level objects, counting by type
-        for rdfgraph in graph_iterator:
-            pass
-        #iterate on 1 level objects
-        for rdfgraph in graph_iterator:
-            #rdf_importer = RDF_IMPORTERS.get(k)
-            pass
-        
+        src_uri = RDF.Uri("file://" + args[0])
+        _, fileExtension = os.path.splitext(args[0])
+
+        parser = RDF.Parser(name=RDF_EXT_MAP.get(fileExtension, 'rdfxml'))
+
+        current_graph = RDF.Model()
+        current_entity = None
+        current_id = None
+        current_type = None
+        blank_node_statements = {}
+
+        graph_disk_cache = GraphCache()
+
+        for statement in parser.parse_as_stream(uri=src_uri):
+            if statement.predicate.uri == RDF.Uri("http://www.w3.org/1999/02/22-rdf-syntax-ns#type") and statement.subject.type != RDF.node_type('NODE_TYPE_BLANK'):
+                current_type = unicode(statement.object.uri)
+            if statement.predicate.uri == RDF.Uri("http://purl.org/dc/terms/identifier") and statement.subject.type != RDF.node_type('NODE_TYPE_BLANK'):
+                current_id = statement.object.literal[0]
+
+            if statement.subject.type == RDF.node_type('NODE_TYPE_RESOURCE'):
+                if current_entity is not None and current_entity != statement.subject:
+                    cache_keys = [current_type, current_id]
+                    graph_disk_cache.put(cache_keys, current_graph)
+                    current_graph = RDF.Model()
+                    current_id = None
+                    current_type = None
+                current_graph.append(RDF.Statement(statement=statement))
+                current_entity = statement.subject
+            elif statement.subject.type == RDF.node_type('NODE_TYPE_BLANK'):
+                if not current_graph.find_statements(RDF.Statement(None, None, statement.subject)).end():
+                    current_graph.append(RDF.Statement(statement=statement))
+                else:
+                    blank_node_statements.setdefault(statement.subject,[]).append(statement)
+            else:
+                current_graph.append(RDF.Statement(statement=statement))
 
+            if statement.object.type == RDF.node_type('NODE_TYPE_BLANK') and statement.object in blank_node_statements:
+                for stmt in blank_node_statements.pop(statement.object):
+                    current_graph.append(RDF.Statement(statement=stmt))
+
+        if current_entity is not None and current_type is not None and current_id is not None:
+            cache_keys = [current_type, current_id]
+            graph_disk_cache.put(cache_keys, current_graph)
+
+
+        if blank_node_statements:
+            raise CommandError("Orphan rdf statements for blank nodes %r", blank_node_statements)
+
+
+        for i, obj_type in enumerate(self.types_list):
+            print("stage %d/%d : processing %s :" % (i+1, len(self.types_list), obj_type))
+            obj_type_url = HDA_ONTOLOGY_BASE_URL+obj_type
+            obj_len = graph_disk_cache.get_len(obj_type_url)
+            for i,g in enumerate(graph_disk_cache.get_iter(obj_type_url)):
+                importer_class = RDF_IMPORTERS[obj_type]
+                importer = importer_class(g)
+                obj_id = importer.id
+                show_progress(i+1, obj_len, 80, "ID: %s" % obj_id[:76])
+                importer.import_graph()
+            print("")
+
+
+        for target_type in TARGET_TYPE_LIST:
+            delete_qs = target_type.objects.exclude(id__in = ProcessedObjects.objects.filter(obj_name=target_type.__name__).values_list('obj_id', flat=True))
+            RdfImporter.importer_stats[target_type.__name__]['deleted'] = delete_qs.count()
+            print("Deleting %s : %d " % (target_type.__name__, RdfImporter.importer_stats[target_type.__name__]['deleted']))
+            delete_qs.delete()
+
+        print("Import stats :")
+        for obj_stat in RdfImporter.importer_stats.values():
+            print("    Obj: {object_name} -> created: {created:d}, updated: {updated:d}, deleted: {deleted:d}, untouched: {untouched:d}".format(**obj_stat))
+        print("")
+
+        #print(graph_disk_cache.temp_folder)
+        shutil.rmtree(graph_disk_cache.temp_folder)
+
+#]
--- a/src/hdabo/migrations/0004_index_tag.py	Wed Dec 10 10:05:38 2014 +0100
+++ b/src/hdabo/migrations/0004_index_tag.py	Wed Feb 18 01:53:34 2015 +0100
@@ -1,8 +1,6 @@
 # encoding: utf-8
-import datetime
 from south.db import db
 from south.v2 import SchemaMigration
-from django.db import models
 
 class Migration(SchemaMigration):
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hdabo/migrations/0009_auto__add_field_tag_natural_key.py	Wed Feb 18 01:53:34 2015 +0100
@@ -0,0 +1,216 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'Tag.natural_key'
+        db.add_column(u'hdabo_tag', 'natural_key',
+                      self.gf('django.db.models.fields.CharField')(db_index=True, max_length=7168, null=True, blank=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Tag.natural_key'
+        db.delete_column(u'hdabo_tag', 'natural_key')
+
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        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'hdabo.author': {
+            'Meta': {'object_name': 'Author'},
+            'firstname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lastname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet': {
+            'Meta': {'object_name': 'Datasheet'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Author']", 'null': 'True', 'blank': 'True'}),
+            'college_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'college_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'domains': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_domains']", 'to': u"orm['hdabo.Domain']"}),
+            'format': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.DocumentFormat']", 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'highschool_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'highschool_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_themes']", 'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'manual_order': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'modification_datetime': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Organisation']", 'null': 'True'}),
+            'original_creation_date': ('django.db.models.fields.DateField', [], {}),
+            'original_modification_date': ('django.db.models.fields.DateField', [], {}),
+            'primary_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'primary_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Tag']", 'through': u"orm['hdabo.TaggedSheet']", 'symmetrical': 'False'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+            'town': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Location']", 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'validation_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'validator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.User']", 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet_college_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_college_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_domains': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_domains'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_highschool_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_highschool_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_primary_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_primary_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.documentformat': {
+            'Meta': {'object_name': 'DocumentFormat'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.domain': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'Domain'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.folder': {
+            'Meta': {'object_name': 'Folder'},
+            'datasheets': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Datasheet']", 'symmetrical': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '2048'})
+        },
+        u'hdabo.location': {
+            'Meta': {'object_name': 'Location'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '5'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
+        },
+        u'hdabo.organisation': {
+            'Meta': {'object_name': 'Organisation'},
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'website': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tag': {
+            'Meta': {'unique_together': "(('label', 'original_label', 'url_status'),)", 'object_name': 'Tag'},
+            'alias': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TagCategory']", 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dbpedia_uri': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '7168', 'null': 'True', 'blank': 'True'}),
+            'normalized_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'original_label': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+            'popularity': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'url_status': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tagcategory': {
+            'Meta': {'object_name': 'TagCategory'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.taggedsheet': {
+            'Meta': {'object_name': 'TaggedSheet'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'index_note': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'db_index': 'True'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'original_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Tag']"}),
+            'wikipedia_revision_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.timeperiod': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'TimePeriod'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.user': {
+            'Meta': {'object_name': 'User', 'db_table': "'auth_user'"},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        }
+    }
+
+    complete_apps = ['hdabo']
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hdabo/migrations/0010_tag_natural_key.py	Wed Feb 18 01:53:34 2015 +0100
@@ -0,0 +1,230 @@
+# -*- coding: utf-8 -*-
+
+from south.v2 import DataMigration
+
+from hdabo import utils
+
+
+def calculate_natural_key(tag):
+    parts = [
+        utils.sanitize(tag.label),
+        utils.sanitize(tag.normalized_label),
+        utils.sanitize(tag.original_label),
+        tag.wikipedia_url.split("/")[-1].rstrip('/') if tag.wikipedia_url else ""
+    ]
+    return ('_'.join(parts))[:7168]
+
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        total_line = orm.Tag.objects.count()
+        for i,tag in enumerate(orm.Tag.objects.all()):
+            utils.show_progress(i+1, total_line, "Processing %r " % tag.label, 50)
+            tag.natural_key = calculate_natural_key(tag)
+            tag.save()
+        # Note: Don't use "from appname.models import ModelName". 
+        # Use orm.ModelName to refer to models in this application,
+        # and orm['appname.ModelName'] for models in other applications.
+
+    def backwards(self, orm):
+        orm.Tag.objects.all().update(natural_key=None)
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        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'hdabo.author': {
+            'Meta': {'object_name': 'Author'},
+            'firstname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lastname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet': {
+            'Meta': {'object_name': 'Datasheet'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Author']", 'null': 'True', 'blank': 'True'}),
+            'college_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'college_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'domains': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_domains']", 'to': u"orm['hdabo.Domain']"}),
+            'format': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.DocumentFormat']", 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'highschool_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'highschool_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_themes']", 'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'manual_order': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'modification_datetime': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Organisation']", 'null': 'True'}),
+            'original_creation_date': ('django.db.models.fields.DateField', [], {}),
+            'original_modification_date': ('django.db.models.fields.DateField', [], {}),
+            'primary_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'primary_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Tag']", 'through': u"orm['hdabo.TaggedSheet']", 'symmetrical': 'False'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+            'town': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Location']", 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'validation_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'validator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.User']", 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet_college_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_college_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_domains': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_domains'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_highschool_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_highschool_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_primary_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_primary_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.documentformat': {
+            'Meta': {'object_name': 'DocumentFormat'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.domain': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'Domain'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.folder': {
+            'Meta': {'object_name': 'Folder'},
+            'datasheets': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Datasheet']", 'symmetrical': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '2048'})
+        },
+        u'hdabo.location': {
+            'Meta': {'object_name': 'Location'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '5'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
+        },
+        u'hdabo.organisation': {
+            'Meta': {'object_name': 'Organisation'},
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'website': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tag': {
+            'Meta': {'unique_together': "(('label', 'original_label', 'url_status'),)", 'object_name': 'Tag'},
+            'alias': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TagCategory']", 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dbpedia_uri': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'db_index': 'True', 'max_length': '7168', 'null': 'True', 'blank': 'True'}),
+            'normalized_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'original_label': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+            'popularity': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'url_status': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tagcategory': {
+            'Meta': {'object_name': 'TagCategory'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.taggedsheet': {
+            'Meta': {'object_name': 'TaggedSheet'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'index_note': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'db_index': 'True'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'original_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Tag']"}),
+            'wikipedia_revision_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.timeperiod': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'TimePeriod'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.user': {
+            'Meta': {'object_name': 'User', 'db_table': "'auth_user'"},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        }
+    }
+
+    complete_apps = ['hdabo']
+    symmetrical = True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hdabo/migrations/0011_auto__chg_field_tag_natural_key.py	Wed Feb 18 01:53:34 2015 +0100
@@ -0,0 +1,214 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+
+        # Changing field 'Tag.natural_key'
+        db.alter_column(u'hdabo_tag', 'natural_key', self.gf('django.db.models.fields.CharField')(default='_', max_length=7168))
+
+    def backwards(self, orm):
+
+        # Changing field 'Tag.natural_key'
+        db.alter_column(u'hdabo_tag', 'natural_key', self.gf('django.db.models.fields.CharField')(max_length=7168, null=True))
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        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'hdabo.author': {
+            'Meta': {'object_name': 'Author'},
+            'firstname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lastname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet': {
+            'Meta': {'object_name': 'Datasheet'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Author']", 'null': 'True', 'blank': 'True'}),
+            'college_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'college_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'domains': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_domains']", 'to': u"orm['hdabo.Domain']"}),
+            'format': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.DocumentFormat']", 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'highschool_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'highschool_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_themes']", 'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'manual_order': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'modification_datetime': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Organisation']", 'null': 'True'}),
+            'original_creation_date': ('django.db.models.fields.DateField', [], {}),
+            'original_modification_date': ('django.db.models.fields.DateField', [], {}),
+            'primary_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'primary_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Tag']", 'through': u"orm['hdabo.TaggedSheet']", 'symmetrical': 'False'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+            'town': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Location']", 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'validation_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'validator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.User']", 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet_college_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_college_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_domains': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_domains'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_highschool_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_highschool_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_primary_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_primary_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.documentformat': {
+            'Meta': {'object_name': 'DocumentFormat'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.domain': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'Domain'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.folder': {
+            'Meta': {'object_name': 'Folder'},
+            'datasheets': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Datasheet']", 'symmetrical': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '2048'})
+        },
+        u'hdabo.location': {
+            'Meta': {'object_name': 'Location'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '5'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
+        },
+        u'hdabo.organisation': {
+            'Meta': {'object_name': 'Organisation'},
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'website': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tag': {
+            'Meta': {'unique_together': "(('label', 'original_label', 'url_status'),)", 'object_name': 'Tag'},
+            'alias': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TagCategory']", 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dbpedia_uri': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '7168', 'db_index': 'True'}),
+            'normalized_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'original_label': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+            'popularity': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'url_status': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tagcategory': {
+            'Meta': {'object_name': 'TagCategory'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.taggedsheet': {
+            'Meta': {'object_name': 'TaggedSheet'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'index_note': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'db_index': 'True'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'original_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Tag']"}),
+            'wikipedia_revision_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.timeperiod': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'TimePeriod'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.user': {
+            'Meta': {'object_name': 'User', 'db_table': "'auth_user'"},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        }
+    }
+
+    complete_apps = ['hdabo']
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hdabo/migrations/0012_auto__add_field_domain_natural_key__add_field_timeperiod_natural_key.py	Wed Feb 18 01:53:34 2015 +0100
@@ -0,0 +1,226 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'Domain.natural_key'
+        db.add_column(u'hdabo_domain', 'natural_key',
+                      self.gf('django.db.models.fields.CharField')(max_length=512, unique=True, null=True),
+                      keep_default=False)
+
+        # Adding field 'TimePeriod.natural_key'
+        db.add_column(u'hdabo_timeperiod', 'natural_key',
+                      self.gf('django.db.models.fields.CharField')(max_length=512, unique=True, null=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'Domain.natural_key'
+        db.delete_column(u'hdabo_domain', 'natural_key')
+
+        # Deleting field 'TimePeriod.natural_key'
+        db.delete_column(u'hdabo_timeperiod', 'natural_key')
+
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        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'hdabo.author': {
+            'Meta': {'object_name': 'Author'},
+            'firstname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lastname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet': {
+            'Meta': {'object_name': 'Datasheet'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Author']", 'null': 'True', 'blank': 'True'}),
+            'college_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'college_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'domains': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_domains']", 'to': u"orm['hdabo.Domain']"}),
+            'format': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.DocumentFormat']", 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'highschool_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'highschool_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_themes']", 'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'manual_order': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'modification_datetime': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Organisation']", 'null': 'True'}),
+            'original_creation_date': ('django.db.models.fields.DateField', [], {}),
+            'original_modification_date': ('django.db.models.fields.DateField', [], {}),
+            'primary_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'primary_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Tag']", 'through': u"orm['hdabo.TaggedSheet']", 'symmetrical': 'False'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+            'town': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Location']", 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'validation_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'validator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.User']", 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet_college_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_college_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_domains': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_domains'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_highschool_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_highschool_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_primary_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_primary_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.documentformat': {
+            'Meta': {'object_name': 'DocumentFormat'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.domain': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'Domain'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '512', 'unique': 'True', 'null': 'True'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.folder': {
+            'Meta': {'object_name': 'Folder'},
+            'datasheets': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Datasheet']", 'symmetrical': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '2048'})
+        },
+        u'hdabo.location': {
+            'Meta': {'object_name': 'Location'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '5'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
+        },
+        u'hdabo.organisation': {
+            'Meta': {'object_name': 'Organisation'},
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'website': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tag': {
+            'Meta': {'unique_together': "(('label', 'original_label', 'url_status'),)", 'object_name': 'Tag'},
+            'alias': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TagCategory']", 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dbpedia_uri': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '7168', 'db_index': 'True'}),
+            'normalized_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'original_label': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+            'popularity': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'url_status': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tagcategory': {
+            'Meta': {'object_name': 'TagCategory'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.taggedsheet': {
+            'Meta': {'object_name': 'TaggedSheet'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'index_note': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'db_index': 'True'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'original_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Tag']"}),
+            'wikipedia_revision_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.timeperiod': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'TimePeriod'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '512', 'unique': 'True', 'null': 'True'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.user': {
+            'Meta': {'object_name': 'User', 'db_table': "'auth_user'"},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        }
+    }
+
+    complete_apps = ['hdabo']
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hdabo/migrations/0013_domain_period_natural_key.py	Wed Feb 18 01:53:34 2015 +0100
@@ -0,0 +1,227 @@
+# -*- coding: utf-8 -*-
+from south.v2 import DataMigration
+
+from hdabo import utils
+
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        print("Stage 1 of 2 : procession Domains")
+        total_line = orm.Domain.objects.count()
+        for i,domain in enumerate(orm.Domain.objects.all()):
+            utils.show_progress(i+1, total_line, "Processing %r " % domain.label, 50)
+            domain.natural_key = utils.sanitize(domain.label).replace("-","")
+            domain.save()
+
+        print("Stage 2 of 2 : procession Periods")
+        total_line = orm.TimePeriod.objects.count()
+        for i,period in enumerate(orm.TimePeriod.objects.all()):
+            utils.show_progress(i+1, total_line, "Processing %r " % period.label, 50)
+            period.natural_key = utils.sanitize(period.label)
+            period.save()
+
+    def backwards(self, orm):
+        orm.Domain.objects.all().update(natural_key=None)
+        orm.TimePeriod.objects.all().update(natural_key=None)
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        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'hdabo.author': {
+            'Meta': {'object_name': 'Author'},
+            'firstname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lastname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet': {
+            'Meta': {'object_name': 'Datasheet'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Author']", 'null': 'True', 'blank': 'True'}),
+            'college_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'college_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'domains': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_domains']", 'to': u"orm['hdabo.Domain']"}),
+            'format': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.DocumentFormat']", 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'highschool_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'highschool_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_themes']", 'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'manual_order': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'modification_datetime': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Organisation']", 'null': 'True'}),
+            'original_creation_date': ('django.db.models.fields.DateField', [], {}),
+            'original_modification_date': ('django.db.models.fields.DateField', [], {}),
+            'primary_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'primary_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Tag']", 'through': u"orm['hdabo.TaggedSheet']", 'symmetrical': 'False'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+            'town': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Location']", 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'validation_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'validator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.User']", 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet_college_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_college_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_domains': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_domains'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_highschool_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_highschool_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_primary_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_primary_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.documentformat': {
+            'Meta': {'object_name': 'DocumentFormat'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.domain': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'Domain'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '512', 'unique': 'True', 'null': 'True'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.folder': {
+            'Meta': {'object_name': 'Folder'},
+            'datasheets': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Datasheet']", 'symmetrical': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '2048'})
+        },
+        u'hdabo.location': {
+            'Meta': {'object_name': 'Location'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '5'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
+        },
+        u'hdabo.organisation': {
+            'Meta': {'object_name': 'Organisation'},
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'website': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tag': {
+            'Meta': {'unique_together': "(('label', 'original_label', 'url_status'),)", 'object_name': 'Tag'},
+            'alias': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TagCategory']", 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dbpedia_uri': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '7168', 'db_index': 'True'}),
+            'normalized_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'original_label': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+            'popularity': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'url_status': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tagcategory': {
+            'Meta': {'object_name': 'TagCategory'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.taggedsheet': {
+            'Meta': {'object_name': 'TaggedSheet'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'index_note': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'db_index': 'True'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'original_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Tag']"}),
+            'wikipedia_revision_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.timeperiod': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'TimePeriod'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '512', 'unique': 'True', 'null': 'True'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.user': {
+            'Meta': {'object_name': 'User', 'db_table': "'auth_user'"},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        }
+    }
+
+    complete_apps = ['hdabo']
+    symmetrical = True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hdabo/migrations/0014_auto__chg_field_domain_natural_key__chg_field_timeperiod_natural_key.py	Wed Feb 18 01:53:34 2015 +0100
@@ -0,0 +1,222 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+
+        # Changing field 'Domain.natural_key'
+        db.alter_column(u'hdabo_domain', 'natural_key', self.gf('django.db.models.fields.CharField')(default='_', unique=True, max_length=512))
+
+        # Changing field 'TimePeriod.natural_key'
+        db.alter_column(u'hdabo_timeperiod', 'natural_key', self.gf('django.db.models.fields.CharField')(default='_', unique=True, max_length=512))
+
+    def backwards(self, orm):
+
+        # Changing field 'Domain.natural_key'
+        db.alter_column(u'hdabo_domain', 'natural_key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=512, null=True))
+
+        # Changing field 'TimePeriod.natural_key'
+        db.alter_column(u'hdabo_timeperiod', 'natural_key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=512, null=True))
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        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'hdabo.author': {
+            'Meta': {'object_name': 'Author'},
+            'firstname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lastname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet': {
+            'Meta': {'object_name': 'Datasheet'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Author']", 'null': 'True', 'blank': 'True'}),
+            'college_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'college_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'domains': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_domains']", 'to': u"orm['hdabo.Domain']"}),
+            'format': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.DocumentFormat']", 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'highschool_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'highschool_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_themes']", 'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'manual_order': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'modification_datetime': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Organisation']", 'null': 'True'}),
+            'original_creation_date': ('django.db.models.fields.DateField', [], {}),
+            'original_modification_date': ('django.db.models.fields.DateField', [], {}),
+            'primary_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'primary_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Tag']", 'through': u"orm['hdabo.TaggedSheet']", 'symmetrical': 'False'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+            'town': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Location']", 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'validation_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'validator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.User']", 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet_college_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_college_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_domains': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_domains'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_highschool_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_highschool_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_primary_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_primary_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.documentformat': {
+            'Meta': {'object_name': 'DocumentFormat'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.domain': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'Domain'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.folder': {
+            'Meta': {'object_name': 'Folder'},
+            'datasheets': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Datasheet']", 'symmetrical': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '2048'})
+        },
+        u'hdabo.location': {
+            'Meta': {'object_name': 'Location'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '5'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
+        },
+        u'hdabo.organisation': {
+            'Meta': {'object_name': 'Organisation'},
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'website': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tag': {
+            'Meta': {'unique_together': "(('label', 'original_label', 'url_status'),)", 'object_name': 'Tag'},
+            'alias': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TagCategory']", 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dbpedia_uri': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '7168', 'db_index': 'True'}),
+            'normalized_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'original_label': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+            'popularity': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'url_status': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tagcategory': {
+            'Meta': {'object_name': 'TagCategory'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.taggedsheet': {
+            'Meta': {'object_name': 'TaggedSheet'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'index_note': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'db_index': 'True'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'original_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Tag']"}),
+            'wikipedia_revision_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.timeperiod': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'TimePeriod'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.user': {
+            'Meta': {'object_name': 'User', 'db_table': "'auth_user'"},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        }
+    }
+
+    complete_apps = ['hdabo']
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hdabo/migrations/0015_sanitize_organisation_hda_id.py	Wed Feb 18 01:53:34 2015 +0100
@@ -0,0 +1,220 @@
+# -*- coding: utf-8 -*-
+from south.v2 import DataMigration
+
+from hdabo import utils
+
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        total_line = orm.Organisation.objects.count()
+        for i,org in enumerate(orm.Organisation.objects.all()):
+            utils.show_progress(i+1, total_line, "Processing %r " % org.hda_id, 50)
+            new_hda_id = utils.sanitize(org.hda_id)
+            if not orm.Organisation.objects.filter(hda_id=new_hda_id).exists():
+                org.hda_id = new_hda_id
+                org.save() 
+
+    def backwards(self, orm):
+        pass
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        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'hdabo.author': {
+            'Meta': {'object_name': 'Author'},
+            'firstname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lastname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet': {
+            'Meta': {'object_name': 'Datasheet'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Author']", 'null': 'True', 'blank': 'True'}),
+            'college_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'college_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'domains': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_domains']", 'to': u"orm['hdabo.Domain']"}),
+            'format': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.DocumentFormat']", 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'highschool_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'highschool_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_themes']", 'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'manual_order': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'modification_datetime': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Organisation']", 'null': 'True'}),
+            'original_creation_date': ('django.db.models.fields.DateField', [], {}),
+            'original_modification_date': ('django.db.models.fields.DateField', [], {}),
+            'primary_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'primary_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Tag']", 'through': u"orm['hdabo.TaggedSheet']", 'symmetrical': 'False'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+            'town': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Location']", 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'validation_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'validator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.User']", 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet_college_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_college_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_domains': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_domains'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_highschool_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_highschool_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_primary_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_primary_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.documentformat': {
+            'Meta': {'object_name': 'DocumentFormat'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.domain': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'Domain'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.folder': {
+            'Meta': {'object_name': 'Folder'},
+            'datasheets': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Datasheet']", 'symmetrical': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '2048'})
+        },
+        u'hdabo.location': {
+            'Meta': {'object_name': 'Location'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '5'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
+        },
+        u'hdabo.organisation': {
+            'Meta': {'object_name': 'Organisation'},
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'website': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tag': {
+            'Meta': {'unique_together': "(('label', 'original_label', 'url_status'),)", 'object_name': 'Tag'},
+            'alias': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TagCategory']", 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dbpedia_uri': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '7168', 'db_index': 'True'}),
+            'normalized_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'original_label': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+            'popularity': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'url_status': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tagcategory': {
+            'Meta': {'object_name': 'TagCategory'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.taggedsheet': {
+            'Meta': {'object_name': 'TaggedSheet'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'index_note': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'db_index': 'True'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'original_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Tag']"}),
+            'wikipedia_revision_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.timeperiod': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'TimePeriod'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.user': {
+            'Meta': {'object_name': 'User', 'db_table': "'auth_user'"},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        }
+    }
+
+    complete_apps = ['hdabo']
+    symmetrical = True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hdabo/migrations/0016_auto__add_field_tagcategory_natural_key.py	Wed Feb 18 01:53:34 2015 +0100
@@ -0,0 +1,219 @@
+# -*- coding: utf-8 -*-
+from south.db import db
+from south.v2 import SchemaMigration
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding field 'TagCategory.natural_key'
+        db.add_column(u'hdabo_tagcategory', 'natural_key',
+                      self.gf('django.db.models.fields.CharField')(max_length=512, null=True, blank=True),
+                      keep_default=False)
+
+
+    def backwards(self, orm):
+        # Deleting field 'TagCategory.natural_key'
+        db.delete_column(u'hdabo_tagcategory', 'natural_key')
+
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        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'hdabo.author': {
+            'Meta': {'object_name': 'Author'},
+            'firstname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lastname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet': {
+            'Meta': {'object_name': 'Datasheet'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Author']", 'null': 'True', 'blank': 'True'}),
+            'college_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'college_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'domains': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_domains']", 'to': u"orm['hdabo.Domain']"}),
+            'format': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.DocumentFormat']", 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'highschool_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'highschool_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_themes']", 'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'manual_order': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'modification_datetime': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Organisation']", 'null': 'True'}),
+            'original_creation_date': ('django.db.models.fields.DateField', [], {}),
+            'original_modification_date': ('django.db.models.fields.DateField', [], {}),
+            'primary_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'primary_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Tag']", 'through': u"orm['hdabo.TaggedSheet']", 'symmetrical': 'False'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+            'town': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Location']", 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'validation_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'validator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.User']", 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet_college_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_college_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_domains': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_domains'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_highschool_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_highschool_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_primary_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_primary_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.documentformat': {
+            'Meta': {'object_name': 'DocumentFormat'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.domain': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'Domain'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.folder': {
+            'Meta': {'object_name': 'Folder'},
+            'datasheets': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Datasheet']", 'symmetrical': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '2048'})
+        },
+        u'hdabo.location': {
+            'Meta': {'object_name': 'Location'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '5'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
+        },
+        u'hdabo.organisation': {
+            'Meta': {'object_name': 'Organisation'},
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'website': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tag': {
+            'Meta': {'unique_together': "(('label', 'original_label', 'url_status'),)", 'object_name': 'Tag'},
+            'alias': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TagCategory']", 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dbpedia_uri': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '7168', 'db_index': 'True'}),
+            'normalized_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'original_label': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+            'popularity': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'url_status': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tagcategory': {
+            'Meta': {'object_name': 'TagCategory'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.taggedsheet': {
+            'Meta': {'object_name': 'TaggedSheet'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'index_note': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'db_index': 'True'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'original_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Tag']"}),
+            'wikipedia_revision_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.timeperiod': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'TimePeriod'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.user': {
+            'Meta': {'object_name': 'User', 'db_table': "'auth_user'"},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        }
+    }
+
+    complete_apps = ['hdabo']
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hdabo/migrations/0017_calculate_category_natural_key.py	Wed Feb 18 01:53:34 2015 +0100
@@ -0,0 +1,223 @@
+# -*- coding: utf-8 -*-
+from south.v2 import DataMigration
+
+from hdabo import utils
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        "Write your forwards methods here."
+        # Note: Don't use "from appname.models import ModelName". 
+        # Use orm.ModelName to refer to models in this application,
+        # and orm['appname.ModelName'] for models in other applications.
+        total_line = orm.TagCategory.objects.count()
+        for i,cat in enumerate(orm.TagCategory.objects.all()):
+            utils.show_progress(i+1, total_line, "Processing %r " % cat.label, 50)
+            cat.natural_key = utils.sanitize(cat.label)
+            cat.save()
+
+
+    def backwards(self, orm):
+        orm.TagCategory.objects.all().update(natural_key=None)
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        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'hdabo.author': {
+            'Meta': {'object_name': 'Author'},
+            'firstname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lastname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet': {
+            'Meta': {'object_name': 'Datasheet'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Author']", 'null': 'True', 'blank': 'True'}),
+            'college_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'college_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'domains': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_domains']", 'to': u"orm['hdabo.Domain']"}),
+            'format': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.DocumentFormat']", 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'highschool_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'highschool_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_themes']", 'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'manual_order': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'modification_datetime': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Organisation']", 'null': 'True'}),
+            'original_creation_date': ('django.db.models.fields.DateField', [], {}),
+            'original_modification_date': ('django.db.models.fields.DateField', [], {}),
+            'primary_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'primary_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Tag']", 'through': u"orm['hdabo.TaggedSheet']", 'symmetrical': 'False'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+            'town': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Location']", 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'validation_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'validator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.User']", 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet_college_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_college_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_domains': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_domains'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_highschool_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_highschool_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_primary_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_primary_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.documentformat': {
+            'Meta': {'object_name': 'DocumentFormat'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.domain': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'Domain'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.folder': {
+            'Meta': {'object_name': 'Folder'},
+            'datasheets': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Datasheet']", 'symmetrical': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '2048'})
+        },
+        u'hdabo.location': {
+            'Meta': {'object_name': 'Location'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '5'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
+        },
+        u'hdabo.organisation': {
+            'Meta': {'object_name': 'Organisation'},
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'website': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tag': {
+            'Meta': {'unique_together': "(('label', 'original_label', 'url_status'),)", 'object_name': 'Tag'},
+            'alias': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TagCategory']", 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dbpedia_uri': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '7168', 'db_index': 'True'}),
+            'normalized_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'original_label': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+            'popularity': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'url_status': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tagcategory': {
+            'Meta': {'object_name': 'TagCategory'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.taggedsheet': {
+            'Meta': {'object_name': 'TaggedSheet'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'index_note': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'db_index': 'True'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'original_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Tag']"}),
+            'wikipedia_revision_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.timeperiod': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'TimePeriod'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.user': {
+            'Meta': {'object_name': 'User', 'db_table': "'auth_user'"},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        }
+    }
+
+    complete_apps = ['hdabo']
+    symmetrical = True
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/hdabo/migrations/0018_auto__chg_field_tagcategory_natural_key__add_index_tagcategory_natural.py	Wed Feb 18 01:53:34 2015 +0100
@@ -0,0 +1,225 @@
+# -*- 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 'TagCategory.natural_key'
+        db.alter_column(u'hdabo_tagcategory', 'natural_key', self.gf('django.db.models.fields.CharField')(default='_', max_length=512))
+        # Adding index on 'TagCategory', fields ['natural_key']
+        db.create_index(u'hdabo_tagcategory', ['natural_key'])
+
+
+    def backwards(self, orm):
+        # Removing index on 'TagCategory', fields ['natural_key']
+        db.delete_index(u'hdabo_tagcategory', ['natural_key'])
+
+
+        # Changing field 'TagCategory.natural_key'
+        db.alter_column(u'hdabo_tagcategory', 'natural_key', self.gf('django.db.models.fields.CharField')(max_length=512, null=True))
+
+    models = {
+        u'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        u'auth.permission': {
+            'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        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'hdabo.author': {
+            'Meta': {'object_name': 'Author'},
+            'firstname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lastname': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet': {
+            'Meta': {'object_name': 'Datasheet'},
+            'author': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Author']", 'null': 'True', 'blank': 'True'}),
+            'college_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'college_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'college_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_college_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            'domains': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_domains']", 'to': u"orm['hdabo.Domain']"}),
+            'format': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.DocumentFormat']", 'null': 'True', 'blank': 'True'}),
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'highschool_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'highschool_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'highschool_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_highschool_themes']", 'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'manual_order': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'modification_datetime': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
+            'organisation': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Organisation']", 'null': 'True'}),
+            'original_creation_date': ('django.db.models.fields.DateField', [], {}),
+            'original_modification_date': ('django.db.models.fields.DateField', [], {}),
+            'primary_periods': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_periods_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_periods']", 'to': u"orm['hdabo.TimePeriod']"}),
+            'primary_themes': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'primary_themes_datasheets'", 'symmetrical': 'False', 'through': u"orm['hdabo.Datasheet_primary_themes']", 'to': u"orm['hdabo.Domain']"}),
+            'tags': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Tag']", 'through': u"orm['hdabo.TaggedSheet']", 'symmetrical': 'False'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048'}),
+            'town': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Location']", 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'validated': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'db_index': 'True'}),
+            'validation_date': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
+            'validator': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.User']", 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.datasheet_college_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_college_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_college_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_domains': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_domains'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_highschool_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_highschool_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_highschool_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.datasheet_primary_periods': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_periods'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {}),
+            'timeperiod': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TimePeriod']"})
+        },
+        u'hdabo.datasheet_primary_themes': {
+            'Meta': {'ordering': "['sort_value']", 'object_name': 'Datasheet_primary_themes'},
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            'domain': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Domain']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'sort_value': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.documentformat': {
+            'Meta': {'object_name': 'DocumentFormat'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'})
+        },
+        u'hdabo.domain': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'Domain'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.folder': {
+            'Meta': {'object_name': 'Folder'},
+            'datasheets': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['hdabo.Datasheet']", 'symmetrical': 'False'}),
+            'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'url': ('django.db.models.fields.URLField', [], {'unique': 'True', 'max_length': '2048'})
+        },
+        u'hdabo.location': {
+            'Meta': {'object_name': 'Location'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'insee': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '5'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'})
+        },
+        u'hdabo.organisation': {
+            'Meta': {'object_name': 'Organisation'},
+            'hda_id': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'location': ('django.db.models.fields.CharField', [], {'max_length': '512', 'null': 'True', 'blank': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'website': ('django.db.models.fields.CharField', [], {'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tag': {
+            'Meta': {'unique_together': "(('label', 'original_label', 'url_status'),)", 'object_name': 'Tag'},
+            'alias': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'alternative_wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            'category': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.TagCategory']", 'null': 'True', 'blank': 'True'}),
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'dbpedia_uri': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '7168', 'db_index': 'True'}),
+            'normalized_label': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'db_index': 'True'}),
+            'original_label': ('django.db.models.fields.CharField', [], {'max_length': '1024'}),
+            'popularity': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'url_status': ('django.db.models.fields.IntegerField', [], {'default': 'None', 'null': 'True', 'db_index': 'True', 'blank': 'True'}),
+            'wikipedia_pageid': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True', 'null': 'True', 'blank': 'True'}),
+            'wikipedia_url': ('django.db.models.fields.URLField', [], {'db_index': 'True', 'max_length': '2048', 'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.tagcategory': {
+            'Meta': {'object_name': 'TagCategory'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'max_length': '512', 'db_index': 'True'})
+        },
+        u'hdabo.taggedsheet': {
+            'Meta': {'object_name': 'TaggedSheet'},
+            'created_at': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'datasheet': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Datasheet']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'index_note': ('django.db.models.fields.FloatField', [], {'default': '0.0', 'db_index': 'True'}),
+            'order': ('django.db.models.fields.IntegerField', [], {'default': '0', 'db_index': 'True'}),
+            'original_order': ('django.db.models.fields.IntegerField', [], {'default': '0'}),
+            'tag': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['hdabo.Tag']"}),
+            'wikipedia_revision_id': ('django.db.models.fields.BigIntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        u'hdabo.timeperiod': {
+            'Meta': {'unique_together': "(('label', 'school_period'),)", 'object_name': 'TimePeriod'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'label': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
+            'natural_key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
+            'school_period': ('django.db.models.fields.IntegerField', [], {})
+        },
+        u'hdabo.user': {
+            'Meta': {'object_name': 'User', 'db_table': "'auth_user'"},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        }
+    }
+
+    complete_apps = ['hdabo']
\ No newline at end of file
--- a/src/hdabo/models.py	Wed Dec 10 10:05:38 2014 +0100
+++ b/src/hdabo/models.py	Wed Feb 18 01:53:34 2015 +0100
@@ -1,10 +1,13 @@
 # -*- coding: utf-8 -*-
 
+import datetime
+
 from django.conf import settings
 from django.contrib.auth.models import AbstractUser
 from django.db import models
+
+from hdabo import utils
 from hdabo.utils import Property, normalize
-import datetime
 
 
 # User Class, due to migration to django 1.6.5
@@ -46,7 +49,8 @@
     }
     label = models.CharField(max_length=512, unique=False, blank=False, null=False)
     school_period = models.IntegerField(choices=TIME_PERIOD_CHOICES)
-    
+    natural_key = models.CharField(max_length=512, unique=True, blank=False, null=False)
+
     objects = SortedModelManager()
     
     class Meta:
@@ -70,6 +74,7 @@
     }
     label = models.CharField(max_length=512, unique=False, blank=False, null=False)
     school_period = models.IntegerField(choices=DOMAIN_PERIOD_CHOICES)
+    natural_key = models.CharField(max_length=512, unique=True, blank=False, null=False)
 
     objects = SortedModelManager()
 
@@ -88,6 +93,7 @@
     
 class TagCategory(models.Model):
     label = models.CharField(max_length=512, unique=True, blank=False, null=False)
+    natural_key = models.CharField(max_length=512, unique=False, blank=False, null=False, db_index=True)
     
     def __unicode__(self):
         return unicode(self.label)
@@ -126,6 +132,14 @@
     url_status = models.IntegerField(choices=TAG_URL_STATUS_CHOICES, blank=True, null=True, default=None, db_index=True)
     dbpedia_uri = models.URLField(max_length=2048, blank=True, null=True, db_index=True)
     popularity = models.IntegerField(blank=False, null=False, default=0, db_index=True)
+    #natural_key = models.CharField(max_length=7168, blank=True, null=True, db_index=True)
+    #TODO: find a proper key. natural key is not really a key.
+    natural_key = models.CharField(max_length=7168, blank=False, null=False, db_index=True)
+    
+
+    def __init__(self, *args, **kwargs):
+        models.Model.__init__(self, *args, **kwargs)
+        self.force_natural_key = False
 
     @Property
     def url_status_text(): #@NoSelf
@@ -133,9 +147,22 @@
             return self.TAG_URL_STATUS_CHOICES[self.url_status][1]
         
         return locals()
+
+    def calculate_natural_key(self):
+        parts = [
+            utils.sanitize(self.label if hasattr(self,'label') else ''),
+            utils.sanitize(self.normalized_label if hasattr(self,'normalized_label') else ''),
+            utils.sanitize(self.original_label if hasattr(self,'original_label') else ''),
+            self.wikipedia_url.split("/")[-1].rstrip('/') if hasattr(self,'wikipedia_url') and self.wikipedia_url else ""
+        ]
+        return ('_'.join(parts))[:7168]
+
     
     def save(self, *args, **kwargs):
-        self.normalized_label = normalize(self.label)
+        if self.label and not self.normalized_label:
+            self._normalized_label = normalize(self.label)
+        if not self.force_natural_key:
+            self.natural_key = self.calculate_natural_key()
         super(Tag, self).save(*args, **kwargs) 
             
     class Meta:
@@ -151,7 +178,7 @@
 
 def generate_m2m_setter(m2m_field_name):
     
-    def set_m2m_field(self, list):
+    def set_m2m_field(self, lst):
         
         m2m_manager = getattr(self, m2m_field_name)
         m2m_manager.clear()
@@ -167,7 +194,7 @@
                     break
         target_obj_field_name = set_m2m_field.cache['target_obj_field_name']
 
-        for i, obj in enumerate(list):
+        for i, obj in enumerate(lst):
             kwargs = {
                 'datasheet': self,
                 'sort_value' : i,
--- a/src/hdabo/utils.py	Wed Dec 10 10:05:38 2014 +0100
+++ b/src/hdabo/utils.py	Wed Feb 18 01:53:34 2015 +0100
@@ -1,9 +1,13 @@
 # -*- coding: utf-8 -*-
+import codecs
 import collections
+import math
+import re
+import sys
 import unicodedata
-import sys
-import math
-import codecs
+
+import unidecode
+
 
 ###
 # allow to declare a property as a decorator
@@ -342,12 +346,27 @@
         return ItemsView(self)
 ## end of http://code.activestate.com/recipes/576693/ }}}
 
-def remove_accents(str):
-    nkfd_form = unicodedata.normalize('NFKD', unicode(str))
+def remove_accents(lne):
+    nkfd_form = unicodedata.normalize('NFKD', unicode(lne))
     return u"".join([c for c in nkfd_form if not unicodedata.combining(c)])
 
-def normalize(str):
-    return remove_accents(str).lower().replace(u"œ",u"oe")
+def normalize(lne):
+    return remove_accents(lne).lower().replace(u"œ",u"oe")
+
+def sanitize(line, separator = '-', ascii_only = True):
+
+    if not line:
+        return ''
+    
+    #Transliterate non-ASCII characters
+    line =  unidecode.unidecode(line)
+    #Remove all characters that are not the separator, a-z, 0-9, or whitespace
+    line = re.sub('[^\%sa-z0-9\s]+'%separator, '', line.lower())
+    #// Replace all separator characters and whitespace by a single separator
+    line = re.sub('[\%s\s]+' % separator, separator, line)
+
+    return line.strip(separator)
+
 
 def show_progress(current_line, total_line, label, width, writer=None):
 
--- a/src/hdalab/management/commands/import_hda_insee_csv.py	Wed Dec 10 10:05:38 2014 +0100
+++ b/src/hdalab/management/commands/import_hda_insee_csv.py	Wed Feb 18 01:53:34 2015 +0100
@@ -2,14 +2,14 @@
 '''
 @author: raphv
 '''
-from django.core.management.base import BaseCommand, CommandError
-import django.utils.simplejson as json
-from SPARQLWrapper import SPARQLWrapper, JSON
-from hdalab.models import InseeCoords, DatasheetExtras
-from hdabo.models import Datasheet
 import csv
 import re
-import sys
+
+from django.core.management.base import BaseCommand, CommandError
+
+from hdabo.models import Datasheet
+from hdalab.models import InseeCoords, DatasheetExtras
+
 
 class Command(BaseCommand):
     '''
--- a/src/hdalab/management/commands/import_hdabo_db.py	Wed Dec 10 10:05:38 2014 +0100
+++ b/src/hdalab/management/commands/import_hdabo_db.py	Wed Feb 18 01:53:34 2015 +0100
@@ -33,10 +33,10 @@
         call_command('syncdb', migrate=True)
         if options.get('categories', False):
             print("===========  QUERY WIKIPEDIA CATEGORY ===========")
-            call_command('query_wikipedia_category', interactive=False, force=True)
+            call_command('query_wikipedia_category', interactive=False, force=True, all=True)
 
         print("===========  QUERY DBPEDIA ===========")                
-        call_command('query_dbpedia', interactive=False, force=True)
+        call_command('query_dbpedia', interactive=False, force=True, all=True)
         print("===========  FILL TAG YEAR ===========")
         call_command('fill_tag_years')
         print("===========  GEOJSON TRANSFORM ===========")
--- a/src/hdalab/views/ajax.py	Wed Dec 10 10:05:38 2014 +0100
+++ b/src/hdalab/views/ajax.py	Wed Feb 18 01:53:34 2015 +0100
@@ -352,6 +352,8 @@
                         
         cont_count = contentqs.count()
         
+        logger.debug("ajax filter SQL for contentqs %s", contentqs.query)
+        
         contenus = dict([(content.id, {'score' : 0, 'tags' : [], 'hda_id': content.hda_id, 'id':content.id, 'title': content.title, 'description': content.description, 'url': content.url}) for content in contentqs[0:content_count]])
         contentids = contenus.keys()
         
--- a/virtualenv/res/lib/lib_create_env.py	Wed Dec 10 10:05:38 2014 +0100
+++ b/virtualenv/res/lib/lib_create_env.py	Wed Feb 18 01:53:34 2015 +0100
@@ -30,7 +30,7 @@
     'WHOOSH': {'setup': 'whoosh', 'url':'https://pypi.python.org/packages/source/W/Whoosh/Whoosh-2.5.7.tar.gz', 'local':'whoosh-2.5.7.tar.gz', 'install' : {'method':'pip', 'option_str': None, 'dict_extra_env': None}},
     'WIKITOOLS' : { 'setup': 'wikitools', 'url': 'https://github.com/alexz-enwp/wikitools/archive/1.2.tar.gz', 'local': 'wikitools-1.2.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
     'ISODATE' : {'setup': 'isodate', 'url': 'http://pypi.python.org/packages/source/i/isodate/isodate-0.5.1.tar.gz', 'local': 'isodate-0.5.1.tar.gz', 'install' : {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
-    'RDFLIB' : { 'setup': 'rdflib', 'url': 'https://github.com/RDFLib/rdflib/archive/96c30f98bbb628e13aaa32c9c392584b0fbf8788.tar.gz', 'local': 'rdflib-4.2-dev.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
+    'RDFLIB' : { 'setup': 'rdflib', 'url': 'https://github.com/IRI-Research/rdflib/archive/30f0f8ca7dba3e9dbfef39fb2006e4b395748f6c.tar.gz', 'local': 'rdflib-4.2.0-dev.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
     'SPARQLWRAPPER' : { 'setup': 'SPARQLWrapper', 'url': 'https://github.com/RDFLib/sparqlwrapper/archive/1.6.4.tar.gz', 'local' : 'SPARQLWrapper-1.6.4.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},    
     'REQUESTS' : { 'setup': 'requests', 'url': 'https://github.com/kennethreitz/requests/archive/v2.4.3.tar.gz', 'local' : 'requests-2.4.3.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
     'ELASTICSEARCH' : { 'setup': 'elasticsearch', 'url': 'https://github.com/elasticsearch/elasticsearch-py/archive/1.2.0.tar.gz', 'local' : 'elasticsearch-1.2.0.tar.gz', 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
@@ -48,6 +48,8 @@
     'RASQAL': { 'setup': 'rasqal', 'url':'rasqal-0.9.32.tar.gz', 'local':"rasqal-0.9.32.tar.gz", 'install': {'method': 'install_rasqal', 'option_str': None, 'dict_extra_env': None}},
     'REDLAND': { 'setup': 'redland', 'url':'redland-1.0.17.tar.gz', 'local':"redland-1.0.17.tar.gz", 'install': {'method': 'install_redland', 'option_str': None, 'dict_extra_env': None}},
     'REDLAND_BINDINGS': { 'setup': 'redland_bindings', 'url':'redland-bindings-1.0.17.1.tar.gz', 'local':"redland-bindings-1.0.17.1.tar.gz", 'install': {'method': 'install_redland_bindings', 'option_str': None, 'dict_extra_env': None}},
+    'UNIDECODE': { 'setup': 'unidecode', 'url':'https://pypi.python.org/packages/source/U/Unidecode/Unidecode-0.04.17.tar.gz', 'local':"Unidecode-0.04.17.tar.gz", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
+    'PYTZ': { 'setup': 'pytz', 'url':'https://pypi.python.org/packages/source/p/pytz/pytz-2014.10.tar.bz2', 'local':"pytz-2014.10.tar.bz2", 'install': {'method': 'pip', 'option_str': None, 'dict_extra_env': None}},
 }
 
 
Binary file virtualenv/res/src/Unidecode-0.04.17.tar.gz has changed
Binary file virtualenv/res/src/pytz-2014.10.tar.bz2 has changed
Binary file virtualenv/res/src/rdflib-4.2-dev.tar.gz has changed
Binary file virtualenv/res/src/rdflib-4.2.0-dev.tar.gz has changed
--- a/virtualenv/web/res/requirements.txt	Wed Dec 10 10:05:38 2014 +0100
+++ b/virtualenv/web/res/requirements.txt	Wed Feb 18 01:53:34 2015 +0100
@@ -26,4 +26,6 @@
 urllib3==1.9.1
 wikitools==1.2
 wsgiref==0.1.2
+Unidecode==0.04.17
+pytz==2014.10