Docfile commited on
Commit
17284b5
·
verified ·
1 Parent(s): b6167f0

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -459
app.py DELETED
@@ -1,459 +0,0 @@
1
- from flask import Flask, render_template, request, send_file
2
- import os
3
- import convertapi
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
- from docx.oxml.shared import qn
11
- from docx.oxml import OxmlElement
12
- # Configuration des identifiants ConvertAPI
13
- convertapi.api_credentials = 'secret_8wCI6pgOP9AxLVJG'
14
-
15
- # --- Classe de génération de document ---
16
- class EvaluationGymnique:
17
- def __init__(self):
18
- self.document = Document()
19
- self.document.sections[0].page_height = Cm(29.7)
20
- self.document.sections[0].page_width = Cm(21)
21
- self.document.sections[0].left_margin = Cm(1.5)
22
- self.document.sections[0].right_margin = Cm(1.5)
23
- self.document.sections[0].top_margin = Cm(1)
24
- self.document.sections[0].bottom_margin = Cm(1)
25
-
26
- # Informations d'en-tête par défaut
27
- self.centre_examen = "Centre d'examen"
28
- self.type_examen = "Bac Général"
29
- self.serie = "Série"
30
- self.etablissement = "Établissement"
31
- self.session = "2025"
32
- self.nom_candidat = "Candidat"
33
-
34
- # Liste vide pour les éléments techniques
35
- self.elements_techniques = []
36
- self.appreciations = ["M", "PM", "NM", "NR"]
37
-
38
- def ajouter_entete_colore(self):
39
- header_paragraph = self.document.add_paragraph()
40
- header_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
41
- header_paragraph.space_after = Pt(6)
42
- header_run = header_paragraph.add_run("ÉVALUATION GYMNASTIQUE")
43
- header_run.bold = True
44
- header_run.font.size = Pt(14)
45
- header_run.font.color.rgb = RGBColor(0, 32, 96)
46
-
47
- header_table = self.document.add_table(rows=3, cols=2)
48
- header_table.style = 'Table Grid'
49
- header_table.autofit = False
50
-
51
- for row in header_table.rows:
52
- row.height = Cm(0.8)
53
- for row in header_table.rows:
54
- for cell in row.cells:
55
- shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="D9E2F3"/>')
56
- cell._tc.get_or_add_tcPr().append(shading_elm)
57
-
58
- # Première ligne
59
- cell = header_table.cell(0, 0)
60
- paragraph = cell.paragraphs[0]
61
- run = paragraph.add_run("Centre d'examen: ")
62
- run.bold = True
63
- run.font.size = Pt(10)
64
- run.font.color.rgb = RGBColor(0, 32, 96)
65
- paragraph.add_run(self.centre_examen).font.size = Pt(10)
66
-
67
- cell = header_table.cell(0, 1)
68
- paragraph = cell.paragraphs[0]
69
- run = paragraph.add_run("Examen: ")
70
- run.bold = True
71
- run.font.size = Pt(10)
72
- run.font.color.rgb = RGBColor(0, 32, 96)
73
- paragraph.add_run(self.type_examen).font.size = Pt(10)
74
-
75
- # Deuxième ligne
76
- cell = header_table.cell(1, 0)
77
- paragraph = cell.paragraphs[0]
78
- run = paragraph.add_run("Série: ")
79
- run.bold = True
80
- run.font.size = Pt(10)
81
- run.font.color.rgb = RGBColor(0, 32, 96)
82
- paragraph.add_run(self.serie).font.size = Pt(10)
83
-
84
- cell = header_table.cell(1, 1)
85
- paragraph = cell.paragraphs[0]
86
- run = paragraph.add_run("Établissement: ")
87
- run.bold = True
88
- run.font.size = Pt(10)
89
- run.font.color.rgb = RGBColor(0, 32, 96)
90
- paragraph.add_run(self.etablissement).font.size = Pt(10)
91
-
92
- # Troisième ligne
93
- cell = header_table.cell(2, 0)
94
- paragraph = cell.paragraphs[0]
95
- run = paragraph.add_run("Session: ")
96
- run.bold = True
97
- run.font.size = Pt(10)
98
- run.font.color.rgb = RGBColor(0, 32, 96)
99
- paragraph.add_run(self.session).font.size = Pt(10)
100
-
101
- cell = header_table.cell(2, 1)
102
- paragraph = cell.paragraphs[0]
103
- run = paragraph.add_run("Candidat: ")
104
- run.bold = True
105
- run.font.size = Pt(10)
106
- run.font.color.rgb = RGBColor(0, 32, 96)
107
- paragraph.add_run(self.nom_candidat).font.size = Pt(10)
108
-
109
- self.document.add_paragraph().space_after = Pt(4)
110
-
111
- def _add_image_relation(self, image_path):
112
- """Ajoute une relation d'image et retourne l'ID"""
113
- # Récupérer la partie du document
114
- part = self.document.part
115
-
116
- # Ajouter l'image à la partie
117
- with open(image_path, 'rb') as f:
118
- image_blob = f.read()
119
- image_part = part.package.image_parts.get_or_add_image_part(image_blob)
120
-
121
- # Créer une relation entre la partie du document et l'image
122
- rId = part.relate_to(image_part, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/image')
123
-
124
- return rId
125
-
126
- def creer_tableau_elements(self, image_path=None):
127
- table = self.document.add_table(rows=len(self.elements_techniques) + 1, cols=5)
128
- table.style = 'Table Grid'
129
- table.alignment = WD_TABLE_ALIGNMENT.CENTER
130
-
131
- # Configuration des largeurs de colonnes
132
- for cell in table.columns[0].cells:
133
- cell.width = Cm(8)
134
- for cell in table.columns[1].cells:
135
- cell.width = Cm(3)
136
- for cell in table.columns[2].cells:
137
- cell.width = Cm(2)
138
- for cell in table.columns[3].cells:
139
- cell.width = Cm(2.5)
140
- for cell in table.columns[4].cells:
141
- cell.width = Cm(2.5)
142
-
143
- # Ajouter une image en arrière-plan si fournie
144
- if image_path and os.path.exists(image_path):
145
- # Accéder à l'élément XML du tableau
146
- tbl = table._tbl
147
-
148
- # Créer l'élément tblPr s'il n'existe pas
149
- tblPr = tbl.get_or_add_tblPr()
150
-
151
- # Créer le namespace nécessaire
152
- nsPrefix = {'xmlns:v': 'urn:schemas-microsoft-com:vml',
153
- 'xmlns:o': 'urn:schemas-microsoft-com:office:office',
154
- 'xmlns:r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'}
155
-
156
- # Ajouter les namespaces au document
157
- for prefix, uri in nsPrefix.items():
158
- tbl.set(prefix, uri)
159
-
160
- # Créer l'élément pour l'image d'arrière-plan
161
- background = OxmlElement('w:background')
162
- background.set(qn('w:color'), "auto")
163
- background.set(qn('w:themeColor'), "background1")
164
-
165
- # Créer les éléments VML pour l'image
166
- vFill = OxmlElement('v:fill')
167
- rId = self._add_image_relation(image_path)
168
- vFill.set(qn('r:id'), rId)
169
- vFill.set('type', 'frame')
170
- vFill.set('src', os.path.basename(image_path))
171
- vFill.set('o:title', "Background")
172
- vFill.set('opacity', '0.3')
173
-
174
- background.append(vFill)
175
- tblPr.append(background)
176
-
177
- for row in table.rows:
178
- row.height = Cm(1)
179
-
180
- # En-tête du tableau
181
- header_row = table.rows[0]
182
- for cell in header_row.cells:
183
- shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="BDD7EE"/>')
184
- cell._tc.get_or_add_tcPr().append(shading_elm)
185
-
186
- headers = ["ELEMENTS TECHNIQUES", "CATEGORIES D'ELEMENTS TECHNIQUES ET PONDERATION", "", "APPRECIATIONS", "POINTS Accordés"]
187
- for i, header in enumerate(headers):
188
- cell = table.cell(0, i)
189
- cell.text = header
190
- cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
191
- run = cell.paragraphs[0].runs[0]
192
- run.bold = True
193
- run.font.size = Pt(9)
194
- run.font.color.rgb = RGBColor(0, 32, 96)
195
- table.cell(0, 1).merge(table.cell(0, 2))
196
-
197
- # Ajout des éléments techniques
198
- for i, element in enumerate(self.elements_techniques, 1):
199
- element_cell = table.cell(i, 0)
200
- element_cell.text = element["nom"]
201
- element_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.LEFT
202
- run = element_cell.paragraphs[0].runs[0]
203
- run.bold = True
204
- run.font.size = Pt(9)
205
-
206
- categorie_cell = table.cell(i, 1)
207
- categorie_cell.text = element["categorie"]
208
- categorie_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
209
- run = categorie_cell.paragraphs[0].runs[0]
210
- run.bold = True
211
- run.font.size = Pt(9)
212
- run.italic = True
213
-
214
- points_cell = table.cell(i, 2)
215
- points_cell.text = str(element["points"])
216
- points_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
217
- run = points_cell.paragraphs[0].runs[0]
218
- run.bold = True
219
- run.font.size = Pt(9)
220
- run.italic = True
221
-
222
- def ajouter_note_jury(self):
223
- para = self.document.add_paragraph()
224
- para.space_before = Pt(4)
225
- para.space_after = Pt(4)
226
- 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.")
227
- run.bold = True
228
- run.font.color.rgb = RGBColor(255, 0, 0)
229
- run.font.size = Pt(9)
230
-
231
- def creer_tableau_recapitulatif(self):
232
- note_table = self.document.add_table(rows=3, cols=13)
233
- note_table.style = 'Table Grid'
234
- note_table.alignment = WD_TABLE_ALIGNMENT.CENTER
235
-
236
- for row in note_table.rows:
237
- row.height = Cm(0.6)
238
- for cell in note_table.rows[0].cells:
239
- shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="BDD7EE"/>')
240
- cell._tc.get_or_add_tcPr().append(shading_elm)
241
-
242
- for col, (type_lettre, points) in enumerate([("A", "1pt"), ("B", "1,5pt"), ("C", "2pts"), ("D", "2,5pts"), ("E", "3pts")]):
243
- idx = col * 2
244
- if idx + 1 < len(note_table.columns):
245
- cell = note_table.cell(0, idx)
246
- cell.merge(note_table.cell(0, idx + 1))
247
- cell.text = f"Type {type_lettre}\n{points}"
248
- cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
249
- for run in cell.paragraphs[0].runs:
250
- run.bold = True
251
- run.font.size = Pt(9)
252
- run.font.color.rgb = RGBColor(0, 32, 96)
253
-
254
- for col, (titre, points) in enumerate([("ROV", "2pts"), ("Projet", "2pts"), ("Réalisation", "16pts")], 10):
255
- if col < len(note_table.columns):
256
- cell = note_table.cell(0, col)
257
- cell.text = f"{titre}\n{points}"
258
- cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
259
- for run in cell.paragraphs[0].runs:
260
- run.bold = True
261
- run.font.size = Pt(9)
262
- run.font.color.rgb = RGBColor(0, 32, 96)
263
-
264
- for col in range(5):
265
- idx = col * 2
266
- if idx + 1 < len(note_table.columns):
267
- neg_cell = note_table.cell(1, idx)
268
- neg_cell.text = "NEG"
269
- neg_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
270
- for run in neg_cell.paragraphs[0].runs:
271
- run.italic = True
272
- run.font.size = Pt(9)
273
-
274
- note_cell = note_table.cell(1, idx + 1)
275
- note_cell.text = "Note"
276
- note_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
277
- for run in note_cell.paragraphs[0].runs:
278
- run.italic = True
279
- run.font.size = Pt(9)
280
-
281
- for col in range(10, 13):
282
- if col < len(note_table.columns):
283
- cell = note_table.cell(1, col)
284
- cell.text = "Note"
285
- cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
286
- for run in cell.paragraphs[0].runs:
287
- run.italic = True
288
- run.font.size = Pt(9)
289
-
290
- for col, type_lettre in enumerate(["A", "B", "C", "D", "E"]):
291
- idx = col * 2
292
- if idx < len(note_table.columns):
293
- neg_cell = note_table.cell(2, idx)
294
- neg_cell.text = ""
295
- neg_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
296
-
297
- def ajouter_note_candidat_avec_cadre(self):
298
- note_table = self.document.add_table(rows=1, cols=1)
299
- note_table.style = 'Table Grid'
300
- note_table.alignment = WD_TABLE_ALIGNMENT.CENTER
301
-
302
- cell = note_table.cell(0, 0)
303
- shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="E2EFDA"/>')
304
- cell._tc.get_or_add_tcPr().append(shading_elm)
305
-
306
- p = cell.paragraphs[0]
307
- 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).")
308
- run.italic = True
309
- run.font.size = Pt(8)
310
-
311
- def ajouter_zone_note(self):
312
- zone_note = self.document.add_table(rows=1, cols=2)
313
- zone_note.style = 'Table Grid'
314
- zone_note.alignment = WD_TABLE_ALIGNMENT.RIGHT
315
-
316
- # Définir une largeur fixe pour les deux cellules
317
- cell_width = Cm(2)
318
-
319
- cell_libelle = zone_note.cell(0, 0)
320
- cell_libelle.width = cell_width
321
- cell_libelle.text = "Note finale"
322
- cell_libelle.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.RIGHT
323
- run = cell_libelle.paragraphs[0].runs[0]
324
- run.bold = True
325
- run.font.size = Pt(12)
326
- run.font.color.rgb = RGBColor(0, 32, 96)
327
-
328
- cell_saisie = zone_note.cell(0, 1)
329
- cell_saisie.width = cell_width
330
- zone_note.rows[0].height = cell_width
331
- shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="F2F2F2"/>')
332
- cell_saisie._tc.get_or_add_tcPr().append(shading_elm)
333
- cell_saisie.text = "/20"
334
- cell_saisie.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
335
- run = cell_saisie.paragraphs[0].runs[0]
336
- run.bold = True
337
- run.font.size = Pt(12)
338
-
339
- # Ajouter plus d'espace avant la liste des correcteurs
340
- self.document.add_paragraph().space_after = Pt(10)
341
- self.document.add_paragraph().space_after = Pt(10)
342
-
343
- def ajouter_lignes_correcteurs(self):
344
- para_intro = self.document.add_paragraph()
345
- para_intro.space_before = Pt(6)
346
-
347
- for role in ["Projet", "principal", "ROV"]:
348
- para = self.document.add_paragraph()
349
- para.space_before = Pt(4)
350
- para.space_after = Pt(4)
351
- run = para.add_run(f"Correcteur {role} : ")
352
- run.bold = True
353
- para.add_run(".................................................................")
354
-
355
- # Méthodes de modification des données
356
- def modifier_centre_examen(self, nom):
357
- self.centre_examen = nom
358
-
359
- def modifier_type_examen(self, type_examen):
360
- self.type_examen = type_examen
361
-
362
- def modifier_serie(self, serie):
363
- self.serie = serie
364
-
365
- def modifier_etablissement(self, nom):
366
- self.etablissement = nom
367
-
368
- def modifier_session(self, annee):
369
- self.session = annee
370
-
371
- def modifier_candidat(self, nom):
372
- self.nom_candidat = nom
373
-
374
- def ajouter_element(self, nom, categorie, points):
375
- self.elements_techniques.append({
376
- "nom": nom,
377
- "categorie": categorie,
378
- "points": float(points)
379
- })
380
-
381
- def generer_document(self, nom_fichier="evaluation_gymnastique.docx", image_path=None):
382
- self.ajouter_entete_colore()
383
- self.creer_tableau_elements(image_path=image_path)
384
- self.ajouter_note_jury()
385
- self.creer_tableau_recapitulatif()
386
- self.ajouter_lignes_correcteurs()
387
- self.ajouter_zone_note()
388
- self.ajouter_note_candidat_avec_cadre()
389
- self.document.save(nom_fichier)
390
- return nom_fichier
391
-
392
- # --- Application Flask ---
393
- app = Flask(__name__)
394
-
395
- # Assurer que le dossier de téléchargements existe
396
- UPLOAD_FOLDER = 'uploads'
397
- os.makedirs(UPLOAD_FOLDER, exist_ok=True)
398
-
399
- @app.route("/", methods=["GET", "POST"])
400
- def index():
401
- if request.method == "POST":
402
- # Récupération des informations depuis le formulaire
403
- centre_examen = request.form.get("centre_examen", "Centre d'examen")
404
- type_examen = request.form.get("type_examen", "Bac Général")
405
- serie = request.form.get("serie", "Série")
406
- etablissement = request.form.get("etablissement", "Établissement")
407
- session_value = request.form.get("session", "2025")
408
- nom_candidat = request.form.get("nom_candidat", "Candidat")
409
-
410
- # Gérer le téléchargement de l'image
411
- background_image = None
412
- if 'background_image' in request.files:
413
- file = request.files['background_image']
414
- if file.filename != '':
415
- image_path = os.path.join(UPLOAD_FOLDER, file.filename)
416
- file.save(image_path)
417
- background_image = image_path
418
-
419
- # Création et configuration du document
420
- evaluation = EvaluationGymnique()
421
- evaluation.modifier_centre_examen(centre_examen)
422
- evaluation.modifier_type_examen(type_examen)
423
- evaluation.modifier_serie(serie)
424
- evaluation.modifier_etablissement(etablissement)
425
- evaluation.modifier_session(session_value)
426
- evaluation.modifier_candidat(nom_candidat)
427
-
428
- # Récupération des éléments techniques ajoutés dynamiquement
429
- element_names = request.form.getlist("new_element_name")
430
- element_categories = request.form.getlist("new_element_categorie")
431
- element_points = request.form.getlist("new_element_points")
432
- for name, cat, pts in zip(element_names, element_categories, element_points):
433
- if name and cat and pts:
434
- evaluation.ajouter_element(name, cat, pts)
435
-
436
- # Génération du document DOCX
437
- filename = "evaluation_gymnastique.docx"
438
- evaluation.generer_document(filename, image_path=background_image)
439
-
440
- # Vérification du format de sortie demandé
441
- output_format = request.form.get("format", "docx")
442
- if output_format == "pdf":
443
- # Conversion en PDF via ConvertAPI
444
- result = convertapi.convert('pdf', {
445
- 'File': filename,
446
- 'FileName': 'evaluation_gymnastique',
447
- 'ConvertMetadata': 'false',
448
- 'ConvertHeadings': 'false'
449
- }, from_format='doc')
450
- pdf_filename = "evaluation_gymnastique.pdf"
451
- result.save_files(pdf_filename)
452
- return send_file(pdf_filename, as_attachment=True)
453
- else:
454
- return send_file(filename, as_attachment=True)
455
-
456
- return render_template("index.html")
457
-
458
- if __name__ == "__main__":
459
- app.run(debug=True)