Skip to content

Instantly share code, notes, and snippets.

@ColinMaudry
Last active October 3, 2017 23:47
Étapes de transformation du CSV des marchés publics bretons vers le format JSON réglementaire (XSLT 3.0)
We can make this file beautiful and searchable if this error is corrected: No commas found in this CSV file in line 0.
Nmarché;SIRETMandataire;LibelleEntiteMandataire;SIRETAcheteur;LibelleAcheteur;Nature;Objet;CodeCPV;Type ;Procedure;CodePostalCommuneExecution;NomCommuneExecution;CodeINSEEExecution;GranulariteINSEEExecution;MillesimeMandatement;DateNotification;Montant mandate TTC;Montant mandate HT;Montant attribue TTC;Montant attribue HT;Date de cloture;Duree;SIRETContractant;DenominationSociale;Role;CodePostal;Dpt ID ;Département;Commune;Taille;Taille des entreprises par categorie officielle;Code NAF;Libelle NAF;Libelle SBA;Libelle CCI;geolocalisation
2013-90006;;Région Bretagne;;;;PBF Formation qualifiante 2013 Monteur en construction bois lot 4;;Services;MAPA - art 30 - au dessus des seuils;;;;;2013;2013-10-01;245526;;245526;;;;300599123;AFPA DIRECTION REGIONALE BRETAGNE;Titulaire;35208;35;Ille-et-Vilaine;RENNES;10 000 et plus;Grande entreprise;8559;ENSEIGNEMENT;Administration publique/enseignement;Tertiaire non marchand;
2013-90144;;Région Bretagne;;;;Dispositif de formations : Compétences clés 2013 - Lot 1;;Services;MAPA - art 30 - au dessus des seuils;;;;;2013;2013-01-01;0;;200817;;;;192900710;GRETA BRETAGNE OCCIDENTALE;Titulaire;29104;29;Finistère;PLUGUFFAN;100 à 199;PME;8559;ENSEIGNEMENT;Administration publique/enseignement;Tertiaire non marchand;
#! /bin/bash
# Les noms de nombreuses procédures (colonne Procedure) ne correspondent pas aux noms de procédures valides. Il faut donc les
# remplacer par le bon nom.
# Pour chaque commande, la première valeur est la valeur à normaliser et la
# deuxième valeur est la valeur normale issue de l'arrêté relatif à la publication des données essentielles.
# Les valeurs n'ayant pas d'équivalent dans l'arrêté seront ignorées au moment de la conversion vers JSON.
cp $1 valeurs-normalisées.csv
sed -i 's/\;MAPA - art 28;/;Procédure adaptée\;/g' valeurs-normalisées.csv
sed -i 's/\;MAPA - art 30 - au dessus des seuils\;/;Procédure adaptée;/g' valeurs-normalisées.csv
sed -i 's/\;MAPA - art 30 - en dessous des seuils\;/;Procédure adaptée;/g' valeurs-normalisées.csv
sed -i 's/\;Proc. adaptée\/allégée (art.28et30)\;/;Procédure adaptée;/g' valeurs-normalisées.csv
sed -i 's/\;Procédure adaptée (MAPA)\;/;Procédure adaptée;/g' valeurs-normalisées.csv
sed -i "s/\;Appel d'offre ouvert\;/;Appel d'offres ouvert;/g" valeurs-normalisées.csv
sed -i "s/\;appel d'offres ouvert\;/;Appel d'offres ouvert;/g" valeurs-normalisées.csv
sed -i "s/\;Appel d'offre ouvert (art.33)\;/;Appel d'offres ouvert;/g" valeurs-normalisées.csv
sed -i "s/\;Appel d'offre restreint\;/;Appel d'offres restreint;/g" valeurs-normalisées.csv
sed -i 's/\;Achat direct\;/;Marché négocié sans publicité ni mise en concurrence préalable;/g' valeurs-normalisées.csv
sed -i 's/\;Négocié avec pub (art.35I)\;/;Procédure négociée avec mise en concurrence préalable;/g' valeurs-normalisées.csv
sed -i 's/\;Procédure négociée après pub\;/;Procédure négociée avec mise en concurrence préalable;/g' valeurs-normalisées.csv
sed -i 's/\;Procédure négociée après pub.\;/;Procédure négociée avec mise en concurrence préalable;/g' valeurs-normalisées.csv
sed -i 's/\;Procédure négociée sans pub\;/;6 Marché négocié sans publicité ni mise en concurrence préalable;/g' valeurs-normalisées.csv
sed -i 's/\;Procédure négociée sans pub.\;/;6 Marché négocié sans publicité ni mise en concurrence préalable;/g' valeurs-normalisées.csv
sed -i 's/\;Marché négocié\;/;Marché négocié sans publicité ni mise en concurrence préalable;/g' valeurs-normalisées.csv
sed -i 's/\;marché négocié\;/;Marché négocié sans publicité ni mise en concurrence préalable;/g' valeurs-normalisées.csv
sed -i 's/\;Marché negocié\;/;Marché négocié sans publicité ni mise en concurrence préalable;/g' valeurs-normalisées.csv
#Valeurs sans équivalent
sed -i "s/\;Appel d'offres\;/;;/g" valeurs-normalisées.csv
sed -i "s/\;Concours\;/;;/g" valeurs-normalisées.csv
sed -i "s/\;Contrat de mandat\;/;;/g" valeurs-normalisées.csv
sed -i 's/\;UGAP\;/;;/g' valeurs-normalisées.csv
#! /usr/bin/env python
# Adapté de https://help.ubuntu.com/community/Converting%20CSV%20to%20XML
import csv
import sys
csv.register_dialect('custom',
delimiter=';',
doublequote=True,
escapechar=None,
quotechar='"',
quoting=csv.QUOTE_MINIMAL,
skipinitialspace=False)
with open(sys.argv[1]) as ifile:
data = csv.reader(ifile, dialect='custom')
row=0
headers=[]
print "<document>"
for record in data:
row = row + 1
if row == 1:
for i, field in enumerate(record):
headers.append(field.strip().replace(" ","_"))
else:
print " <row>"
n = 0
for i, field in enumerate(record):
print " <" + headers[n] + ">" + field + "</" + headers[n] + ">"
n = n + 1
print " </row>"
print "</document>"
<document>
<row>
<Nmarché>2013-90006</Nmarché>
<SIRETMandataire></SIRETMandataire>
<LibelleEntiteMandataire>Région Bretagne</LibelleEntiteMandataire>
<SIRETAcheteur></SIRETAcheteur>
<LibelleAcheteur></LibelleAcheteur>
<Nature></Nature>
<Objet>PBF Formation qualifiante 2013 Monteur en construction bois lot 4</Objet>
<CodeCPV></CodeCPV>
<Type>Services</Type>
<Procedure>MAPA - art 30 - au dessus des seuils</Procedure>
<CodePostalCommuneExecution></CodePostalCommuneExecution>
<NomCommuneExecution></NomCommuneExecution>
<CodeINSEEExecution></CodeINSEEExecution>
<GranulariteINSEEExecution></GranulariteINSEEExecution>
<MillesimeMandatement>2013</MillesimeMandatement>
<DateNotification>2013-10-01</DateNotification>
<Montant_mandate_TTC>245526</Montant_mandate_TTC>
<Montant_mandate_HT></Montant_mandate_HT>
<Montant_attribue_TTC>245526</Montant_attribue_TTC>
<Montant_attribue_HT></Montant_attribue_HT>
<Date_de_cloture></Date_de_cloture>
<Duree></Duree>
<SIRETContractant>300599123</SIRETContractant>
<DenominationSociale>AFPA DIRECTION REGIONALE BRETAGNE</DenominationSociale>
<Role>Titulaire</Role>
<CodePostal>35208</CodePostal>
<Dpt_ID>35</Dpt_ID>
<Département>Ille-et-Vilaine</Département>
<Commune>RENNES</Commune>
<Taille>10 000 et plus</Taille>
<Taille_des_entreprises_par_categorie_officielle>Grande entreprise</Taille_des_entreprises_par_categorie_officielle>
<Code_NAF>8559</Code_NAF>
<Libelle_NAF>ENSEIGNEMENT</Libelle_NAF>
<Libelle_SBA>Administration publique/enseignement</Libelle_SBA>
<Libelle_CCI>Tertiaire non marchand</Libelle_CCI>
<geolocalisation></geolocalisation>
</row>
<row>
<Nmarché>2013-90144</Nmarché>
<SIRETMandataire></SIRETMandataire>
<LibelleEntiteMandataire>Région Bretagne</LibelleEntiteMandataire>
<SIRETAcheteur></SIRETAcheteur>
<LibelleAcheteur></LibelleAcheteur>
<Nature></Nature>
<Objet>Dispositif de formations : Compétences clés 2013 - Lot 1</Objet>
<CodeCPV></CodeCPV>
<Type>Services</Type>
<Procedure>MAPA - art 30 - au dessus des seuils</Procedure>
<CodePostalCommuneExecution></CodePostalCommuneExecution>
<NomCommuneExecution></NomCommuneExecution>
<CodeINSEEExecution></CodeINSEEExecution>
<GranulariteINSEEExecution></GranulariteINSEEExecution>
<MillesimeMandatement>2013</MillesimeMandatement>
<DateNotification>2013-01-01</DateNotification>
<Montant_mandate_TTC>0</Montant_mandate_TTC>
<Montant_mandate_HT></Montant_mandate_HT>
<Montant_attribue_TTC>200817</Montant_attribue_TTC>
<Montant_attribue_HT></Montant_attribue_HT>
<Date_de_cloture></Date_de_cloture>
<Duree></Duree>
<SIRETContractant>192900710</SIRETContractant>
<DenominationSociale>GRETA BRETAGNE OCCIDENTALE</DenominationSociale>
<Role>Titulaire</Role>
<CodePostal>29104</CodePostal>
<Dpt_ID>29</Dpt_ID>
<Département>Finistère</Département>
<Commune>PLUGUFFAN</Commune>
<Taille>100 à 199</Taille>
<Taille_des_entreprises_par_categorie_officielle>PME</Taille_des_entreprises_par_categorie_officielle>
<Code_NAF>8559</Code_NAF>
<Libelle_NAF>ENSEIGNEMENT</Libelle_NAF>
<Libelle_SBA>Administration publique/enseignement</Libelle_SBA>
<Libelle_CCI>Tertiaire non marchand</Libelle_CCI>
<geolocalisation></geolocalisation>
</row>
</document>
#!/bin/bash
# Commande pour créer une représentation XML d'un des exemples (https://github.com/etalab/format-commande-publique/blob/master/exemples/json/paquet.json).
# afin de me faire une idée de la structure cible.
# Nécessite Java et Saxon 9.8+ (http://saxon.sourceforge.net/). La Home Edition gratuite est suffisante.
java -cp ./saxon9he.jar net.sf.saxon.Query -t -qs:"json-to-xml(unparsed-text('./paquet.json'))" -o:output-json.xml
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns="http://www.w3.org/2005/xpath-functions"
exclude-result-prefixes="xs"
version="3.0">
<xsl:output indent="yes" method="xml" omit-xml-declaration="yes"/>
<xsl:variable name="quot">'</xsl:variable>
<xsl:template match="/">
<xsl:variable name="xml">
<map>
<string key="$schema">
https://raw.githubusercontent.com/etalab/format-commande-publique/master/sch%C3%A9mas/json/paquet.json</string>
<array key="marches">
<xsl:apply-templates/>
</array>
</map>
</xsl:variable>
<xsl:value-of select="xml-to-json($xml)"/>
</xsl:template>
<xsl:template match="row">
<map>
<xsl:apply-templates/>
<map key="acheteur">
<xsl:apply-templates mode="acheteurs"/>
</map>
<string key="_type">Marché</string>
<xsl:call-template name="lieuExecution"/>
<xsl:call-template name="titulaires"/>
<array key="modifications"><xsl:comment>Pas de données en entrée</xsl:comment></array>
</map>
</xsl:template>
<xsl:template match="Nmarché[not(empty(text()))]">
<string key="id"><xsl:value-of select="text()"/></string>
</xsl:template>
<xsl:template match="SIRETMandataire" mode="acheteurs">
<string key="id"><xsl:value-of select="text()"/></string>
</xsl:template>
<xsl:template match="LibelleEntiteMandataire" mode="acheteurs">
<string key="nom"><xsl:value-of select="text()"/></string>
</xsl:template>
<xsl:template match="Objet">
<string key="objet"><xsl:value-of select="substring(text(),0,256)"/></string>
</xsl:template>
<xsl:template match="CodeCPV">
<string key="codeCPV"><xsl:value-of select="text()"/></string>
</xsl:template>
<xsl:template match="DateNotification">
<string key="dateNotification"><xsl:value-of select="text()"/></string>
</xsl:template>
<xsl:template match="Montant_attribue_HT">
<number key="montant"><xsl:value-of select="text()"/></number>
</xsl:template>
<xsl:template match="Procedure[
text() = 'Procédure adaptée' or
text() = concat('Appel d',$quot,'offres ouvert') or
text() = concat('Appel d',$quot,'offres restreint') or
text() = 'Procédure concurrentielle avec négociation' or
text() = 'Procédure négociée avec mise en concurrence préalable' or
text() = 'Marché négocié sans publicité ni mise en concurrence préalable' or
text() = 'Dialogue compétitif'
]">
<string key="procedure"><xsl:value-of select="text()"/></string>
</xsl:template>
<xsl:template name="lieuExecution">
<map key="lieuExecution">
<xsl:choose>
<xsl:when test="CodeINSEEExecution != ''">
<string key="code"><xsl:value-of select="CodeINSEEExecution"/></string>
<string key="typeCode">Code commune</string>
</xsl:when>
<xsl:when test="CodePostalCommuneExecution != ''">
<string key="code"><xsl:value-of select="CodePostalCommuneExecution"/></string>
<string key="typeCode">Code postal</string>
</xsl:when>
</xsl:choose>
<xsl:if test="NomCommuneExecution/text()">
<string key="nom"><xsl:value-of select="NomCommuneExecution"/></string>
</xsl:if>
</map>
</xsl:template>
<xsl:template name="titulaires">
<xsl:if test="SIRETContractant != '' or DenominationSociale != ''">
<array key="titulaires">
<map>
<string key="typeIdentifiant">SIRET</string>
<xsl:if test="SIRETContractant != ''">
<string key="id">
<xsl:value-of select="SIRETContractant"/>
</string>
</xsl:if>
<xsl:if test="DenominationSociale != ''">
<string key="denominationSociale">
<xsl:value-of select="DenominationSociale"/>
</string>
</xsl:if>
</map>
</array>
</xsl:if>
</xsl:template>
<xsl:template match="text() | *[not(node())]" mode="#all"/>
</xsl:stylesheet>
{
"$schema" : "https://raw.githubusercontent.com/etalab/format-commande-publique/master/sch%C3%A9mas/json/paquet.json",
"marches" : [
{
"acheteur" : { "nom" : "Région Bretagne" },
"dateNotification" : "2013-10-01",
"id" : "2013-90006",
"lieuExecution" : { },
"modifications" : [ ],
"objet" : "PBF Formation qualifiante 2013 Monteur en construction bois lot 4",
"titulaires" : [ {
"denominationSociale" : "AFPA DIRECTION REGIONALE BRETAGNE",
"id" : "300599123",
"typeIdentifiant" : "SIRET"
} ],
"_type" : "Marché"
},
{
"acheteur" : { "nom" : "Région Bretagne" },
"dateNotification" : "2013-01-01",
"id" : "2013-90144",
"lieuExecution" : { },
"modifications" : [ ],
"objet" : "Dispositif de formations : Compétences clés 2013 - Lot 1",
"titulaires" : [ {
"denominationSociale" : "GRETA BRETAGNE OCCIDENTALE",
"id" : "192900710",
"typeIdentifiant" : "SIRET"
} ],
"_type" : "Marché"
}
]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment