maintenance.bdg_mathExos

La contextualisation du dépôt d'exercices s'assure que la base en graphe reflète les exercices et leurs méta-données locales. Ces méta-données sont définies par l'auteur c'est à dire écrites dans le dépôt local. La modification d'une méta-donnée de ce type se fait dans le dépôt et non dans la base en graphe.

Modifié le 26/03/23 @author: remy

La fonction exec() est appelée lors de l'instanciation de la classe Maquis. Elle met à jour la base en fonction de l'état du dépôt en exécutant des requêtes cypher.

Noeuds et arêtes

Les éléments du graphe associés aux exercices détaillés dans le manifeste.

  • noeud exercice: label: Document, typeDoc: "exercice", discipline: "mathématique", titre: "cdxx"
  • noeud thème: label; Concept, litteral: "nom du thème"
  • noeud feuille: label: Document, typeDoc: "liste exercices", titre: "nom du thème"
  • arête (CONTIENT) entre une feuille et un exercice
  • arête (EVALUE) entre un exercice et le concept de son thème
  • arête (EVALUE) entre une feuille et le concept de son thème
  • arête (INDEXE) entre un exercice et un concept indexé
  1#!/usr/bin/env python3
  2# -*- coding: utf-8 -*-
  3"""
  4La contextualisation du dépôt d'exercices  s'assure que la base en graphe reflète les exercices et leurs méta-données locales. Ces méta-données sont définies par l'auteur c'est à dire écrites dans le dépôt local. La modification d'une méta-donnée de ce type se fait dans le dépôt et non dans la base en graphe.
  5
  6Modifié le 26/03/23 @author: remy
  7
  8La fonction `exec()` est appelée lors de l'instanciation de la classe `Maquis`. Elle met à jour la base en fonction de l'état du dépôt en exécutant des requêtes cypher.
  9
 10
 11#### Noeuds et arêtes
 12Les éléments du graphe associés aux exercices détaillés dans le [manifeste](init_mathExos.html).
 13
 14- noeud exercice: label: `Document`,  `typeDoc: "exercice", discipline: "mathématique", titre: "cdxx"`
 15- noeud thème: label; `Concept`, `litteral: "nom du thème"`
 16- noeud feuille: label: `Document`, `typeDoc: "liste exercices", titre: "nom du thème"`
 17- arête (`CONTIENT`) entre une feuille et un exercice
 18- arête (`EVALUE`) entre un exercice et le concept de son thème
 19- arête (`EVALUE`) entre une feuille et le concept de son thème
 20- arête (`INDEXE`) entre un exercice et un concept indexé
 21
 22"""
 23import neo4j
 24import locale
 25import functools
 26locale.setlocale(locale.LC_ALL, '')
 27
 28def exec(self):
 29    """
 30    Exécution des requêtes spécifques de maintenance de la base.
 31    - les données locales (feuilles et exercices) ont été formées par l'exécution locale spécifique et sont passées par le paramètre `specific_results`.
 32    - récupération dans la base des patterns
 33
 34        (feuille) -[:CONTIENT]-> (exercice) -[:EVALUE]-> (concept)
 35    - affichage des feuilles par ordre alphabétique des thèmes
 36    - comparaison des exercices locaux et dans le graphe (orphelins)
 37    - création des patterns pour les exercices locaux absents du graphe
 38    - création des patterns pour les indexations locales absentes du graphe.
 39
 40    #### Renvoie
 41
 42    log: str journal
 43
 44    """
 45    print("coucou de exec() dans bdg_mathExos.py")
 46    
 47    data = self.connect_data
 48    URI = data['credentials']['URI']
 49    user = data['credentials']['user']
 50    password = data['credentials']['password']
 51    AUTH = (user, password)
 52
 53    # données locales (dictionnaires)
 54    loc_dictExos = self.specific_results['listeExos']
 55    loc_exercices = []
 56    for code,liste in loc_dictExos.items():
 57        loc_exercices += liste
 58    loc_exercices = list(map(
 59        lambda chaine: chaine.removeprefix('E').removesuffix('.tex'),
 60        loc_exercices))
 61
 62    loc_themes = self.specific_results['themes']
 63    loc_indexations = self.specific_results['indexations']
 64
 65    # Noeuds exercices du graphe
 66    param = {'label' : "Document",
 67             'propsF' :"{typeDoc:'exercice', discipline:'mathématique'}",
 68             'propsR' : "n.titre"}
 69    req = self.format_cypher_RETURN(param)
 70    # Nouvelle requête:
 71    new_req = 'MATCH (f:Document {typeDoc:"liste exercices"})-[:CONTIENT]-> (e:Document {typeDoc:"exercice", discipline:"mathématique"})-[:EVALUE]->(c:Concept) WHERE f.titre = c.litteral RETURN f.titre, e.titre, c.litteral ORDER BY e.titre'
 72    #print(new_req)
 73    with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
 74        rem_feu_exo_con = session.execute_read(self.do_cypher_tx, new_req)
 75    #print(rem_feu_exo_con[0:3])
 76    rem_exercices = list(map(lambda x: x[1], rem_feu_exo_con))
 77    # print(rem_exercices[0:5])
 78
 79    # codes et thèmes dans le graphe
 80    # codes des thèmes
 81    req = 'MATCH (f:Document {typeDoc:"liste exercices"})-[:CONTIENT]-> (e:Document {typeDoc:"exercice", discipline:"mathématique"}) RETURN DISTINCT f.titre, left(e.titre,2) AS code ORDER BY code'
 82    with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
 83        rem_theme_code = session.execute_read(self.do_cypher_tx, req)
 84    #print(rem_theme_code[0:5])
 85    themes = {l[1]:l[0] for l in rem_theme_code}
 86
 87    # Noeuds feuilles d'exercices du graphe
 88    param = {'label' : "Document",
 89             'propsF' :"{typeDoc:'liste exercices'}",
 90             'propsR' : "n.titre"}
 91    req = self.format_cypher_RETURN(param)
 92    with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
 93        rem_feuilles = session.execute_read(self.do_cypher_tx, req)
 94    rem_feuilles = list(map(lambda x: x[0], rem_feuilles))
 95    # pour bien ranger les accents
 96    rem_feuilles.sort(key=functools.cmp_to_key(locale.strcoll))
 97    i = 1
 98    print("\n thèmes des feuilles")
 99    for theme in rem_feuilles:
100        print(i,theme)
101        i += 1
102
103    #Nbs d'exercices
104    #print(loc_exercices)
105    blabla = "\n Nbs d'exercices. local {nloc}, base {nrem}"
106    blabla = blabla.format(
107        nloc=len(loc_exercices),
108        nrem=len(rem_exercices))
109    print(blabla)
110
111    # Noeuds exercices isolés (orphelins)
112    rem_exos_orph = []
113    for nom in rem_exercices:
114        if nom not in loc_exercices:
115            rem_exos_orph.append(nom)
116    blabla = "\n Noeuds exercices distants sans source locale (à supprimer): {}"
117    blabla = blabla.format(rem_exos_orph)
118    print(blabla)
119    for nom in rem_exos_orph:
120        param = {'label' : "Document"}
121        param['propsF'] = 'typeDoc:"exercice", discipline:"mathématique", titre:"{titre}"'
122        param['propsF'] = param['propsF'].format(titre=nom)
123        param['propsF'] = '{' + param['propsF'] + '}'
124        req = self.format_cypher_DELETE(param)
125        print(req)
126        with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
127            val = session.execute_write(self.do_cypher_tx, req)
128        print(val)
129
130
131    # Exercices locaux sans noeud associé
132    loc_exos_orph = []
133    for nom in loc_exercices:
134        if nom not in rem_exercices:
135            loc_exos_orph.append(nom)
136    blabla = "\n Exercices locaux sans noeud associé (à créer): {}"
137    blabla = blabla.format(loc_exos_orph)
138    print(blabla)
139
140    nouveaux_exos(self,loc_exos_orph, themes)
141
142    # indexations
143    #print("\n Indexations locales")
144    indexe(self, loc_indexations)
145    
146    log = ""
147    return log
148
149def nouveaux_exos(self,loc_exos_orph,themes):
150    """
151    insertion dans le maquis des éléments associés à un exercice local sans noeud associé.
152
153    Pour chaque exercice local absent du graphe
154    - crée le noeud exercice
155    - crée la relation feuille `CONTIENT` exercice
156    - crée la relation exercice `EVALUE` concept
157
158    Noter que le titre de la feuille contenant l'exercice est le littéral du concept évalué.
159
160    On peut créer un nouvel exercice dans une feuille existante mais pas créer une nouvelle feuille.
161    """
162    print("\n coucou de nouvel_exo()")
163    data = self.connect_data
164    URI = data['credentials']['URI']
165    user = data['credentials']['user']
166    password = data['credentials']['password']
167    AUTH = (user, password)
168
169    for nom in loc_exos_orph:
170        code = nom[:2]
171        theme = themes[code]
172        print(nom, code, theme)
173
174        # créer le noeud exercice
175        label = "Document"
176        propsF = '{'
177        propsF += 'date: datetime({epochMillis: timestamp()}), '
178        propsF += 'titre: "' + nom +'", '
179        propsF += 'urlSrcEnon: "https://github.com/nicolair/math-exos/blob/master/' + nom + '.tex", '
180        propsF += 'typeDoc: "exercice", '
181        propsF += 'discipline: "mathématique", '
182        propsF +=  'url: "https://maquisdoc-math.fra1.digitaloceanspaces.com/math-exos/Aexo_' + nom + '.html" }'
183        param = {'label': label, 'propsF': propsF}
184        req = self.format_cypher_CREATE(param)
185        #print(req)
186        with neo4j.GraphDatabase.driver(URI,auth=AUTH).session(database="neo4j") as session:
187            val = session.execute_write(self.do_cypher_tx, req)
188
189        # créer la relation feuille [CONTIENT] exercice
190        req = '''MATCH (e:Document {{typeDoc:"exercice", titre: "{0}"}})
191        MATCH (f:Document {{typeDoc: "liste exercices", titre: "{1}"}})
192        CREATE (f) -[r:CONTIENT]-> (e)'''
193        req = req.format(nom, theme)
194        with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
195            val = session.execute_write(self.do_cypher_tx, req)
196
197        # créer la relation exercice [EVALUE] theme (concept)
198        req = '''MATCH (e:Document {{typeDoc:"exercice", titre: "{0}"}})
199        MATCH (c:Concept {{typeConcept: "thème feuille exercices", litteral: "{1}"}})
200        CREATE (e) -[:EVALUE]-> (c)'''
201        req = req.format(nom, theme)
202        with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
203            val = session.execute_write(self.do_cypher_tx, req)
204
205def indexe(self, loc_indexations):
206    '''
207        Assure que des relations `INDEXE` sont associées aux indexations locales.
208    '''
209    print("\n coucou de indexe()")
210    data = self.connect_data
211    URI = data['credentials']['URI']
212    user = data['credentials']['user']
213    password = data['credentials']['password']
214    AUTH = (user, password)
215
216    # indexations dans le graphe
217    req = ''' MATCH (e:Document {typeDoc:"exercice", discipline: "mathématique"}) -[:INDEXE]-> (c:Concept)
218    RETURN "Aexo_" + e.titre, c.litteral
219    '''
220    #print(req)
221    with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
222            rem_indexations = session.execute_write(self.do_cypher_tx, req)
223
224    print(rem_indexations)
225    print(loc_indexations)
226
227    # création des indexations manquantes dans le graphe
228    req = ''' MATCH (e:Document {{typeDoc:"exercice", discipline: "mathématique", titre: "{0}"}})
229    MERGE (c:Concept {{litteral:"{1}"}})
230    CREATE (e)-[:INDEXE]->(c)
231    '''
232    rem_index_orph = []
233    for index in loc_indexations :
234        if index not in rem_indexations:
235            nom = index[0][5:]
236            concept = index[1]
237            req1 = req.format(nom, concept)
238            with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
239                val = session.execute_write(self.do_cypher_tx, req1)
def exec(self):
 29def exec(self):
 30    """
 31    Exécution des requêtes spécifques de maintenance de la base.
 32    - les données locales (feuilles et exercices) ont été formées par l'exécution locale spécifique et sont passées par le paramètre `specific_results`.
 33    - récupération dans la base des patterns
 34
 35        (feuille) -[:CONTIENT]-> (exercice) -[:EVALUE]-> (concept)
 36    - affichage des feuilles par ordre alphabétique des thèmes
 37    - comparaison des exercices locaux et dans le graphe (orphelins)
 38    - création des patterns pour les exercices locaux absents du graphe
 39    - création des patterns pour les indexations locales absentes du graphe.
 40
 41    #### Renvoie
 42
 43    log: str journal
 44
 45    """
 46    print("coucou de exec() dans bdg_mathExos.py")
 47    
 48    data = self.connect_data
 49    URI = data['credentials']['URI']
 50    user = data['credentials']['user']
 51    password = data['credentials']['password']
 52    AUTH = (user, password)
 53
 54    # données locales (dictionnaires)
 55    loc_dictExos = self.specific_results['listeExos']
 56    loc_exercices = []
 57    for code,liste in loc_dictExos.items():
 58        loc_exercices += liste
 59    loc_exercices = list(map(
 60        lambda chaine: chaine.removeprefix('E').removesuffix('.tex'),
 61        loc_exercices))
 62
 63    loc_themes = self.specific_results['themes']
 64    loc_indexations = self.specific_results['indexations']
 65
 66    # Noeuds exercices du graphe
 67    param = {'label' : "Document",
 68             'propsF' :"{typeDoc:'exercice', discipline:'mathématique'}",
 69             'propsR' : "n.titre"}
 70    req = self.format_cypher_RETURN(param)
 71    # Nouvelle requête:
 72    new_req = 'MATCH (f:Document {typeDoc:"liste exercices"})-[:CONTIENT]-> (e:Document {typeDoc:"exercice", discipline:"mathématique"})-[:EVALUE]->(c:Concept) WHERE f.titre = c.litteral RETURN f.titre, e.titre, c.litteral ORDER BY e.titre'
 73    #print(new_req)
 74    with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
 75        rem_feu_exo_con = session.execute_read(self.do_cypher_tx, new_req)
 76    #print(rem_feu_exo_con[0:3])
 77    rem_exercices = list(map(lambda x: x[1], rem_feu_exo_con))
 78    # print(rem_exercices[0:5])
 79
 80    # codes et thèmes dans le graphe
 81    # codes des thèmes
 82    req = 'MATCH (f:Document {typeDoc:"liste exercices"})-[:CONTIENT]-> (e:Document {typeDoc:"exercice", discipline:"mathématique"}) RETURN DISTINCT f.titre, left(e.titre,2) AS code ORDER BY code'
 83    with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
 84        rem_theme_code = session.execute_read(self.do_cypher_tx, req)
 85    #print(rem_theme_code[0:5])
 86    themes = {l[1]:l[0] for l in rem_theme_code}
 87
 88    # Noeuds feuilles d'exercices du graphe
 89    param = {'label' : "Document",
 90             'propsF' :"{typeDoc:'liste exercices'}",
 91             'propsR' : "n.titre"}
 92    req = self.format_cypher_RETURN(param)
 93    with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
 94        rem_feuilles = session.execute_read(self.do_cypher_tx, req)
 95    rem_feuilles = list(map(lambda x: x[0], rem_feuilles))
 96    # pour bien ranger les accents
 97    rem_feuilles.sort(key=functools.cmp_to_key(locale.strcoll))
 98    i = 1
 99    print("\n thèmes des feuilles")
