Procházet zdrojové kódy

Revue des modèles

olivier.massot před 7 roky
rodič
revize
ec8dcf0ec7

+ 29 - 4
core/cerberus_extend.py

@@ -3,18 +3,36 @@
 @author: olivier.massot, 2018
 '''
 from datetime import datetime
+import locale
 import re
 
 import cerberus
+import chardet
 
 from core import gis
 
+
 def is_french_date(field, value, error):
     try:
         datetime.strptime(value, '%d/%m/%Y')
     except:
         error(field, 'Doit être une date au format jj/mm/aaaa')
 
+def is_int(field, value, error):
+    try:
+        if  float(value) != int(value):
+            error(field, 'Doit être un nombre entier: {}'.format(value))
+    except ValueError:
+        error(field, 'Doit être un nombre entier: {}'.format(value))
+
+def is_float(field, value, error):
+    value = locale.atof(str(value))
+    try:
+        if  value != float(value):
+            error(field, 'Doit être un nombre décimal ({})'.format(value))
+    except ValueError:
+        error(field, 'Doit être un nombre décimal ({})'.format(value))
+
 # Ref: http://docs.python-cerberus.org/en/stable/api.html#error-codes
 
 class GeoValidator(cerberus.validator.Validator):
@@ -59,7 +77,14 @@ class GeoValidator(cerberus.validator.Validator):
                 self._error(field, "Certaines coordonnées hors de l'emprise autorisée")
         except AttributeError:
             pass
-            
+
+
+def _translate_messages(message):
+    message = message.replace("string", "texte")
+    message = message.replace("integer", "nombre entier")
+    message = message.replace("float", "nombre décimal")
+    message = message.replace("boolean", "booléen")
+    return message
 
 class CerberusErrorHandler(cerberus.errors.BasicErrorHandler):
     messages = {0x00: "{0}",
@@ -72,10 +97,10 @@ class CerberusErrorHandler(cerberus.errors.BasicErrorHandler):
                 0x06: "{0} ne doit pas être présent avec '{field}'",
 
                 0x21: "'{0}' is not a document, must be a dict",
-                0x22: "Le champs ne peut pas être vide",
+                0x22: "Le champs doit être renseigné",
                 0x23: "Les valeurs NULL ne sont pas autorisées",
-                0x24: "Doit être du type: {constraint}",
-                0x25: "Doit être de type 'dictionnaire'",
+                0x24: "Doit être du type {constraint}: {value}",
+                0x25: "Doit être de type 'dictionnaire': {value}",
                 0x26: "La longueur de la liste doit être de {constraint}, elle est de {0}",
                 0x27: "La longueur minimum du champs est de {constraint}",
                 0x28: "Trop long, la longueur max. du champs est de {constraint}",

+ 7 - 4
core/validation.py

@@ -9,9 +9,11 @@ import zipfile
 from path import Path, TempDir
 
 from core import gis
-from core.cerberus_extend import CerberusErrorHandler, GeoValidator
+from core.cerberus_extend import CerberusErrorHandler, GeoValidator, \
+    _translate_messages
 from schemas.common import SRID
 
+
 class BaseModel():
     filename = ""
     pk = ""
@@ -160,7 +162,7 @@ class BaseValidator():
     def _structure_validation(self):
         
         for model in self.models:
-            v = GeoValidator(model.schema, error_handler=CerberusErrorHandler)
+            v = GeoValidator(model.schema, purge_unknown=True, error_handler=CerberusErrorHandler)
             
             for item in self.dataset[model]:
 
@@ -168,17 +170,18 @@ class BaseValidator():
             
                 for field, verrors in v.errors.items():
                     for err in verrors:
-                        self.log_error(DataError(err, filename=model.filename, field=field))
+                        self.log_error(DataError(_translate_messages(err), filename=model.filename, field=field))
     
     @classmethod
     def _technical_validation(cls):
         raise NotImplementedError()
     
+    
     def build_report(self, schema, filename):
         report = {}
         report["schema"] = schema
         report["filename"] = filename
-        report["exec_time"] = "{} s.".format(self.dt)
+        report["exec_time"] = "{:.3g} s.".format(self.dt)
         report["checkpoints"] = [{"name": chk.name, "valid": chk.valid} for chk in self.checkpoints]
         
         report["errors"] = {}

+ 1 - 1
requirements.txt

@@ -4,4 +4,4 @@ pyyaml
 pyshp
 Jinja2
 Flask
-cerberus
+cerberus

+ 10 - 2
resources/questions_20180924.txt

@@ -3,7 +3,7 @@ Questions:
 données alphanum:
 
 * Netgeo v12 et 22 -> Auters différences que les champs en plus et les listes de choix? NON
-* Listes à choix multiples: quelles listes? Artres: diamètres, types de fourreau (PVC, PHD...), proprios, gestionnaires
+* Listes à choix multiples: quelles listes? Artères: diamètres, types de fourreau (PVC, PHD...), proprios, gestionnaires
 * Listes avec saisie libre? Si 'Autres'
 * Quels champs peuvent rester vide? (comment, ?): Certains champs vides si artere existant (à voir): date_in, date_reception, ref_planche, source georeferencement (idem nour les noeuds)
 peuvent etre vide: noeud.NO_HAUT
@@ -28,4 +28,12 @@ Autre:
    > Grace THD
    > Compléments?
    
-   
+   
+# Mods
+
+Ajouter erdf en gestionnaire / propriétaire aux artères et noeuds
+AR_PRO_MD: si vide, égal à 'Mache num'
+CA_GEST: ajouter MANCHE FIBRE à la liste
+Gestionnaires / Proprios: ajouter 'PRIVE'
+
+x Champs numériques: préciser l'erreur lorsque le champs est vide

+ 2 - 0
schemas/netgeo_1_12_doe/__init__.py

@@ -0,0 +1,2 @@
+
+from . import *

+ 133 - 0
schemas/netgeo_1_12_doe/models.py

@@ -0,0 +1,133 @@
+'''
+
+@author: olivier.massot, 2018
+'''
+from core import gis
+from core.cerberus_extend import is_french_date, is_int, is_float
+from core.validation import BaseGeoModel
+from schemas.common import INSEE_VALIDES, XMIN, YMIN, XMAX, YMAX
+
+
+class Artere(BaseGeoModel):
+    filename = "artere_geo.shp"
+    schema = {'geom': {'geometry': (gis.POLYLINE, (XMIN, YMIN, XMAX, YMAX))}, 
+              'AR_ID_INSE': {'type': 'string', 'empty': False, 'allowed': INSEE_VALIDES}, 
+              'AR_LONG': {'empty': False, 'validator': is_float},
+              'AR_ETAT': {'type': 'string', 'empty': False, 'allowed': ['0', '1', '2', '3', '4']}, 
+              'AR_OCCP': {'type': 'string', 'empty': False, 'allowed': ['0', '1.1', '1.2', '2', '3', '4']}, 
+              'AR_NOEUD_A': {'type': 'string', 'maxlength': 20}, 
+              'AR_NOEUD_B': {'type': 'string', 'maxlength': 20}, 
+              'AR_NB_FOUR': {'type': 'string', 'maxlength': 10}, 
+              'AR_FOU_DIS': {'empty': False, 'validator': is_int}, 
+              'AR_TYPE_FO': {'type': 'string', 'empty': False, 'multiallowed': ['PVC', 'PEHD', 'SOUS-TUBAGE PEHD', 'SOUS-TUBAGE  SOUPLE', 'FACADE', 'AERIEN', 'ENCORBELLEMENT', 'AUTRE']}, 
+              'AR_DIAM_FO': {'type': 'string', 'empty': False, 'multiallowed': ['10', '14', '18', '25', '28', '32', '40', '45', '60', 'NUL']}, 
+              'AR_PRO_FOU': {'type': 'string', 'empty': False, 'multiallowed': ['MANCHE NUMERIQUE', 'COLLECTIVITE', 'ORANGE', 'AUTRE (à préciser)']}, 
+              'AR_PRO_CAB': {'type': 'string', 'empty': False, 'allowed': ['MANCHE NUMERIQUE']}, 
+              'AR_GEST_FO': {'type': 'string', 'empty': False, 'multiallowed': ['MANCHE NUMERIQUE', 'MANCHE TELECOM', 'COLLECTIVITE', 'ORANGE', 'MANCHE FIBRE', 'AUTRE (à préciser)', 'NUL']}, 
+              'AR_UTIL_FO': {'type': 'string', 'empty': False, 'multiallowed': ['MANCHE NUMERIQUE', 'MANCHE TELECOM', 'COLLECTIVITE', 'ORANGE', 'AUTRE (à préciser)', 'NUL']}, 
+              'AR_DATE_IN': {'empty': False, 'validator': is_french_date}, 
+              'AR_DATE_RE': {'empty': False, 'validator': is_french_date}, 
+              'AR_REF_PLA': {'type': 'string', 'maxlength': 100}, 
+              'AR_SRC_GEO': {'type': 'string', 'maxlength': 50}, 
+              'AR_QLT_GEO': {'type': 'string', 'empty': False, 'allowed': ['A', 'B', 'C']}, 
+              'AR_PRO_MD': {'type': 'string', 'empty': False, 'allowed': ['MANCHE NUMERIQUE']}, 
+              'AR_COMMENT': {'type': 'string', 'maxlength': 300, 'empty': True}, 
+              'AR_STATUT': {'type': 'string', 'empty': False, 'allowed': ['APS', 'APD', 'EXE', 'REC']}}
+
+class Cable(BaseGeoModel):
+    filename = "cable_geo.shp"
+    schema = {'geom': {'geometry': (gis.POLYLINE, (XMIN, YMIN, XMAX, YMAX))}, 
+              'CA_NUMERO': {'type': 'string', 'maxlength': 17}, 
+              'CA_TYPE': {'type': 'string', 'maxlength': 10, 'empty': False, 'allowed': ['AERIEN', 'IMMEUBLE', 'FACADE', 'MIXTE', 'SOUTERRAIN']}, 
+              'CA_ETAT': {'type': 'string', 'maxlength': 1, 'empty': False, 'allowed': ['0', '1', '2', '3', '4']}, 
+              'CA_LONG': {'validator': is_float}, 
+              'CA_EQ_A': {'type': 'string', 'maxlength': 18}, 
+              'CA_EQ_B': {'type': 'string', 'maxlength': 18}, 
+              'CA_DIAMETR': {'empty': False, 'validator': is_float}, 
+              'CA_COULEUR': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['NOIR', 'BLEU', 'BLANC']}, 
+              'CA_TECHNOL': {'type': 'string', 'maxlength': 17, 'empty': False, 'allowed': ['G657A2_M6', 'G657A2_M12']}, 
+              'CA_NB_FO': {'validator': is_int}, 
+              'CA_NB_FO_U': {'empty': False, 'validator': is_int}, 
+              'CA_NB_FO_D': {'empty': False, 'validator': is_int}, 
+              'CA_PRO': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['MANCHE NUMERIQUE']}, 
+              'CA_GEST': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['NUL']}, 
+              'CA_DATE_IN': {'empty': False, 'validator': is_french_date}, 
+              'CA_COMMENT': {'type': 'string', 'maxlength': 300, 'empty': True}, 
+              'CA_STATUT': {'type': 'string', 'maxlength': 14, 'empty': False, 'allowed': ['APS', 'APD', 'EXE', 'REC']}}
+
+class Equipement(BaseGeoModel):
+    filename = "equipement_passif.shp"
+    schema = {'geom': {'geometry': (gis.POINT, (XMIN, YMIN, XMAX, YMAX))}, 
+              'EQ_NOM': {'type': 'string', 'maxlength': 10}, 
+              'EQ_NOM_NOE': {'type': 'string', 'maxlength': 14}, 
+              'EQ_ETAT': {'type': 'string', 'maxlength': 1, 'empty': False, 'allowed': ['0', '1', '2', '3', '4']}, 
+              'EQ_OCCP': {'type': 'string', 'maxlength': 3, 'empty': False, 'allowed': ['0', '1.1', '1.2', '2', '3', '4']}, 
+              'EQ_TYPE': {'type': 'string', 'empty': False, 'allowed': ['PBO', 'PBOE', 'BPE', 'BAI']}, 
+              'EQ_TYPE_LQ': {'type': 'string', 'maxlength': 6, 'empty': False, 'allowed': ['PBO', 'BPE JB', 'BPE JD', 'BAIDC', 'BAIOP']}, 
+              'EQ_TYPE_PH': {'type': 'string', 'maxlength': 24, 'empty': False, 'allowed': ['PBO 6', 'PBO 12', 'BPE 12EP', 'BPE 24EP', 'BPE 48EP', 'BPE 72EP', 'BPE 96EP', 'BPE 144EP', 'BPE 288EP', 'BPE 576EP', 'BPE 720EP', 'BAI']}, 
+              'EQ_PRO': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['MANCHE NUMERIQUE', 'COLLECTIVITE', 'ORANGE', 'AUTRE (à préciser)', 'NUL']}, 
+              'EQ_GEST': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['MANCHE NUMERIQUE', 'MANCHE TELECOM', 'COLLECTIVITE', 'ORANGE', 'MANCHE FIBRE', 'AUTRE (à préciser)', 'NUL']}, 
+              'EQ_HAUT': {'empty': False, 'validator': is_float}, 
+              'EQ_DATE_IN': {'empty': False, 'validator': is_french_date}, 
+              'EQ_COMMENT': {'type': 'string', 'maxlength': 300, 'empty': True}, 
+              'EQ_STATUT': {'type': 'string', 'maxlength': 14, 'empty': False, 'allowed': ['APS', 'APD', 'EXE', 'REC']}}
+
+class Noeud(BaseGeoModel):
+    filename = "noeud_geo.shp"
+    schema = {'geom': {'geometry': (gis.POINT, (XMIN, YMIN, XMAX, YMAX))},
+              'NO_NOM': {'type': 'string', 'maxlength': 20}, 
+              'NO_ID_INSE': {'type': 'string', 'empty': False, 'allowed': INSEE_VALIDES}, 
+              'NO_VOIE': {'type': 'string', 'maxlength': 100}, 
+              'NO_ETAT': {'type': 'string', 'maxlength': 1, 'empty': False, 'allowed': ['0', '1', '2', '3', '4']}, 
+              'NO_OCCP': {'type': 'string', 'maxlength': 3, 'empty': False, 'allowed': ['0', '1.1', '1.2', '2', '3', '4']}, 
+              'NO_TYPE': {'type': 'string', 'maxlength': 3, 'empty': False, 'allowed': ['CHA', 'POT', 'LTE', 'SEM', 'FAC', 'OUV', 'IMM']}, 
+              'NO_TYPE_LQ': {'type': 'string', 'maxlength': 10, 'empty': False, 'allowed': ['CHTIR', 'CHRACC', 'POT', 'NRO', 'PM', 'MIMO', 'FAC', 'OUV', 'IMM']}, 
+              'NO_TYPE_PH': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['CHAMBRE', 'POTEAU', 'ARMOIRE', 'SHELTER', 'BATIMENT', 'SITE MIMO', 'FACADE', 'OUVRAGE', 'IMMEUBLE']}, 
+              'NO_CODE_PH': {'type': 'string', 'maxlength': 20}, 
+              'NO_TECH_PS': {'type': 'string', 'maxlength': 4, 'empty': False, 'multiallowed': ['COAX', 'CUT', 'ECL', 'ELEC', 'VP', 'OPT', 'NC']}, 
+              'NO_AMO': {'type': 'string', 'maxlength': 20}, 
+              'NO_PLINOX': {'type': 'string', 'maxlength': 3, 'empty': False, 'allowed': ['OUI', 'NON']}, 
+              'NO_X': {'empty': False, 'validator': is_float}, 
+              'NO_Y': {'empty': False, 'validator': is_float}, 
+              'NO_PRO': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['MANCHE NUMERIQUE', 'COLLECTIVITE', 'ORANGE', 'ERDF', 'AUTRE (à préciser)', 'NUL']}, 
+              'NO_GEST': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['MANCHE NUMERIQUE', 'MANCHE TELECOM', 'COLLECTIVITE', 'ORANGE', 'ERDF', 'MANCHE FIBRE', 'AUTRE (à préciser)', 'NUL']}, 
+              'NO_HAUT': {'empty': False, 'validator': is_float}, 
+              'NO_DATE_IN': {'empty': False, 'validator': is_french_date}, 
+              'NO_REF_PLA': {'type': 'string', 'maxlength': 100}, 
+              'NO_SRC_GEO': {'type': 'string', 'maxlength': 50}, 
+              'NO_QLT_GEO': {'type': 'string', 'maxlength': 1, 'empty': False, 'allowed': ['A', 'B', 'C']}, 
+              'NO_PRO_MD': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['MANCHE NUMERIQUE']}, 
+              'NO_COMMENT': {'type': 'string', 'maxlength': 300, 'empty': True}, 
+              'NO_STATUT': {'type': 'string', 'maxlength': 14, 'empty': False, 'allowed': ['APS', 'APD', 'EXE', 'REC']}}
+
+class Tranchee(BaseGeoModel):
+    filename = "tranchee_geo.shp"
+    schema = {'geom': {'geometry': (gis.POLYLINE, (XMIN, YMIN, XMAX, YMAX))}, 
+              'TR_ID_INSE': {'type': 'string', 'empty': False, 'allowed': INSEE_VALIDES}, 
+              'TR_VOIE': {'type': 'string', 'maxlength': 200}, 
+              'TR_TYP_IMP': {'type': 'string', 'empty': False, 'allowed': ['ACCOTEMENT STABILISE', 'ACCOTEMENT NON STABILISE', 'CHAUSSEE LOURDE', 'CHAUSSEE LEGERE', 'FOSSE', 'TROTTOIR', 'ESPACE VERT', 'ENCORBELLEMENT']}, 
+              'TR_MOD_POS': {'type': 'string', 'empty': False, 'allowed': ['TRADITIONNEL', 'MICRO TRANCHEE', 'FONCAGE 60', 'FONCAGE 90', 'FONCAGE 120', 'TRANCHEUSE', 'FORAGE URBAIN', 'FORAGE RURAL', 'ENCORBELLEMENT']}, 
+              'TR_LONG': {'empty': False, 'validator': is_float}, 
+              'TR_LARG': {'empty': False, 'validator': is_float}, 
+              'TR_REVET': {'type': 'string', 'empty': False, 'allowed': ['SABLE', 'BICOUCHE', 'ENROBE', 'BETON', 'PAVE', 'TERRAIN NATUREL']}, 
+              'TR_CHARGE': {'empty': False, 'validator': is_float}, 
+              'TR_GRILLAG': {'empty': False, 'validator': is_float}, 
+              'TR_REMBLAI': {'type': 'string'}, 
+              'TR_PLYNOX': {'type': 'string', 'empty': False, 'allowed': ['OUI', 'NON']}, 
+              'TR_PRO_VOI': {'type': 'string', 'empty': False, 'allowed': ['COMMUNE', 'COMMUNAUTE DE COMMUNES', 'DEPARTEMENT', 'ETAT', 'PRIVE']}, 
+              'TR_GEST_VO': {'type': 'string', 'empty': False, 'allowed': ['COMMUNE', 'COMMUNAUTE DE COMMUNES', 'DEPARTEMENT', 'ETAT', 'PRIVE']}, 
+              'TR_SCHEMA': {'maxlength': 100, 'type': 'string'}, 
+              'TR_DATE_IN': {'empty': False, 'validator': is_french_date}, 
+              'TR_SRC_GEO': {'type': 'string', 'maxlength': 50}, 
+              'TR_QLT_GEO': {'type': 'string', 'empty': False, 'allowed': ['A', 'B', 'C']}, 
+              'TR_PRO_MD': {'type': 'string', 'maxlength': 20}, 
+              'TR_COMMENT': {'type': 'string', 'maxlength': 300, 'empty': True}, 
+              'TR_STATUT': {'type': 'string', 'empty': False, 'allowed': ['APS', 'APD', 'EXE', 'REC']}}
+
+class Zapbo(BaseGeoModel):
+    filename = "zapbo_geo.shp"
+    schema = {'geom': {'geometry': (gis.POLYGON, (XMIN, YMIN, XMAX, YMAX))}, 
+              'ID_ZAPBO': {'type': 'string', 'maxlength': 30}, 
+              'COMMENTAIR': {'type': 'string', 'maxlength': 254, 'empty': True}, 
+              'STATUT': {'type': 'string', 'empty': False, 'allowed': ['APS', 'APD', 'EXE', 'REC']}}
+    

+ 73 - 0
schemas/netgeo_1_12_doe/validator.py

@@ -0,0 +1,73 @@
+'''
+
+@author: olivier.massot, 2018
+'''
+from core.validation import NetgeoValidator, DuplicatedPk, RelationError
+from schemas.netgeo_1_12_doe.models import Artere, Cable, Equipement, Noeud, \
+    Tranchee, Zapbo
+
+
+class Netgeo112DoeValidator(NetgeoValidator):
+    schema_name = "Netgeo v1.12 DOE"
+    models = [Artere, Cable, Equipement, Noeud, Tranchee, Zapbo]
+    
+    def _technical_validation(self):
+        
+        # construction de l'index
+        self.index = {}
+        for model in self.dataset:
+            self.index[model] = {}
+            
+            for item in self.dataset[model]:
+                pk_value = getattr(item, model.pk)
+                if pk_value in self.index[model]:
+                    self.log_error(DuplicatedPk("Doublons dans le champs", filename=model.filename, field=model.pk))
+                else:
+                    self.index[model][pk_value] = item
+            
+        del self.dataset
+             
+        if self.errors: 
+            return
+            
+        # rattachement les noeuds aux artères     
+        for artere in self.index[Artere]:
+            try:
+                artere.noeud_a = self.index[Artere][artere.AR_NOEUD_A]
+            except KeyError:
+                self.log_error(RelationError("Le noeud '{}' n'existe pas".format(artere.AR_NOEUD_A), filename=Artere.filename, field="AR_NOEUD_A"))
+                
+            try:
+                artere.noeud_b = self.index[Artere][artere.AR_NOEUD_B]
+            except KeyError:
+                self.log_error(RelationError("Le noeud '{}' n'existe pas".format(artere.AR_NOEUD_B), filename=Artere.filename, field="AR_NOEUD_A"))
+        
+        # rattachement des equipements aux cables
+        for cable in self.index[Cable]:
+            try:
+                cable.equipement_a = self.index[Cable][cable.CA_EQ_A]
+            except KeyError:
+                self.log_error(RelationError("L'équipement '{}' n'existe pas".format(cable.AR_NOEUD_A), filename=Cable.filename, field="CA_EQ_A"))
+                
+            try:
+                cable.equipement_b = self.index[Cable][cable.CA_EQ_B]
+            except KeyError:
+                self.log_error(RelationError("L'équipement '{}' n'existe pas".format(cable.AR_NOEUD_B), filename=Cable.filename, field="CA_EQ_B"))
+                
+        if self.errors: 
+            return
+        
+        # Contrôler dans la base si des éléments portant ces codes existent à des emplacements différents
+        
+        # Contrôler l'emprise des ZAPBO
+        
+        
+        
+    
+if __name__ == "__main__":
+    from core.constants import MAIN
+    subject = MAIN / "work" / "AXIANS_082AP0_REC_180924.zip"
+#     subject = MAIN / "work" / "STURNO_192AP1_REC_171211_OK"
+    report = Netgeo112DoeValidator.submit(subject)
+    print(report)
+    

+ 23 - 28
schemas/netgeo_2_2_doe/models.py

@@ -3,25 +3,24 @@
 @author: olivier.massot, 2018
 '''
 from core import gis
