Docfile commited on
Commit
bbe9349
·
verified ·
1 Parent(s): 9911182

Upload app - 2025-03-10T104410.884.py

Browse files
Files changed (1) hide show
  1. app - 2025-03-10T104410.884.py +403 -0
app - 2025-03-10T104410.884.py ADDED
@@ -0,0 +1,403 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, render_template, request, send_file
2
+ import os
3
+ import subprocess
4
+ from docx import Document
5
+ from docx.shared import Pt, Cm, RGBColor
6
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
7
+ from docx.enum.table import WD_TABLE_ALIGNMENT
8
+ from docx.oxml.ns import nsdecls
9
+ from docx.oxml import parse_xml
10
+
11
+ # --- Classe de génération de document ---
12
+ class EvaluationGymnique:
13
+ def __init__(self):
14
+ self.document = Document()
15
+ self.document.sections[0].page_height = Cm(29.7)
16
+ self.document.sections[0].page_width = Cm(21)
17
+ self.document.sections[0].left_margin = Cm(1.5)
18
+ self.document.sections[0].right_margin = Cm(1.5)
19
+ self.document.sections[0].top_margin = Cm(1)
20
+ self.document.sections[0].bottom_margin = Cm(1)
21
+
22
+ # Informations d'en-tête par défaut
23
+ self.centre_examen = "Centre d'examen"
24
+ self.type_examen = "Bac Général"
25
+ self.serie = "Série"
26
+ self.etablissement = "Établissement"
27
+ self.session = "2025"
28
+ self.nom_candidat = "Candidat"
29
+
30
+ # Liste vide pour les éléments techniques
31
+ self.elements_techniques = []
32
+ self.appreciations = ["M", "PM", "NM", "NR"]
33
+
34
+ def ajouter_entete_colore(self):
35
+ header_paragraph = self.document.add_paragraph()
36
+ header_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
37
+ header_paragraph.space_after = Pt(6)
38
+ header_run = header_paragraph.add_run("ÉVALUATION GYMNASTIQUE")
39
+ header_run.bold = True
40
+ header_run.font.size = Pt(14)
41
+ header_run.font.color.rgb = RGBColor(0, 32, 96)
42
+
43
+ header_table = self.document.add_table(rows=3, cols=2)
44
+ header_table.style = 'Table Grid'
45
+ header_table.autofit = False
46
+
47
+ for row in header_table.rows:
48
+ row.height = Cm(0.8)
49
+ for row in header_table.rows:
50
+ for cell in row.cells:
51
+ shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="D9E2F3"/>')
52
+ cell._tc.get_or_add_tcPr().append(shading_elm)
53
+
54
+ # Première ligne
55
+ cell = header_table.cell(0, 0)
56
+ paragraph = cell.paragraphs[0]
57
+ run = paragraph.add_run("Centre d'examen: ")
58
+ run.bold = True
59
+ run.font.size = Pt(10)
60
+ run.font.color.rgb = RGBColor(0, 32, 96)
61
+ paragraph.add_run(self.centre_examen).font.size = Pt(10)
62
+
63
+ cell = header_table.cell(0, 1)
64
+ paragraph = cell.paragraphs[0]
65
+ run = paragraph.add_run("Examen: ")
66
+ run.bold = True
67
+ run.font.size = Pt(10)
68
+ run.font.color.rgb = RGBColor(0, 32, 96)
69
+ paragraph.add_run(self.type_examen).font.size = Pt(10)
70
+
71
+ # Deuxième ligne
72
+ cell = header_table.cell(1, 0)
73
+ paragraph = cell.paragraphs[0]
74
+ run = paragraph.add_run("Série: ")
75
+ run.bold = True
76
+ run.font.size = Pt(10)
77
+ run.font.color.rgb = RGBColor(0, 32, 96)
78
+ paragraph.add_run(self.serie).font.size = Pt(10)
79
+
80
+ cell = header_table.cell(1, 1)
81
+ paragraph = cell.paragraphs[0]
82
+ run = paragraph.add_run("Établissement: ")
83
+ run.bold = True
84
+ run.font.size = Pt(10)
85
+ run.font.color.rgb = RGBColor(0, 32, 96)
86
+ paragraph.add_run(self.etablissement).font.size = Pt(10)
87
+
88
+ # Troisième ligne
89
+ cell = header_table.cell(2, 0)
90
+ paragraph = cell.paragraphs[0]
91
+ run = paragraph.add_run("Session: ")
92
+ run.bold = True
93
+ run.font.size = Pt(10)
94
+ run.font.color.rgb = RGBColor(0, 32, 96)
95
+ paragraph.add_run(self.session).font.size = Pt(10)
96
+
97
+ cell = header_table.cell(2, 1)
98
+ paragraph = cell.paragraphs[0]
99
+ run = paragraph.add_run("Candidat: ")
100
+ run.bold = True
101
+ run.font.size = Pt(10)
102
+ run.font.color.rgb = RGBColor(0, 32, 96)
103
+ paragraph.add_run(self.nom_candidat).font.size = Pt(10)
104
+
105
+ self.document.add_paragraph().space_after = Pt(4)
106
+
107
+ def creer_tableau_elements(self):
108
+ table = self.document.add_table(rows=len(self.elements_techniques) + 1, cols=5)
109
+ table.style = 'Table Grid'
110
+ table.alignment = WD_TABLE_ALIGNMENT.CENTER
111
+
112
+ # Configuration des largeurs de colonnes
113
+ for cell in table.columns[0].cells:
114
+ cell.width = Cm(8)
115
+ for cell in table.columns[1].cells:
116
+ cell.width = Cm(3)
117
+ for cell in table.columns[2].cells:
118
+ cell.width = Cm(2)
119
+ for cell in table.columns[3].cells:
120
+ cell.width = Cm(2.5)
121
+ for cell in table.columns[4].cells:
122
+ cell.width = Cm(2.5)
123
+
124
+ for row in table.rows:
125
+ row.height = Cm(1)
126
+
127
+ # En-tête du tableau
128
+ header_row = table.rows[0]
129
+ for cell in header_row.cells:
130
+ shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="BDD7EE"/>')
131
+ cell._tc.get_or_add_tcPr().append(shading_elm)
132
+
133
+ headers = ["ELEMENTS TECHNIQUES", "CATEGORIES D'ELEMENTS TECHNIQUES ET PONDERATION", "", "APPRECIATIONS", "POINTS Accordés"]
134
+ for i, header in enumerate(headers):
135
+ cell = table.cell(0, i)
136
+ cell.text = header
137
+ cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
138
+ run = cell.paragraphs[0].runs[0]
139
+ run.bold = True
140
+ run.font.size = Pt(9)
141
+ run.font.color.rgb = RGBColor(0, 32, 96)
142
+ table.cell(0, 1).merge(table.cell(0, 2))
143
+
144
+ # Ajout des éléments techniques
145
+ for i, element in enumerate(self.elements_techniques, 1):
146
+ element_cell = table.cell(i, 0)
147
+ element_cell.text = element["nom"]
148
+ element_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.LEFT
149
+ run = element_cell.paragraphs[0].runs[0]
150
+ run.bold = True
151
+ run.font.size = Pt(9)
152
+
153
+ categorie_cell = table.cell(i, 1)
154
+ categorie_cell.text = element["categorie"]
155
+ categorie_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
156
+ run = categorie_cell.paragraphs[0].runs[0]
157
+ run.bold = True
158
+ run.font.size = Pt(9)
159
+ run.italic = True
160
+
161
+ points_cell = table.cell(i, 2)
162
+ points_cell.text = str(element["points"])
163
+ points_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
164
+ run = points_cell.paragraphs[0].runs[0]
165
+ run.bold = True
166
+ run.font.size = Pt(9)
167
+ run.italic = True
168
+
169
+ def ajouter_note_jury(self):
170
+ para = self.document.add_paragraph()
171
+ para.space_before = Pt(4)
172
+ para.space_after = Pt(4)
173
+ run = para.add_run("NB1 : Zone réservée aux membres du jury ! Le jury cochera le point correspondant au niveau de réalisation de l'élément gymnique par le candidat.")
174
+ run.bold = True
175
+ run.font.color.rgb = RGBColor(255, 0, 0)
176
+ run.font.size = Pt(9)
177
+
178
+ def creer_tableau_recapitulatif(self):
179
+ note_table = self.document.add_table(rows=3, cols=13)
180
+ note_table.style = 'Table Grid'
181
+ note_table.alignment = WD_TABLE_ALIGNMENT.CENTER
182
+
183
+ for row in note_table.rows:
184
+ row.height = Cm(0.6)
185
+ for cell in note_table.rows[0].cells:
186
+ shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="BDD7EE"/>')
187
+ cell._tc.get_or_add_tcPr().append(shading_elm)
188
+
189
+ for col, (type_lettre, points) in enumerate([("A", "1pt"), ("B", "1,5pt"), ("C", "2pts"), ("D", "2,5pts"), ("E", "3pts")]):
190
+ idx = col * 2
191
+ if idx + 1 < len(note_table.columns):
192
+ cell = note_table.cell(0, idx)
193
+ cell.merge(note_table.cell(0, idx + 1))
194
+ cell.text = f"Type {type_lettre}\n{points}"
195
+ cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
196
+ for run in cell.paragraphs[0].runs:
197
+ run.bold = True
198
+ run.font.size = Pt(9)
199
+ run.font.color.rgb = RGBColor(0, 32, 96)
200
+
201
+ for col, (titre, points) in enumerate([("ROV", "2pts"), ("Projet", "2pts"), ("Réalisation", "16pts")], 10):
202
+ if col < len(note_table.columns):
203
+ cell = note_table.cell(0, col)
204
+ cell.text = f"{titre}\n{points}"
205
+ cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
206
+ for run in cell.paragraphs[0].runs:
207
+ run.bold = True
208
+ run.font.size = Pt(9)
209
+ run.font.color.rgb = RGBColor(0, 32, 96)
210
+
211
+ for col in range(5):
212
+ idx = col * 2
213
+ if idx + 1 < len(note_table.columns):
214
+ neg_cell = note_table.cell(1, idx)
215
+ neg_cell.text = "NEG"
216
+ neg_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
217
+ for run in neg_cell.paragraphs[0].runs:
218
+ run.italic = True
219
+ run.font.size = Pt(9)
220
+
221
+ note_cell = note_table.cell(1, idx + 1)
222
+ note_cell.text = "Note"
223
+ note_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
224
+ for run in note_cell.paragraphs[0].runs:
225
+ run.italic = True
226
+ run.font.size = Pt(9)
227
+
228
+ for col in range(10, 13):
229
+ if col < len(note_table.columns):
230
+ cell = note_table.cell(1, col)
231
+ cell.text = "Note"
232
+ cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
233
+ for run in cell.paragraphs[0].runs:
234
+ run.italic = True
235
+ run.font.size = Pt(9)
236
+
237
+ for col, type_lettre in enumerate(["A", "B", "C", "D", "E"]):
238
+ idx = col * 2
239
+ if idx < len(note_table.columns):
240
+ neg_cell = note_table.cell(2, idx)
241
+ neg_cell.text = ""
242
+ neg_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
243
+
244
+ def ajouter_note_candidat_avec_cadre(self):
245
+ note_table = self.document.add_table(rows=1, cols=1)
246
+ note_table.style = 'Table Grid'
247
+ note_table.alignment = WD_TABLE_ALIGNMENT.CENTER
248
+
249
+ cell = note_table.cell(0, 0)
250
+ shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="E2EFDA"/>')
251
+ cell._tc.get_or_add_tcPr().append(shading_elm)
252
+
253
+ p = cell.paragraphs[0]
254
+ run = p.add_run("NB2: Après le choix des catégories d'éléments gymniques par le candidat, ce dernier remplira la colonne de pointage selon l'orientation suivante: A (0.25; 0.5; 0.75; 1) B (0.25; 0.5; 0.75; 1; 1.25; 1.5) C (0.5; 0.75; 1; 1.25; 1.5; 2) D (0.75; 1; 1.25; 1.5; 2; 2.5) et E (0.75; 1; 1.5; 2; 2.5; 3) également, le candidat devra fournir 2 copies de son projet sur une page! (appréciations: NR, NM, PM, M).")
255
+ run.italic = True
256
+ run.font.size = Pt(8)
257
+
258
+
259
+ def ajouter_zone_note(self):
260
+ zone_note = self.document.add_table(rows=1, cols=2)
261
+ zone_note.style = 'Table Grid'
262
+ zone_note.alignment = WD_TABLE_ALIGNMENT.RIGHT
263
+
264
+ # Définir une largeur fixe pour les deux cellules
265
+ cell_width = Cm(2)
266
+
267
+ cell_libelle = zone_note.cell(0, 0)
268
+ cell_libelle.width = cell_width # Même largeur que la cellule de note
269
+ cell_libelle.text = "Note finale"
270
+ cell_libelle.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.RIGHT
271
+ run = cell_libelle.paragraphs[0].runs[0]
272
+ run.bold = True
273
+ run.font.size = Pt(12)
274
+ run.font.color.rgb = RGBColor(0, 32, 96)
275
+
276
+ cell_saisie = zone_note.cell(0, 1)
277
+ cell_saisie.width = cell_width # Largeur fixe
278
+ zone_note.rows[0].height = cell_width # Hauteur identique à la largeur pour être carré
279
+ shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="F2F2F2"/>')
280
+ cell_saisie._tc.get_or_add_tcPr().append(shading_elm)
281
+ cell_saisie.text = "/20"
282
+ cell_saisie.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
283
+ run = cell_saisie.paragraphs[0].runs[0]
284
+ run.bold = True
285
+ run.font.size = Pt(12)
286
+
287
+ # Ajouter plus d'espace avant la liste des correcteurs
288
+ self.document.add_paragraph().space_after = Pt(12)
289
+ self.document.add_paragraph().space_after = Pt(12) # Un deuxième paragraphe pour plus d'espace
290
+
291
+
292
+ # Modification 2: Modification de la méthode ajouter_lignes_correcteurs()
293
+ # pour ajuster l'espacement est optionnelle, mais recommandée pour cohérence:
294
+
295
+ def ajouter_lignes_correcteurs(self):
296
+ # Commencer avec un peu d'espace
297
+ para_intro = self.document.add_paragraph()
298
+ para_intro.space_before = Pt(6)
299
+
300
+ # Ajouter les lignes des correcteurs
301
+ for role in ["Projet", "principal", "ROV"]:
302
+ para = self.document.add_paragraph()
303
+ para.space_before = Pt(4)
304
+ para.space_after = Pt(4)
305
+ run = para.add_run(f"Correcteur {role} : ")
306
+ run.bold = True
307
+ para.add_run(".................................................................")
308
+
309
+ # Méthodes de modification des données
310
+ def modifier_centre_examen(self, nom):
311
+ self.centre_examen = nom
312
+
313
+ def modifier_type_examen(self, type_examen):
314
+ self.type_examen = type_examen
315
+
316
+ def modifier_serie(self, serie):
317
+ self.serie = serie
318
+
319
+ def modifier_etablissement(self, nom):
320
+ self.etablissement = nom
321
+
322
+ def modifier_session(self, annee):
323
+ self.session = annee
324
+
325
+ def modifier_candidat(self, nom):
326
+ self.nom_candidat = nom
327
+
328
+ def ajouter_element(self, nom, categorie, points):
329
+ self.elements_techniques.append({
330
+ "nom": nom,
331
+ "categorie": categorie,
332
+ "points": float(points)
333
+ })
334
+
335
+ def generer_document(self, nom_fichier="evaluation_gymnastique.docx"):
336
+ self.ajouter_entete_colore()
337
+ self.creer_tableau_elements()
338
+ self.ajouter_note_jury()
339
+ self.creer_tableau_recapitulatif()
340
+ self.ajouter_lignes_correcteurs()
341
+ self.ajouter_zone_note()
342
+ self.ajouter_note_candidat_avec_cadre()
343
+ self.document.save(nom_fichier)
344
+ return nom_fichier
345
+
346
+ # --- Application Flask ---
347
+ app = Flask(__name__)
348
+
349
+ @app.route("/", methods=["GET", "POST"])
350
+ def index():
351
+ if request.method == "POST":
352
+ # Récupération des informations depuis le formulaire
353
+ centre_examen = request.form.get("centre_examen", "Centre d'examen")
354
+ type_examen = request.form.get("type_examen", "Bac Général")
355
+ serie = request.form.get("serie", "Série")
356
+ etablissement = request.form.get("etablissement", "Établissement")
357
+ session_value = request.form.get("session", "2025")
358
+ nom_candidat = request.form.get("nom_candidat", "Candidat")
359
+
360
+ # Création et configuration du document
361
+ evaluation = EvaluationGymnique()
362
+ evaluation.modifier_centre_examen(centre_examen)
363
+ evaluation.modifier_type_examen(type_examen)
364
+ evaluation.modifier_serie(serie)
365
+ evaluation.modifier_etablissement(etablissement)
366
+ evaluation.modifier_session(session_value)
367
+ evaluation.modifier_candidat(nom_candidat)
368
+
369
+ # Récupération des éléments techniques ajoutés dynamiquement
370
+ element_names = request.form.getlist("new_element_name")
371
+ element_categories = request.form.getlist("new_element_categorie")
372
+ element_points = request.form.getlist("new_element_points")
373
+ for name, cat, pts in zip(element_names, element_categories, element_points):
374
+ if name and cat and pts:
375
+ evaluation.ajouter_element(name, cat, pts)
376
+
377
+ # Génération du document DOCX
378
+ filename = "evaluation_gymnastique.docx"
379
+ evaluation.generer_document(filename)
380
+
381
+ # Vérification du format de sortie demandé
382
+ output_format = request.form.get("format", "docx")
383
+ if output_format == "pdf":
384
+ pdf_filename = "evaluation_gymnastique.pdf"
385
+ # Conversion en PDF via LibreOffice en mode headless
386
+ command = [
387
+ "libreoffice",
388
+ "--headless",
389
+ "--convert-to",
390
+ "pdf",
391
+ filename,
392
+ "--outdir",
393
+ "."
394
+ ]
395
+ subprocess.run(command, check=True)
396
+ return send_file(pdf_filename, as_attachment=True)
397
+ else:
398
+ return send_file(filename, as_attachment=True)
399
+
400
+ return render_template("index.html")
401
+
402
+ if __name__ == "__main__":
403
+ app.run(debug=True)