100    for theme in rem_feuilles:
101        print(i,theme)
102        i += 1
103
104    #Nbs d'exercices
105    #print(loc_exercices)
106    blabla = "\n Nbs d'exercices. local {nloc}, base {nrem}"
107    blabla = blabla.format(
108        nloc=len(loc_exercices),
109        nrem=len(rem_exercices))
110    print(blabla)
111
112    # Noeuds exercices isolés (orphelins)
113    rem_exos_orph = []
114    for nom in rem_exercices:
115        if nom not in loc_exercices:
116            rem_exos_orph.append(nom)
117    blabla = "\n Noeuds exercices distants sans source locale (à supprimer): {}"
118    blabla = blabla.format(rem_exos_orph)
119    print(blabla)
120    for nom in rem_exos_orph:
121        param = {'label' : "Document"}
122        param['propsF'] = 'typeDoc:"exercice", discipline:"mathématique", titre:"{titre}"'
123        param['propsF'] = param['propsF'].format(titre=nom)
124        param['propsF'] = '{' + param['propsF'] + '}'
125        req = self.format_cypher_DELETE(param)
126        print(req)
127        with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
128            val = session.execute_write(self.do_cypher_tx, req)
129        print(val)
130
131
132    # Exercices locaux sans noeud associé
133    loc_exos_orph = []
134    for nom in loc_exercices:
135        if nom not in rem_exercices:
136            loc_exos_orph.append(nom)
137    blabla = "\n Exercices locaux sans noeud associé (à créer): {}"
138    blabla = blabla.format(loc_exos_orph)
139    print(blabla)
140
141    nouveaux_exos(self,loc_exos_orph, themes)
142
143    # indexations
144    #print("\n Indexations locales")
145    indexe(self, loc_indexations)
146    
147    log = ""
148    return log