-from core.cerberus_extend import is_french_date
+from core.cerberus_extend import is_french_date, is_float, is_int
 from core.validation import BaseGeoModel
 from schemas.common import INSEE_VALIDES, XMIN, YMIN, XMAX, YMAX
 
 
 class Artere(BaseGeoModel):
     filename = "artere_geo.shp"
-    pk = "AR_CODE"
     schema = {'geom': {'geometry': (gis.POLYLINE, (XMIN, YMIN, XMAX, YMAX))}, 
-              'AR_CODE': {'required': False, 'type': 'string', 'maxlength': 26}, 
+              'AR_CODE': {'type': 'string', 'maxlength': 26}, 
               'AR_NOM': {'type': 'string', 'maxlength': 26}, 
               'AR_ID_INSE': {'type': 'string', 'empty': False, 'allowed': INSEE_VALIDES}, 
-              'AR_LONG': {'type': 'float'}, 
+              'AR_LONG': {'validator': is_float}, 
               'AR_ETAT': {'type': 'string', 'empty': False, 'allowed': ['0', '1', '2', '3', '4']}, 
               'AR_OCCP': {'type': 'string', 'empty': False, 'allowed': ['0', '1.1', '1.2', '2', '3', '4']}, 
               'AR_NOEUD_A': {'type': 'string', 'maxlength': 20}, 
               'AR_NOEUD_B': {'type': 'string', 'maxlength': 20}, 
-              'AR_NB_FOUR': {'type': 'string', 'maxlength': 20}, 
-              'AR_FOU_DIS': {'coerce': int, 'type': 'integer'}, 
+              'AR_NB_FOUR': {'type': 'string', 'maxlength': 10}, 
+              'AR_FOU_DIS': {'validator': is_int}, 
               'AR_TYPE_FO': {'type': 'string', 'empty': False, 'multiallowed': ['PVC', 'PEHD', 'SOUS-TUBAGE PEHD', 'SOUS-TUBAGE  SOUPLE', 'FACADE', 'AERIEN', 'ENCORBELLEMENT', 'AUTRE']}, 
               'AR_TYFO_AI': {'type': 'string', 'empty': False, 'multiallowed': ['PVC', 'PEH', 'TUB', 'FAC', 'ENC', 'APP']}, 
               'AR_DIAM_FO': {'type': 'string', 'empty': False, 'multiallowed': ['10', '14', '18', '25', '28', '32', '40', '45', '60', 'NUL']}, 
@@ -30,7 +29,7 @@ class Artere(BaseGeoModel):
               'AR_REFFAB': {'type': 'string', 'maxlength': 100}, 
               'AR_COULEUR': {'type': 'string', 'maxlength': 20}, 
               'AR_AIGUIL': {'type': 'string', 'empty': False, 'allowed': ['OUI', 'NON']}, 
-              'AR_NBCABL': {'type': 'integer'}, 
+              'AR_NBCABL': {'validator': is_int}, 
               'AR_PRO_CAB': {'type': 'string', 'empty': False, 'allowed': ['MANCHE NUMERIQUE']}, 
               'AR_GEST_FO': {'type': 'string', 'empty': False, 'multiallowed': ['MANCHE NUMERIQUE', 'MANCHE TELECOM', 'COLLECTIVITE', 'ORANGE', 'MANCHE FIBRE', 'AUTRE (à préciser)', 'NUL']}, 
               'AR_UTIL_FO': {'type': 'string', 'empty': False, 'multiallowed': ['MANCHE NUMERIQUE', 'MANCHE TELECOM', 'COLLECTIVITE', 'ORANGE', 'AUTRE (à préciser)', 'NUL']}, 
@@ -45,7 +44,6 @@ class Artere(BaseGeoModel):
 
 class Cable(BaseGeoModel):
     filename = "cable_geo.shp"
-    pk = "CA_CODE"
     schema = {'geom': {'geometry': (gis.POLYLINE, (XMIN, YMIN, XMAX, YMAX))}, 
               'CA_CODE': {'type': 'string', 'maxlength': 18}, 
               'CA_NOM': {'type': 'string', 'maxlength': 18}, 
@@ -57,13 +55,14 @@ class Cable(BaseGeoModel):
               'CA_TYPE': {'type': 'string', 'maxlength': 10, 'empty': False, 'allowed': ['AERIEN', 'IMMEUBLE', 'FACADE', 'MIXTE', 'SOUTERRAIN']}, 
               'CA_TYPFCT': {'type': 'string', 'maxlength': 3, 'empty': False, 'allowed': ['CDI', 'CTR', 'CBM', 'RAC', 'CBO']}, 
               'CA_ETAT': {'type': 'string', 'maxlength': 1, 'empty': False, 'allowed': ['0', '1', '2', '3', '4']}, 
-              'CA_LONG': {'type': 'float'}, 'CA_EQ_A': {'type': 'string', 'maxlength': 18}, 
+              'CA_LONG': {'validator': is_float}, 
+              'CA_EQ_A': {'type': 'string', 'maxlength': 18}, 
               'CA_EQ_B': {'type': 'string', 'maxlength': 18}, 
-              'CA_DIAMETR': {'coerce': int, 'type': 'integer'}, 
+              'CA_DIAMETR': {'validator': is_int}, 
               'CA_COULEUR': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['NOIR', 'BLEU', 'BLANC']}, 
               'CA_TECHNOL': {'type': 'string', 'maxlength': 17, 'empty': False, 'allowed': ['G657A2_M6', 'G657A2_M12']}, 
-              'CA_NB_FO_U': {'coerce': int, 'type': 'integer'}, 
-              'CA_NB_FO_D': {'coerce': int, 'type': 'integer'}, 
+              'CA_NB_FO_U': {'validator': is_int}, 
+              'CA_NB_FO_D': {'validator': is_int}, 
               'CA_PRO': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['MANCHE NUMERIQUE']}, 
               'CA_GEST': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['NUL']}, 
               'CA_DATE_IN': {'empty': False, 'validator': is_french_date}, 
