File size: 16,977 Bytes
4d2fc58
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
from flask import Flask, render_template, request, send_file
import os
import docx
from docx import Document
from docx.shared import Pt, Inches, Cm, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_COLOR_INDEX
from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_TABLE_ALIGNMENT
from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml

# --- Classe de génération de document ---
class EvaluationGymnique:
    def __init__(self):
        self.document = Document()
        self.document.sections[0].page_height = Cm(29.7)
        self.document.sections[0].page_width = Cm(21)
        self.document.sections[0].left_margin = Cm(1.5)
        self.document.sections[0].right_margin = Cm(1.5)
        self.document.sections[0].top_margin = Cm(1)
        self.document.sections[0].bottom_margin = Cm(1)
        
        # Données par défaut
        self.centre_examen = "Centre d'examen"
        self.type_examen = "Bac Général"
        self.serie = "Série"
        self.etablissement = "Établissement"
        self.session = "2025"
        self.nom_candidat = "Candidat"
        self.elements_techniques = [
            {"nom": "Salutation", "categorie": "A", "points": 1},
            {"nom": "Roulade avant élevée", "categorie": "B", "points": 1.5},
            {"nom": "Croix de Saint André, Sursaut préparatoire", "categorie": "A", "points": 1},
            {"nom": "Roue à 2 mains, ¾ tour", "categorie": "A", "points": 1},
            {"nom": "Roulade arrière arrivée jambes écartées", "categorie": "B", "points": 1.5},
            {"nom": "Planche écrasée", "categorie": "A", "points": 1},
            {"nom": "Chandelle sans pose des mains", "categorie": "B", "points": 1.5},
            {"nom": "Roulade arrière arrivée jambes tendues et serrées", "categorie": "B", "points": 1.5},
            {"nom": "Planche costale", "categorie": "C", "points": 2},
            {"nom": "Elan couru, 2 roues enchainées", "categorie": "C", "points": 2},
            {"nom": "Rondade saut carpé jambes écarts Salutation", "categorie": "C", "points": 2}
        ]
        self.appreciations = ["M", "PM", "NM", "NR"]
    
    def ajouter_entete_colore(self):
        header_paragraph = self.document.add_paragraph()
        header_paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
        header_paragraph.space_after = Pt(6)
        header_run = header_paragraph.add_run("ÉVALUATION GYMNASTIQUE")
        header_run.bold = True
        header_run.font.size = Pt(14)
        header_run.font.color.rgb = RGBColor(0, 32, 96)
        
        header_table = self.document.add_table(rows=3, cols=2)
        header_table.style = 'Table Grid'
        header_table.autofit = False
        
        for row in header_table.rows:
            row.height = Cm(0.8)
        for row in header_table.rows:
            for cell in row.cells:
                shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="D9E2F3"/>')
                cell._tc.get_or_add_tcPr().append(shading_elm)
        
        # Première ligne
        cell = header_table.cell(0, 0)
        paragraph = cell.paragraphs[0]
        run = paragraph.add_run("Centre d'examen: ")
        run.bold = True
        run.font.size = Pt(10)
        run.font.color.rgb = RGBColor(0, 32, 96)
        paragraph.add_run(self.centre_examen).font.size = Pt(10)
        
        cell = header_table.cell(0, 1)
        paragraph = cell.paragraphs[0]
        run = paragraph.add_run("Examen: ")
        run.bold = True
        run.font.size = Pt(10)
        run.font.color.rgb = RGBColor(0, 32, 96)
        paragraph.add_run(self.type_examen).font.size = Pt(10)
        
        # Deuxième ligne
        cell = header_table.cell(1, 0)
        paragraph = cell.paragraphs[0]
        run = paragraph.add_run("Série: ")
        run.bold = True
        run.font.size = Pt(10)
        run.font.color.rgb = RGBColor(0, 32, 96)
        paragraph.add_run(self.serie).font.size = Pt(10)
        
        cell = header_table.cell(1, 1)
        paragraph = cell.paragraphs[0]
        run = paragraph.add_run("Établissement: ")
        run.bold = True
        run.font.size = Pt(10)
        run.font.color.rgb = RGBColor(0, 32, 96)
        paragraph.add_run(self.etablissement).font.size = Pt(10)
        
        # Troisième ligne
        cell = header_table.cell(2, 0)
        paragraph = cell.paragraphs[0]
        run = paragraph.add_run("Session: ")
        run.bold = True
        run.font.size = Pt(10)
        run.font.color.rgb = RGBColor(0, 32, 96)
        paragraph.add_run(self.session).font.size = Pt(10)
        
        cell = header_table.cell(2, 1)
        paragraph = cell.paragraphs[0]
        run = paragraph.add_run("Candidat: ")
        run.bold = True
        run.font.size = Pt(10)
        run.font.color.rgb = RGBColor(0, 32, 96)
        paragraph.add_run(self.nom_candidat).font.size = Pt(10)
        
        self.document.add_paragraph().space_after = Pt(4)
    
    def creer_tableau_elements(self):
        table = self.document.add_table(rows=len(self.elements_techniques) + 1, cols=5)
        table.style = 'Table Grid'
        table.alignment = WD_TABLE_ALIGNMENT.CENTER
        
        for cell in table.columns[0].cells:
            cell.width = Cm(8)
        for cell in table.columns[1].cells:
            cell.width = Cm(3)
        for cell in table.columns[2].cells:
            cell.width = Cm(2)
        for cell in table.columns[3].cells:
            cell.width = Cm(2.5)
        for cell in table.columns[4].cells:
            cell.width = Cm(2.5)
        
        for row in table.rows:
            row.height = Cm(1)
        
        header_row = table.rows[0]
        for cell in header_row.cells:
            shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="BDD7EE"/>')
            cell._tc.get_or_add_tcPr().append(shading_elm)
        
        headers = ["ELEMENTS TECHNIQUES", "CATEGORIES D'ELEMENTS TECHNIQUES ET PONDERATION", "", "APPRECIATIONS", "POINTS Accordés"]
        for i, header in enumerate(headers):
            cell = table.cell(0, i)
            cell.text = header
            cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
            run = cell.paragraphs[0].runs[0]
            run.bold = True
            run.font.size = Pt(9)
            run.font.color.rgb = RGBColor(0, 32, 96)
        table.cell(0, 1).merge(table.cell(0, 2))
        
        for i, element in enumerate(self.elements_techniques, 1):
            element_cell = table.cell(i, 0)
            element_cell.text = element["nom"]
            element_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.LEFT
            run = element_cell.paragraphs[0].runs[0]
            run.bold = True
            run.font.size = Pt(9)
            
            categorie_cell = table.cell(i, 1)
            categorie_cell.text = element["categorie"]
            categorie_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
            run = categorie_cell.paragraphs[0].runs[0]
            run.bold = True
            run.font.size = Pt(9)
            run.italic = True
            
            points_cell = table.cell(i, 2)
            points_cell.text = str(element["points"])
            points_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
            run = points_cell.paragraphs[0].runs[0]
            run.bold = True
            run.font.size = Pt(9)
            run.italic = True
            # Colonnes "APPRECIATIONS" et "POINTS Accordés" laissées vides
        
    def ajouter_note_jury(self):
        para = self.document.add_paragraph()
        para.space_before = Pt(4)
        para.space_after = Pt(4)
        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.")
        run.bold = True
        run.font.color.rgb = RGBColor(255, 0, 0)
        run.font.size = Pt(9)
    
    def creer_tableau_recapitulatif(self):
        note_table = self.document.add_table(rows=3, cols=13)
        note_table.style = 'Table Grid'
        note_table.alignment = WD_TABLE_ALIGNMENT.CENTER
        
        for row in note_table.rows:
            row.height = Cm(0.6)
        for cell in note_table.rows[0].cells:
            shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="BDD7EE"/>')
            cell._tc.get_or_add_tcPr().append(shading_elm)
        
        for col, (type_lettre, points) in enumerate([("A", "1pt"), ("B", "1,5pt"), ("C", "2pts"), ("D", "2,5pts"), ("E", "3pts")]):
            idx = col * 2
            if idx + 1 < len(note_table.columns):
                cell = note_table.cell(0, idx)
                cell.merge(note_table.cell(0, idx + 1))
                cell.text = f"Type {type_lettre}\n{points}"
                cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
                for run in cell.paragraphs[0].runs:
                    run.bold = True
                    run.font.size = Pt(9)
                    run.font.color.rgb = RGBColor(0, 32, 96)
        
        for col, (titre, points) in enumerate([("ROV", "2pts"), ("Projet", "2pts"), ("Réalisation", "16pts")], 10):
            if col < len(note_table.columns):
                cell = note_table.cell(0, col)
                cell.text = f"{titre}\n{points}"
                cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
                for run in cell.paragraphs[0].runs:
                    run.bold = True
                    run.font.size = Pt(9)
                    run.font.color.rgb = RGBColor(0, 32, 96)
        
        for col in range(5):
            idx = col * 2
            if idx + 1 < len(note_table.columns):
                neg_cell = note_table.cell(1, idx)
                neg_cell.text = "NEG"
                neg_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
                for run in neg_cell.paragraphs[0].runs:
                    run.italic = True
                    run.font.size = Pt(9)
                
                note_cell = note_table.cell(1, idx + 1)
                note_cell.text = "Note"
                note_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
                for run in note_cell.paragraphs[0].runs:
                    run.italic = True
                    run.font.size = Pt(9)
        
        for col in range(10, 13):
            if col < len(note_table.columns):
                cell = note_table.cell(1, col)
                cell.text = "Note"
                cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
                for run in cell.paragraphs[0].runs:
                    run.italic = True
                    run.font.size = Pt(9)
        
        for col, type_lettre in enumerate(["A", "B", "C", "D", "E"]):
            idx = col * 2
            if idx < len(note_table.columns):
                neg_cell = note_table.cell(2, idx)
                neg_cell.text = ""
                neg_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
    
    def ajouter_note_candidat_avec_cadre(self):
        note_table = self.document.add_table(rows=1, cols=1)
        note_table.style = 'Table Grid'
        note_table.alignment = WD_TABLE_ALIGNMENT.CENTER
        
        cell = note_table.cell(0, 0)
        shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="E2EFDA"/>')
        cell._tc.get_or_add_tcPr().append(shading_elm)
        
        p = cell.paragraphs[0]
        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) aussi, il sera exigé au candidat 2 copies de son projet sur une page! appréciations:NR (non réalisation) NM (non maitrisé), PM (peu maitrisé) et M (maitrisé).")
        run.italic = True
        run.font.size = Pt(8)
    
    def ajouter_zone_note(self):
        zone_note = self.document.add_table(rows=1, cols=2)
        zone_note.style = 'Table Grid'
        zone_note.alignment = WD_TABLE_ALIGNMENT.RIGHT
        
        cell_libelle = zone_note.cell(0, 0)
        cell_libelle.text = "Note finale"
        cell_libelle.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.RIGHT
        run = cell_libelle.paragraphs[0].runs[0]
        run.bold = True
        run.font.size = Pt(12)
        run.font.color.rgb = RGBColor(0, 32, 96)
        
        cell_saisie = zone_note.cell(0, 1)
        cell_saisie.width = Cm(1.5)
        zone_note.rows[0].height = Cm(1.5)
        shading_elm = parse_xml(f'<w:shd {nsdecls("w")} w:fill="F2F2F2"/>')
        cell_saisie._tc.get_or_add_tcPr().append(shading_elm)
        cell_saisie.text = "/20"
        cell_saisie.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
        run = cell_saisie.paragraphs[0].runs[0]
        run.bold = True
        run.font.size = Pt(12)
        
        self.document.add_paragraph()
    
    def ajouter_lignes_correcteurs(self):
        for role in ["Projet", "principal", "ROV"]:
            para = self.document.add_paragraph()
            para.space_before = Pt(2)
            para.space_after = Pt(2)
            run = para.add_run(f"Correcteur {role} : ")
            run.bold = True
            para.add_run(".................................................................")
    
    # Méthodes de modification des données
    def modifier_centre_examen(self, nom):
        self.centre_examen = nom
    
    def modifier_type_examen(self, type_examen):
        self.type_examen = type_examen
    
    def modifier_serie(self, serie):
        self.serie = serie
    
    def modifier_etablissement(self, nom):
        self.etablissement = nom
    
    def modifier_session(self, annee):
        self.session = annee
    
    def modifier_candidat(self, nom):
        self.nom_candidat = nom
    
    def ajouter_element(self, nom, categorie, points):
        self.elements_techniques.append({
            "nom": nom,
            "categorie": categorie,
            "points": float(points)
        })
    
    def supprimer_element(self, index):
        if 0 <= index < len(self.elements_techniques):
            del self.elements_techniques[index]
    
    def modifier_element(self, index, nom=None, categorie=None, points=None):
        if 0 <= index < len(self.elements_techniques):
            if nom:
                self.elements_techniques[index]["nom"] = nom
            if categorie:
                self.elements_techniques[index]["categorie"] = categorie
            if points:
                self.elements_techniques[index]["points"] = float(points)
    
    def generer_document(self, nom_fichier="evaluation_gymnastique.docx"):
        self.ajouter_entete_colore()
        self.creer_tableau_elements()
        self.ajouter_note_jury()
        self.creer_tableau_recapitulatif()
        self.ajouter_lignes_correcteurs()
        self.ajouter_zone_note()
        self.ajouter_note_candidat_avec_cadre()
        self.document.save(nom_fichier)
        return nom_fichier