Exécution des requêtes spécifques de maintenance de la base.

  • les données locales (feuilles et exercices) ont été formées par l'exécution locale spécifique et sont passées par le paramètre specific_results.
  • récupération dans la base des patterns
(feuille) -[:CONTIENT]-> (exercice) -[:EVALUE]-> (concept)

  • affichage des feuilles par ordre alphabétique des thèmes
  • comparaison des exercices locaux et dans le graphe (orphelins)
  • création des patterns pour les exercices locaux absents du graphe
  • création des patterns pour les indexations locales absentes du graphe.

Renvoie

log: str journal

def nouveaux_exos(self, loc_exos_orph, themes):
150def nouveaux_exos(self,loc_exos_orph,themes):
151    """
152    insertion dans le maquis des éléments associés à un exercice local sans noeud associé.
153
154    Pour chaque exercice local absent du graphe
155    - crée le noeud exercice
156    - crée la relation feuille `CONTIENT` exercice
157    - crée la relation exercice `EVALUE` concept
158
159    Noter que le titre de la feuille contenant l'exercice est le littéral du concept évalué.
160
161    On peut créer un nouvel exercice dans une feuille existante mais pas créer une nouvelle feuille.
162    """
163    print("\n coucou de nouvel_exo()")
164    data = self.connect_data
165    URI = data['credentials']['URI']
166    user = data['credentials']['user']
167    password = data['credentials']['password']
168    AUTH = (user, password)
169
170    for nom in loc_exos_orph:
171        code = nom[:2]
172        theme = themes[code]
173        print(nom, code, theme)
174
175        # créer le noeud exercice
176        label = "Document"
177        propsF = '{'
178        propsF += 'date: datetime({epochMillis: timestamp()}), '
179        propsF += 'titre: "' + nom +'", '
180        propsF += 'urlSrcEnon: "https://github.com/nicolair/math-exos/blob/master/' + nom + '.tex", '
181        propsF += 'typeDoc: "exercice", '
182        propsF += 'discipline: "mathématique", '
183        propsF +=  'url: "https://maquisdoc-math.fra1.digitaloceanspaces.com/math-exos/Aexo_' + nom + '.html" }'
184        param = {'label': label, 'propsF': propsF}
185        req = self.format_cypher_CREATE(param)
186        #print(req)
187        with neo4j.GraphDatabase.driver(URI,auth=AUTH).session(database="neo4j") as session:
188            val = session.execute_write(self.do_cypher_tx, req)
189
190        # créer la relation feuille [CONTIENT] exercice
191        req = '''MATCH (e:Document {{typeDoc:"exercice", titre: "{0}"}})
192        MATCH (f:Document {{typeDoc: "liste exercices", titre: "{1}"}})
193        CREATE (f) -[r:CONTIENT]-> (e)'''
194        req = req.format(nom, theme)
195        with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
196            val = session.execute_write(self.do_cypher_tx, req)
197
198        # créer la relation exercice [EVALUE] theme (concept)
199        req = '''MATCH (e:Document {{typeDoc:"exercice", titre: "{0}"}})
200        MATCH (c:Concept {{typeConcept: "thème feuille exercices", litteral: "{1}"}})
201        CREATE (e) -[:EVALUE]-> (c)'''
202        req = req.format(nom, theme)
203        with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
204            val = session.execute_write(self.do_cypher_tx, req)

