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)
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
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.
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.