Skip to content

Instantly share code, notes, and snippets.

@ColinMaudry
Last active October 3, 2017 23:47
Show Gist options
  • Save ColinMaudry/74464b8bc02e0e3786873dc1c7175cc0 to your computer and use it in GitHub Desktop.
Save ColinMaudry/74464b8bc02e0e3786873dc1c7175cc0 to your computer and use it in GitHub Desktop.
É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