@@ -72,7 +71,6 @@ class Cable(BaseGeoModel):
 
 class Equipement(BaseGeoModel):
     filename = "equipement_passif.shp"
-    pk = "EQ_CODE"
     schema = {'geom': {'geometry': (gis.POINT, (XMIN, YMIN, XMAX, YMAX))}, 
               'EQ_CODE': {'type': 'string', 'maxlength': 18}, 
               'EQ_NOM': {'type': 'string', 'maxlength': 18}, 
@@ -80,9 +78,9 @@ class Equipement(BaseGeoModel):
               'EQ_REF': {'type': 'string', 'maxlength': 100}, 
               'EQ_EMPRISE': {'type': 'string', 'maxlength': 7}, 
               'EQ_FABR': {'type': 'string', 'maxlength': 100}, 
-              'EQ_CAPFO': {'type': 'integer'}, 
-              'EQ_NBMXEQ': {'coerce': int, 'type': 'integer'}, 
-              'EQ_NBCAB': {'coerce': int, 'type': 'integer'}, 
+              'EQ_CAPFO': {'validator': is_int}, 
+              'EQ_NBMXEQ': {'validator': is_int}, 
+              'EQ_NBCAB': {'validator': is_int}, 
               'EQ_DIMENS': {'type': 'string', 'maxlength': 50}, 
               'EQ_TYPEQ': {'type': 'string', 'maxlength': 100}, 
               'EQ_ETAT': {'type': 'string', 'maxlength': 1, 'empty': False, 'allowed': ['0', '1', '2', '3', '4']}, 
@@ -93,14 +91,13 @@ class Equipement(BaseGeoModel):
               'EQ_TYPE_PH': {'type': 'string', 'maxlength': 24, 'empty': False, 'allowed': ['PBO 6', 'PBO 12', 'BPE 12EP', 'BPE 24EP', 'BPE 48EP', 'BPE 72EP', 'BPE 96EP', 'BPE 144EP', 'BPE 288EP', 'BPE 576EP', 'BPE 720EP']}, 
               'EQ_PRO': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['MANCHE NUMERIQUE', 'COLLECTIVITE', 'ORANGE', 'AUTRE (à préciser)', 'NUL']}, 
               'EQ_GEST': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['MANCHE NUMERIQUE', 'MANCHE TELECOM', 'COLLECTIVITE', 'ORANGE', 'MANCHE FIBRE', 'AUTRE (à préciser)', 'NUL']}, 