# --- Application Flask ---
app = Flask(__name__)

@app.route("/", methods=["GET", "POST"])
def index():
    if request.method == "POST":
        # Récupération des données du formulaire
        centre_examen = request.form.get("centre_examen", "Centre d'examen")
        type_examen = request.form.get("type_examen", "Bac Général")
        serie = request.form.get("serie", "Série")
        etablissement = request.form.get("etablissement", "Établissement")
        session_value = request.form.get("session", "2025")
        nom_candidat = request.form.get("nom_candidat", "Candidat")
        
        # Création de l'instance et modification des données
        evaluation = EvaluationGymnique()
        evaluation.modifier_centre_examen(centre_examen)
        evaluation.modifier_type_examen(type_examen)
        evaluation.modifier_serie(serie)
        evaluation.modifier_etablissement(etablissement)
        evaluation.modifier_session(session_value)
        evaluation.modifier_candidat(nom_candidat)
        
        # Optionnel : ajout d'un nouvel élément technique si renseigné
        new_element_name = request.form.get("new_element_name")
        new_element_categorie = request.form.get("new_element_categorie")
        new_element_points = request.form.get("new_element_points")
        if new_element_name and new_element_categorie and new_element_points:
            evaluation.ajouter_element(new_element_name, new_element_categorie, new_element_points)
        
        # Génération du document
        filename = "evaluation_gymnastique.docx"
        evaluation.generer_document(filename)
        
        # Envoi du fichier généré en téléchargement
        return send_file(filename, as_attachment=True)
    
    return render_template("index.html")

if __name__ == "__main__":
    app.run(debug=True)