refactor: standardize string quotes and improve code formatting
Updated both main.py and generate_math_exercises.py to use consistent double quotes for strings and improved code formatting with better line breaks for readability. Also added JSON import to main.py for bulk operations.
This commit is contained in:
+98
-59
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import random
|
||||
import os
|
||||
import io
|
||||
@@ -7,8 +8,8 @@ import zipfile
|
||||
import tempfile
|
||||
from botocore.exceptions import ClientError
|
||||
from typing import List
|
||||
from fastapi import FastAPI, Request, Response, Form
|
||||
from fastapi.responses import HTMLResponse, StreamingResponse
|
||||
from fastapi import FastAPI, Request, Form
|
||||
from fastapi.responses import HTMLResponse, RedirectResponse, StreamingResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
from pydantic import BaseModel
|
||||
from fpdf import FPDF
|
||||
@@ -16,29 +17,30 @@ from fpdf import FPDF
|
||||
app = FastAPI()
|
||||
|
||||
# S3 Configuration
|
||||
S3_BUCKET_NAME = os.environ.get('S3_BUCKET_NAME', 'math-exercises')
|
||||
S3_BUCKET_NAME = os.environ.get("S3_BUCKET_NAME", "math-exercises")
|
||||
|
||||
templates = Jinja2Templates(directory="app/templates")
|
||||
|
||||
|
||||
def get_s3_client():
|
||||
"""Create and return an S3 client using environment variables"""
|
||||
s3_access_key = os.environ.get('S3_ACCESS_KEY')
|
||||
s3_secret_key = os.environ.get('S3_SECRET_KEY')
|
||||
s3_host_base = os.environ.get('S3_HOST_BASE')
|
||||
s3_host_bucket = os.environ.get('S3_HOST_BUCKET')
|
||||
s3_access_key = os.environ.get("S3_ACCESS_KEY")
|
||||
s3_secret_key = os.environ.get("S3_SECRET_KEY")
|
||||
s3_host_base = os.environ.get("S3_HOST_BASE")
|
||||
|
||||
if not all([s3_access_key, s3_secret_key, s3_host_base]):
|
||||
raise ValueError("S3 environment variables not properly set")
|
||||
|
||||
s3 = boto3.client(
|
||||
's3',
|
||||
"s3",
|
||||
aws_access_key_id=s3_access_key,
|
||||
aws_secret_access_key=s3_secret_key,
|
||||
endpoint_url=s3_host_base,
|
||||
region_name='us-east-1' # Required but unused for Infomaniak
|
||||
region_name="us-east-1", # Required but unused for Infomaniak
|
||||
)
|
||||
return s3
|
||||
|
||||
|
||||
def create_bucket_if_not_exists(bucket_name):
|
||||
"""Create S3 bucket if it doesn't exist"""
|
||||
s3_client = get_s3_client()
|
||||
@@ -46,7 +48,7 @@ def create_bucket_if_not_exists(bucket_name):
|
||||
try:
|
||||
s3_client.head_bucket(Bucket=bucket_name)
|
||||
except ClientError as e:
|
||||
error_code = int(e.response['Error']['Code'])
|
||||
error_code = int(e.response["Error"]["Code"])
|
||||
if error_code == 404:
|
||||
# Bucket doesn't exist, create it
|
||||
try:
|
||||
@@ -60,13 +62,15 @@ def create_bucket_if_not_exists(bucket_name):
|
||||
print(f"Error checking bucket: {e}")
|
||||
raise
|
||||
|
||||
|
||||
# Create bucket on startup
|
||||
try:
|
||||
create_bucket_if_not_exists(S3_BUCKET_NAME)
|
||||
except Exception as e:
|
||||
print(f"Warning: Could not create/check S3 bucket: {e}")
|
||||
|
||||
def upload_to_s3(file_data, bucket_name, object_name, content_type='application/pdf'):
|
||||
|
||||
def upload_to_s3(file_data, bucket_name, object_name, content_type="application/pdf"):
|
||||
"""Upload file data to S3 bucket"""
|
||||
s3_client = get_s3_client()
|
||||
|
||||
@@ -75,34 +79,38 @@ def upload_to_s3(file_data, bucket_name, object_name, content_type='application/
|
||||
Bucket=bucket_name,
|
||||
Key=object_name,
|
||||
Body=file_data,
|
||||
ContentType=content_type
|
||||
ContentType=content_type,
|
||||
)
|
||||
return True
|
||||
except ClientError as e:
|
||||
print(f"Error uploading to S3: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def download_from_s3(bucket_name, object_name):
|
||||
"""Download file data from S3 bucket"""
|
||||
s3_client = get_s3_client()
|
||||
|
||||
try:
|
||||
response = s3_client.get_object(Bucket=bucket_name, Key=object_name)
|
||||
return response['Body'].read()
|
||||
return response["Body"].read()
|
||||
except ClientError as e:
|
||||
print(f"Error downloading from S3: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def list_objects_in_s3(bucket_name):
|
||||
"""List all objects in S3 bucket (sorted from newest to oldest)"""
|
||||
s3_client = get_s3_client()
|
||||
|
||||
try:
|
||||
response = s3_client.list_objects_v2(Bucket=bucket_name)
|
||||
if 'Contents' in response:
|
||||
if "Contents" in response:
|
||||
# Filter for PDF files only and sort by last modified (newest first)
|
||||
pdf_files = [obj for obj in response['Contents'] if obj['Key'].endswith('.pdf')]
|
||||
pdf_files.sort(key=lambda x: x['LastModified'], reverse=True)
|
||||
pdf_files = [
|
||||
obj for obj in response["Contents"] if obj["Key"].endswith(".pdf")
|
||||
]
|
||||
pdf_files.sort(key=lambda x: x["LastModified"], reverse=True)
|
||||
return pdf_files
|
||||
else:
|
||||
return []
|
||||
@@ -110,6 +118,7 @@ def list_objects_in_s3(bucket_name):
|
||||
print(f"Error listing objects in S3: {e}")
|
||||
return []
|
||||
|
||||
|
||||
def delete_from_s3(bucket_name, object_name):
|
||||
"""Delete file from S3 bucket"""
|
||||
s3_client = get_s3_client()
|
||||
@@ -121,26 +130,42 @@ def delete_from_s3(bucket_name, object_name):
|
||||
print(f"Error deleting from S3: {e}")
|
||||
return False
|
||||
|
||||
|
||||
class MathExercisesPDF(FPDF):
|
||||
def header(self):
|
||||
self.set_font('Helvetica', 'B', 16)
|
||||
self.cell(0, 10, 'Exercices de Multiplication et Division', 0, 1, 'C', new_x="LMARGIN", new_y="NEXT")
|
||||
self.set_font("Helvetica", "B", 16)
|
||||
self.cell(
|
||||
0,
|
||||
10,
|
||||
"Exercices de Multiplication et Division",
|
||||
0,
|
||||
1,
|
||||
"C",
|
||||
new_x="LMARGIN",
|
||||
new_y="NEXT",
|
||||
)
|
||||
self.ln(10)
|
||||
|
||||
def footer(self):
|
||||
self.set_y(-15)
|
||||
self.set_font('Helvetica', 'I', 8)
|
||||
self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C', new_x="RIGHT", new_y="TOP")
|
||||
self.set_font("Helvetica", "I", 8)
|
||||
self.cell(
|
||||
0, 10, f"Page {self.page_no()}", 0, 0, "C", new_x="RIGHT", new_y="TOP"
|
||||
)
|
||||
|
||||
|
||||
class ExerciseRequest(BaseModel):
|
||||
min_table: int
|
||||
max_table: int
|
||||
num_exercises: int = 15
|
||||
|
||||
def generate_exercises(min_table: int, max_table: int, num_exercises: int = 15) -> List[str]:
|
||||
|
||||
def generate_exercises(
|
||||
min_table: int, max_table: int, num_exercises: int = 15
|
||||
) -> List[str]:
|
||||
"""Génère des exercices de multiplication et division aléatoires mélangés sans doublons"""
|
||||
exercises = []
|
||||
used_operations = set() # Pour éviter les doublons
|
||||
exercises: List[str] = []
|
||||
used_operations: set = set() # Pour éviter les doublons
|
||||
|
||||
# Générer le nombre exact d'exercices demandé
|
||||
attempts = 0
|
||||
@@ -155,9 +180,9 @@ def generate_exercises(min_table: int, max_table: int, num_exercises: int = 15)
|
||||
result = a * b
|
||||
|
||||
# Choisir aléatoirement le type d'exercice (seulement multiplication ou division)
|
||||
exercise_type = random.choice(['multiplication', 'division'])
|
||||
exercise_type = random.choice(["multiplication", "division"])
|
||||
|
||||
if exercise_type == 'multiplication':
|
||||
if exercise_type == "multiplication":
|
||||
# Exercice de multiplication
|
||||
operation_key = f"mult_{a}_{b}" # Clé unique pour cette opération
|
||||
if operation_key not in used_operations:
|
||||
@@ -184,9 +209,9 @@ def generate_exercises(min_table: int, max_table: int, num_exercises: int = 15)
|
||||
result = a * b
|
||||
|
||||
# Choisir aléatoirement le type d'exercice
|
||||
exercise_type = random.choice(['multiplication', 'division'])
|
||||
exercise_type = random.choice(["multiplication", "division"])
|
||||
|
||||
if exercise_type == 'multiplication':
|
||||
if exercise_type == "multiplication":
|
||||
exercise = f"{a} · {b} = ____"
|
||||
else: # division
|
||||
divisor = random.choice([a, b])
|
||||
@@ -196,7 +221,10 @@ def generate_exercises(min_table: int, max_table: int, num_exercises: int = 15)
|
||||
|
||||
return exercises
|
||||
|
||||
def create_math_exercises_pdf(min_table: int, max_table: int, num_exercises: int = 15) -> str:
|
||||
|
||||
def create_math_exercises_pdf(
|
||||
min_table: int, max_table: int, num_exercises: int = 15
|
||||
) -> str:
|
||||
"""Crée un fichier PDF avec des exercices de mathématiques mélangés en 3 colonnes et l'upload sur S3"""
|
||||
import datetime
|
||||
|
||||
@@ -205,17 +233,19 @@ def create_math_exercises_pdf(min_table: int, max_table: int, num_exercises: int
|
||||
|
||||
# Ajouter des informations sur la plage de tables
|
||||
if min_table == max_table:
|
||||
table_info = f'Tables de multiplication et division pour {min_table}'
|
||||
pdf_filename = f'exercices_mathematiques_table_{min_table}_{num_exercises}_exercices_{timestamp}.pdf'
|
||||
table_info = f"Tables de multiplication et division pour {min_table}"
|
||||
pdf_filename = f"exercices_mathematiques_table_{min_table}_{num_exercises}_exercices_{timestamp}.pdf"
|
||||
else:
|
||||
table_info = f'Tables de multiplication et division de {min_table} à {max_table}'
|
||||
pdf_filename = f'exercices_mathematiques_tables_{min_table}_a_{max_table}_{num_exercises}_exercices_{timestamp}.pdf'
|
||||
table_info = (
|
||||
f"Tables de multiplication et division de {min_table} à {max_table}"
|
||||
)
|
||||
pdf_filename = f"exercices_mathematiques_tables_{min_table}_a_{max_table}_{num_exercises}_exercices_{timestamp}.pdf"
|
||||
|
||||
pdf = MathExercisesPDF()
|
||||
pdf.add_page()
|
||||
pdf.set_font('Helvetica', '', 12)
|
||||
pdf.set_font("Helvetica", "", 12)
|
||||
|
||||
pdf.cell(0, 10, table_info, 0, 1, 'C', new_x="LMARGIN", new_y="NEXT")
|
||||
pdf.cell(0, 10, table_info, 0, 1, "C", new_x="LMARGIN", new_y="NEXT")
|
||||
pdf.ln(5)
|
||||
|
||||
# Générer les exercices
|
||||
@@ -255,9 +285,9 @@ def create_math_exercises_pdf(min_table: int, max_table: int, num_exercises: int
|
||||
col3_text = ""
|
||||
|
||||
# Ajouter la ligne
|
||||
pdf.cell(60, 10, col1_text, 0, 0, 'L', new_x="RIGHT", new_y="TOP")
|
||||
pdf.cell(60, 10, col2_text, 0, 0, 'L', new_x="RIGHT", new_y="TOP")
|
||||
pdf.cell(60, 10, col3_text, 0, 1, 'L', new_x="LMARGIN", new_y="NEXT")
|
||||
pdf.cell(60, 10, col1_text, 0, 0, "L", new_x="RIGHT", new_y="TOP")
|
||||
pdf.cell(60, 10, col2_text, 0, 0, "L", new_x="RIGHT", new_y="TOP")
|
||||
pdf.cell(60, 10, col3_text, 0, 1, "L", new_x="LMARGIN", new_y="NEXT")
|
||||
|
||||
# Sauvegarder le PDF en mémoire
|
||||
pdf_data = pdf.output()
|
||||
@@ -270,11 +300,11 @@ def create_math_exercises_pdf(min_table: int, max_table: int, num_exercises: int
|
||||
|
||||
return pdf_filename
|
||||
|
||||
|
||||
@app.get("/", response_class=HTMLResponse)
|
||||
async def read_root(request: Request):
|
||||
return templates.TemplateResponse("index.html", {"request": request})
|
||||
|
||||
from fastapi.responses import RedirectResponse
|
||||
|
||||
@app.post("/generate")
|
||||
async def generate_exercises_endpoint(request: ExerciseRequest):
|
||||
@@ -282,14 +312,14 @@ async def generate_exercises_endpoint(request: ExerciseRequest):
|
||||
if request.min_table < 1 or request.max_table < 1:
|
||||
return {"error": "Les tables doivent être supérieures à 0"}
|
||||
if request.min_table > request.max_table:
|
||||
return {"error": "La table minimale doit être inférieure ou égale à la table maximale"}
|
||||
return {
|
||||
"error": "La table minimale doit être inférieure ou égale à la table maximale"
|
||||
}
|
||||
if request.num_exercises < 1:
|
||||
return {"error": "Le nombre d'exercices doit être supérieur à 0"}
|
||||
|
||||
pdf_filename = create_math_exercises_pdf(
|
||||
request.min_table,
|
||||
request.max_table,
|
||||
request.num_exercises
|
||||
request.min_table, request.max_table, request.num_exercises
|
||||
)
|
||||
|
||||
# Return redirect to automatically download the file
|
||||
@@ -298,6 +328,7 @@ async def generate_exercises_endpoint(request: ExerciseRequest):
|
||||
except Exception as e:
|
||||
return {"error": f"Erreur lors de la création du PDF: {str(e)}"}
|
||||
|
||||
|
||||
@app.get("/download/{filename}")
|
||||
async def download_pdf(filename: str):
|
||||
# Download file from S3
|
||||
@@ -309,10 +340,11 @@ async def download_pdf(filename: str):
|
||||
# Return streaming response with PDF data
|
||||
return StreamingResponse(
|
||||
io.BytesIO(file_data),
|
||||
media_type='application/pdf',
|
||||
headers={'Content-Disposition': f'attachment; filename="{filename}"'}
|
||||
media_type="application/pdf",
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
||||
)
|
||||
|
||||
|
||||
@app.get("/list")
|
||||
async def list_pdfs(page: int = 1, page_size: int = 10):
|
||||
"""List PDF files in the S3 bucket with pagination (sorted from newest to oldest)"""
|
||||
@@ -342,33 +374,37 @@ async def list_pdfs(page: int = 1, page_size: int = 10):
|
||||
"total_files": total_files,
|
||||
"total_pages": total_pages,
|
||||
"has_next": page < total_pages,
|
||||
"has_prev": page > 1
|
||||
}
|
||||
"has_prev": page > 1,
|
||||
},
|
||||
}
|
||||
except Exception as e:
|
||||
return {"error": f"Error listing files: {str(e)}"}
|
||||
|
||||
|
||||
@app.delete("/delete/{filename}")
|
||||
async def delete_pdf(filename: str):
|
||||
"""Delete a PDF file from S3 bucket"""
|
||||
try:
|
||||
# Decode URL encoded filename
|
||||
import urllib.parse
|
||||
|
||||
decoded_filename = urllib.parse.unquote(filename)
|
||||
|
||||
success = delete_from_s3(S3_BUCKET_NAME, decoded_filename)
|
||||
if success:
|
||||
return {"message": f"Fichier {decoded_filename} supprimé avec succès"}
|
||||
else:
|
||||
return {"error": f"Erreur lors de la suppression du fichier {decoded_filename}"}
|
||||
return {
|
||||
"error": f"Erreur lors de la suppression du fichier {decoded_filename}"
|
||||
}
|
||||
except Exception as e:
|
||||
return {"error": f"Error deleting file: {str(e)}"}
|
||||
|
||||
|
||||
@app.post("/bulk-delete")
|
||||
async def bulk_delete(filenames: str = Form(...)):
|
||||
"""Delete multiple PDF files from S3 bucket"""
|
||||
try:
|
||||
import json
|
||||
filename_list = json.loads(filenames)
|
||||
deleted_files = []
|
||||
errors = []
|
||||
@@ -381,51 +417,54 @@ async def bulk_delete(filenames: str = Form(...)):
|
||||
errors.append(filename)
|
||||
|
||||
if errors:
|
||||
return {"message": f"Fichiers supprimés: {len(deleted_files)}", "errors": errors}
|
||||
return {
|
||||
"message": f"Fichiers supprimés: {len(deleted_files)}",
|
||||
"errors": errors,
|
||||
}
|
||||
else:
|
||||
return {"message": f"{len(deleted_files)} fichiers supprimés avec succès"}
|
||||
except Exception as e:
|
||||
return {"error": f"Error deleting files: {str(e)}"}
|
||||
|
||||
from fastapi import Form
|
||||
|
||||
@app.post("/bulk-download")
|
||||
async def bulk_download(filenames: str = Form(...)):
|
||||
"""Download multiple PDF files as a zip archive"""
|
||||
try:
|
||||
import zipfile
|
||||
import tempfile
|
||||
import json
|
||||
|
||||
# Parse the JSON string to get the list of filenames
|
||||
filename_list = json.loads(filenames)
|
||||
|
||||
# Create a temporary zip file
|
||||
with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmp_zip:
|
||||
with zipfile.ZipFile(tmp_zip.name, 'w') as zipf:
|
||||
with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp_zip:
|
||||
with zipfile.ZipFile(tmp_zip.name, "w") as zipf:
|
||||
for filename in filename_list:
|
||||
file_data = download_from_s3(S3_BUCKET_NAME, filename)
|
||||
if file_data:
|
||||
zipf.writestr(filename, file_data)
|
||||
|
||||
# Read the zip file
|
||||
with open(tmp_zip.name, 'rb') as f:
|
||||
with open(tmp_zip.name, "rb") as f:
|
||||
zip_data = f.read()
|
||||
|
||||
# Clean up temporary file
|
||||
import os
|
||||
|
||||
os.unlink(tmp_zip.name)
|
||||
|
||||
# Return streaming response with zip data
|
||||
zip_filename = f"math_exercises_{len(filename_list)}_files.zip"
|
||||
return StreamingResponse(
|
||||
io.BytesIO(zip_data),
|
||||
media_type='application/zip',
|
||||
headers={'Content-Disposition': f'attachment; filename="{zip_filename}"'}
|
||||
media_type="application/zip",
|
||||
headers={
|
||||
"Content-Disposition": f'attachment; filename="{zip_filename}"'
|
||||
},
|
||||
)
|
||||
except Exception as e:
|
||||
return {"error": f"Error downloading files: {str(e)}"}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||
+57
-24
@@ -5,16 +5,29 @@ import subprocess
|
||||
import os
|
||||
from fpdf import FPDF
|
||||
|
||||
|
||||
class MathExercisesPDF(FPDF):
|
||||
def header(self):
|
||||
self.set_font('Helvetica', 'B', 16)
|
||||
self.cell(0, 10, 'Exercices de Multiplication et Division', 0, 1, 'C', new_x="LMARGIN", new_y="NEXT")
|
||||
self.set_font("Helvetica", "B", 16)
|
||||
self.cell(
|
||||
0,
|
||||
10,
|
||||
"Exercices de Multiplication et Division",
|
||||
0,
|
||||
1,
|
||||
"C",
|
||||
new_x="LMARGIN",
|
||||
new_y="NEXT",
|
||||
)
|
||||
self.ln(10)
|
||||
|
||||
def footer(self):
|
||||
self.set_y(-15)
|
||||
self.set_font('Helvetica', 'I', 8)
|
||||
self.cell(0, 10, f'Page {self.page_no()}', 0, 0, 'C', new_x="RIGHT", new_y="TOP")
|
||||
self.set_font("Helvetica", "I", 8)
|
||||
self.cell(
|
||||
0, 10, f"Page {self.page_no()}", 0, 0, "C", new_x="RIGHT", new_y="TOP"
|
||||
)
|
||||
|
||||
|
||||
def generate_exercises(min_table, max_table, num_exercises=15):
|
||||
"""Génère des exercices de multiplication et division aléatoires mélangés sans doublons"""
|
||||
@@ -34,9 +47,9 @@ def generate_exercises(min_table, max_table, num_exercises=15):
|
||||
result = a * b
|
||||
|
||||
# Choisir aléatoirement le type d'exercice (seulement multiplication ou division)
|
||||
exercise_type = random.choice(['multiplication', 'division'])
|
||||
exercise_type = random.choice(["multiplication", "division"])
|
||||
|
||||
if exercise_type == 'multiplication':
|
||||
if exercise_type == "multiplication":
|
||||
# Exercice de multiplication
|
||||
operation_key = f"mult_{a}_{b}" # Clé unique pour cette opération
|
||||
if operation_key not in used_operations:
|
||||
@@ -63,9 +76,9 @@ def generate_exercises(min_table, max_table, num_exercises=15):
|
||||
result = a * b
|
||||
|
||||
# Choisir aléatoirement le type d'exercice
|
||||
exercise_type = random.choice(['multiplication', 'division'])
|
||||
exercise_type = random.choice(["multiplication", "division"])
|
||||
|
||||
if exercise_type == 'multiplication':
|
||||
if exercise_type == "multiplication":
|
||||
exercise = f"{a} · {b} = ____"
|
||||
else: # division
|
||||
divisor = random.choice([a, b])
|
||||
@@ -75,19 +88,22 @@ def generate_exercises(min_table, max_table, num_exercises=15):
|
||||
|
||||
return exercises
|
||||
|
||||
|
||||
def create_math_exercises_pdf(min_table, max_table, num_exercises=15):
|
||||
"""Crée un fichier PDF avec des exercices de mathématiques mélangés en 3 colonnes"""
|
||||
pdf = MathExercisesPDF()
|
||||
pdf.add_page()
|
||||
pdf.set_font('Helvetica', '', 12)
|
||||
pdf.set_font("Helvetica", "", 12)
|
||||
|
||||
# Ajouter des informations sur la plage de tables
|
||||
if min_table == max_table:
|
||||
table_info = f'Tables de multiplication et division pour {min_table}'
|
||||
table_info = f"Tables de multiplication et division pour {min_table}"
|
||||
else:
|
||||
table_info = f'Tables de multiplication et division de {min_table} à {max_table}'
|
||||
table_info = (
|
||||
f"Tables de multiplication et division de {min_table} à {max_table}"
|
||||
)
|
||||
|
||||
pdf.cell(0, 10, table_info, 0, 1, 'C', new_x="LMARGIN", new_y="NEXT")
|
||||
pdf.cell(0, 10, table_info, 0, 1, "C", new_x="LMARGIN", new_y="NEXT")
|
||||
pdf.ln(5)
|
||||
|
||||
# Générer les exercices
|
||||
@@ -127,18 +143,21 @@ def create_math_exercises_pdf(min_table, max_table, num_exercises=15):
|
||||
col3_text = ""
|
||||
|
||||
# Ajouter la ligne
|
||||
pdf.cell(60, 10, col1_text, 0, 0, 'L', new_x="RIGHT", new_y="TOP")
|
||||
pdf.cell(60, 10, col2_text, 0, 0, 'L', new_x="RIGHT", new_y="TOP")
|
||||
pdf.cell(60, 10, col3_text, 0, 1, 'L', new_x="LMARGIN", new_y="NEXT")
|
||||
pdf.cell(60, 10, col1_text, 0, 0, "L", new_x="RIGHT", new_y="TOP")
|
||||
pdf.cell(60, 10, col2_text, 0, 0, "L", new_x="RIGHT", new_y="TOP")
|
||||
pdf.cell(60, 10, col3_text, 0, 1, "L", new_x="LMARGIN", new_y="NEXT")
|
||||
|
||||
# Sauvegarder le PDF
|
||||
if min_table == max_table:
|
||||
filename = f'exercices_mathematiques_table_{min_table}_{num_exercises}_exercices.pdf'
|
||||
filename = (
|
||||
f"exercices_mathematiques_table_{min_table}_{num_exercises}_exercices.pdf"
|
||||
)
|
||||
else:
|
||||
filename = f'exercices_mathematiques_tables_{min_table}_a_{max_table}_{num_exercises}_exercices.pdf'
|
||||
filename = f"exercices_mathematiques_tables_{min_table}_a_{max_table}_{num_exercises}_exercices.pdf"
|
||||
pdf.output(filename)
|
||||
return filename
|
||||
|
||||
|
||||
def open_pdf(filename):
|
||||
"""Ouvre le fichier PDF avec la commande système appropriée"""
|
||||
try:
|
||||
@@ -152,14 +171,21 @@ def open_pdf(filename):
|
||||
except (subprocess.CalledProcessError, FileNotFoundError, OSError):
|
||||
return False
|
||||
|
||||
|
||||
def main():
|
||||
"""Fonction principale"""
|
||||
# Vérifier les arguments de ligne de commande
|
||||
if len(sys.argv) < 3:
|
||||
print("Usage: python generate_math_exercises.py <min_table> <max_table> [num_exercises] [--open]")
|
||||
print(
|
||||
"Usage: python generate_math_exercises.py <min_table> <max_table> [num_exercises] [--open]"
|
||||
)
|
||||
print("Exemple: python generate_math_exercises.py 4 7 15")
|
||||
print(" python generate_math_exercises.py 4 7 15 --open (pour ouvrir automatiquement le PDF)")
|
||||
print(" python generate_math_exercises.py 5 5 10 (pour seulement la table de 5)")
|
||||
print(
|
||||
" python generate_math_exercises.py 4 7 15 --open (pour ouvrir automatiquement le PDF)"
|
||||
)
|
||||
print(
|
||||
" python generate_math_exercises.py 5 5 10 (pour seulement la table de 5)"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# Vérifier si l'option --open est présente
|
||||
@@ -173,7 +199,9 @@ def main():
|
||||
if min_table < 1 or max_table < 1:
|
||||
raise ValueError("Les tables doivent être supérieures à 0")
|
||||
if min_table > max_table:
|
||||
raise ValueError("La table minimale doit être inférieure ou égale à la table maximale")
|
||||
raise ValueError(
|
||||
"La table minimale doit être inférieure ou égale à la table maximale"
|
||||
)
|
||||
except ValueError as e:
|
||||
print(f"Erreur: {e}")
|
||||
sys.exit(1)
|
||||
@@ -197,7 +225,9 @@ def main():
|
||||
print(f"- Exercices pour la table de {min_table}")
|
||||
else:
|
||||
print(f"- Exercices pour les tables de {min_table} à {max_table}")
|
||||
print(f"- {num_exercises} exercices MÉLANGÉS générés aléatoirement et numérotés de 1 à {num_exercises}")
|
||||
print(
|
||||
f"- {num_exercises} exercices MÉLANGÉS générés aléatoirement et numérotés de 1 à {num_exercises}"
|
||||
)
|
||||
print("- Types d'exercices : multiplications (·) et divisions (:) uniquement")
|
||||
print("- Présentés en 3 colonnes pour économiser l'espace")
|
||||
print("- Aucune opération en double (quand c'est possible)")
|
||||
@@ -205,13 +235,16 @@ def main():
|
||||
# Ouvrir le PDF si demandé
|
||||
if open_after_creation:
|
||||
if open_pdf(filename):
|
||||
print(f"\nPDF ouvert automatiquement avec succès !")
|
||||
print("\nPDF ouvert automatiquement avec succès !")
|
||||
else:
|
||||
print(f"\nImpossible d'ouvrir automatiquement le PDF. Vous pouvez l'ouvrir manuellement.")
|
||||
print(
|
||||
"\nImpossible d'ouvrir automatiquement le PDF. Vous pouvez l'ouvrir manuellement."
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Erreur lors de la création du PDF: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user