-              'EQ_HAUT': {'type': 'float'}, 
+              'EQ_HAUT': {'validator': is_float}, 
               'EQ_DATE_IN': {'empty': False, 'validator': is_french_date}, 
               'EQ_COMMENT': {'type': 'string', 'maxlength': 300, 'empty': True}, 
               'EQ_STATUT': {'type': 'string', 'maxlength': 14, 'empty': False, 'allowed': ['EN ETUDE', 'EN REALISATION', 'EN SERVICE', 'HORS SERVICE']}}
 
 class Noeud(BaseGeoModel):
     filename = "noeud_geo.shp"
-    pk = "NO_CODE"
     schema = {'geom': {'geometry': (gis.POINT, (XMIN, YMIN, XMAX, YMAX))}, 
               'NO_CODE': {'type': 'string', 'maxlength': 18}, 
               'NO_ID_INSE': {'type': 'string', 'empty': False, 'allowed': INSEE_VALIDES}, 
@@ -117,11 +114,11 @@ class Noeud(BaseGeoModel):
               'NO_TECH_PS': {'type': 'string', 'maxlength': 4, 'empty': False, 'multiallowed': ['COAX', 'CUT', 'ECL', 'ELEC', 'VP', 'OPT', 'NC']}, 
               'NO_AMO': {'type': 'string', 'maxlength': 20}, 
               'NO_PLINOX': {'type': 'string', 'maxlength': 3, 'empty': False, 'allowed': ['OUI', 'NON']}, 
-              'NO_X': {'coerce': float, 'type': 'float'}, 
-              'NO_Y': {'coerce': float, 'type': 'float'}, 
+              'NO_X': {'validator': is_float}, 
+              'NO_Y': {'validator': is_float}, 
               'NO_PRO': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['MANCHE NUMERIQUE', 'COLLECTIVITE', 'ORANGE', 'ERDF', 'AUTRE (à préciser)', 'NUL']}, 
               'NO_GEST': {'type': 'string', 'maxlength': 20, 'empty': False, 'allowed': ['MANCHE NUMERIQUE', 'MANCHE TELECOM', 'COLLECTIVITE', 'ORANGE', 'ERDF', 'MANCHE FIBRE', 'AUTRE (à préciser)', 'NUL']}, 