insertion dans le maquis des éléments associés à un exercice local sans noeud associé.

Pour chaque exercice local absent du graphe

  • crée le noeud exercice
  • crée la relation feuille CONTIENT exercice
  • crée la relation exercice EVALUE concept

Noter que le titre de la feuille contenant l'exercice est le littéral du concept évalué.

On peut créer un nouvel exercice dans une feuille existante mais pas créer une nouvelle feuille.

def indexe(self, loc_indexations):
206def indexe(self, loc_indexations):
207    '''
208        Assure que des relations `INDEXE` sont associées aux indexations locales.
209    '''
210    print("\n coucou de indexe()")
211    data = self.connect_data
212    URI = data['credentials']['URI']
213    user = data['credentials']['user']
214    password = data['credentials']['password']
215    AUTH = (user, password)
216
217    # indexations dans le graphe
218    req = ''' MATCH (e:Document {typeDoc:"exercice", discipline: "mathématique"}) -[:INDEXE]-> (c:Concept)
219    RETURN "Aexo_" + e.titre, c.litteral
220    '''
221    #print(req)
222    with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
223            rem_indexations = session.execute_write(self.do_cypher_tx, req)
224
225    print(rem_indexations)
226    print(loc_indexations)
227
228    # création des indexations manquantes dans le graphe
229    req = ''' MATCH (e:Document {{typeDoc:"exercice", discipline: "mathématique", titre: "{0}"}})
230    MERGE (c:Concept {{litteral:"{1}"}})
231    CREATE (e)-[:INDEXE]->(c)
232    '''
233    rem_index_orph = []
234    for index in loc_indexations :
235        if index not in rem_indexations:
236            nom = index[0][5:]
237            concept = index[1]
238            req1 = req.format(nom, concept)
239            with neo4j.GraphDatabase.driver(URI, auth=AUTH).session(database="neo4j") as session:
240                val = session.execute_write(self.do_cypher_tx, req1)

Assure que des relations INDEXE sont associées aux indexations locales.