-              'NO_HAUT': {'type': 'float'}, 
+              'NO_HAUT': {'validator': is_float}, 
               'NO_DATE_IN': {'empty': False, 'validator': is_french_date}, 
               'NO_REF_PLA': {'type': 'string', 'maxlength': 100}, 
               'NO_SRC_GEO': {'type': 'string', 'maxlength': 50}, 
@@ -132,7 +129,6 @@ class Noeud(BaseGeoModel):
 
 class Tranchee(BaseGeoModel):
     filename = "tranchee_geo.shp"
-    pk = "TR_CODE"
     schema = {'geom': {'geometry': (gis.POLYLINE, (XMIN, YMIN, XMAX, YMAX))}, 
               'TR_CODE': {'type': 'string', 'maxlength': 23}, 
               'TR_NOM': {'type': 'string', 'maxlength': 23}, 
@@ -141,11 +137,11 @@ class Tranchee(BaseGeoModel):
               'TR_TYP_IMP': {'type': 'string', 'empty': False, 'allowed': ['ACCOTEMENT STABILISE', 'ACCOTEMENT NON STABILISE', 'CHAUSSEE LOURDE', 'CHAUSSEE LEGERE', 'FOSSE', 'TROTTOIR', 'ESPACE VERT', 'ENCORBELLEMENT']}, 
               'TR_MOD_POS': {'type': 'string', 'empty': False, 'allowed': ['TRADITIONNEL', 'MICRO TRANCHEE', 'FONCAGE 60', 'FONCAGE 90', 'FONCAGE 120', 'TRANCHEUSE', 'FORAGE URBAIN', 'FORAGE RURAL', 'ENCORBELLEMENT']}, 
               'TR_MPOS_AI': {'type': 'string', 'empty': False, 'allowed': ['TRA', 'ALL', 'FONCAGE 60', 'FON', 'FOR', 'ENC']}, 
-              'TR_LONG': {'coerce': float, 'type': 'float'}, 
-              'TR_LARG': {'coerce': float, 'type': 'float'}, 
+              'TR_LONG': {'validator': is_float}, 
+              'TR_LARG': {'validator': is_float}, 
               'TR_REVET': {'type': 'string', 'empty': False, 'allowed': ['SABLE', 'BICOUCHE', 'ENROBE', 'BETON', 'PAVE', 'TERRAIN NATUREL']}, 
-              'TR_CHARGE': {'coerce': float, 'type': 'float'}, 
-              'TR_GRILLAG': {'coerce': float, 'type': 'float'}, 
+              'TR_CHARGE': {'validator': is_float}, 
+              'TR_GRILLAG': {'validator': is_float}, 
               'TR_REMBLAI': {'type': 'string'}, 
               'TR_PLYNOX': {'type': 'string', 'empty': False, 'allowed': ['OUI', 'NON']}, 
               'TR_PRO_VOI': {'type': 'string', 'empty': False, 'allowed': ['COMMUNE', 'COMMUNAUTE DE COMMUNES', 'DEPARTEMENT', 'ETAT', 'PRIVE']}, 
@@ -160,8 +156,7 @@ class Tranchee(BaseGeoModel):
 
 class Zapbo(BaseGeoModel):
     filename = "zapbo_geo.shp"
-    pk = "ID_ZAPBO"
     schema = {'geom': {'geometry': (gis.POLYGON, (XMIN, YMIN, XMAX, YMAX))}, 
-              'ID_ZAPBO': {'type': 'string', 'maxlength': 10}, 
+              'ID_ZAPBO': {'type': 'string', 'maxlength': 30}, 
               'COMMENTAIR': {'type': 'string', 'maxlength': 254, 'empty': True}, 
               'STATUT': {'type': 'string', 'empty': False, 'allowed': ['EN ETUDE', 'EN REALISATION', 'EN SERVICE', 'HORS SERVICE']}}