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:
+150
-111
@@ -1,4 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
import random
|
import random
|
||||||
import os
|
import os
|
||||||
import io
|
import io
|
||||||
@@ -7,8 +8,8 @@ import zipfile
|
|||||||
import tempfile
|
import tempfile
|
||||||
from botocore.exceptions import ClientError
|
from botocore.exceptions import ClientError
|
||||||
from typing import List
|
from typing import List
|
||||||
from fastapi import FastAPI, Request, Response, Form
|
from fastapi import FastAPI, Request, Form
|
||||||
from fastapi.responses import HTMLResponse, StreamingResponse
|
from fastapi.responses import HTMLResponse, RedirectResponse, StreamingResponse
|
||||||
from fastapi.templating import Jinja2Templates
|
from fastapi.templating import Jinja2Templates
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from fpdf import FPDF
|
from fpdf import FPDF
|
||||||
@@ -16,37 +17,38 @@ from fpdf import FPDF
|
|||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
# S3 Configuration
|
# 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")
|
templates = Jinja2Templates(directory="app/templates")
|
||||||
|
|
||||||
|
|
||||||
def get_s3_client():
|
def get_s3_client():
|
||||||
"""Create and return an S3 client using environment variables"""
|
"""Create and return an S3 client using environment variables"""
|
||||||
s3_access_key = os.environ.get('S3_ACCESS_KEY')
|
s3_access_key = os.environ.get("S3_ACCESS_KEY")
|
||||||
s3_secret_key = os.environ.get('S3_SECRET_KEY')
|
s3_secret_key = os.environ.get("S3_SECRET_KEY")
|
||||||
s3_host_base = os.environ.get('S3_HOST_BASE')
|
s3_host_base = os.environ.get("S3_HOST_BASE")
|
||||||
s3_host_bucket = os.environ.get('S3_HOST_BUCKET')
|
|
||||||
|
|
||||||
if not all([s3_access_key, s3_secret_key, s3_host_base]):
|
if not all([s3_access_key, s3_secret_key, s3_host_base]):
|
||||||
raise ValueError("S3 environment variables not properly set")
|
raise ValueError("S3 environment variables not properly set")
|
||||||
|
|
||||||
s3 = boto3.client(
|
s3 = boto3.client(
|
||||||
's3',
|
"s3",
|
||||||
aws_access_key_id=s3_access_key,
|
aws_access_key_id=s3_access_key,
|
||||||
aws_secret_access_key=s3_secret_key,
|
aws_secret_access_key=s3_secret_key,
|
||||||
endpoint_url=s3_host_base,
|
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
|
return s3
|
||||||
|
|
||||||
|
|
||||||
def create_bucket_if_not_exists(bucket_name):
|
def create_bucket_if_not_exists(bucket_name):
|
||||||
"""Create S3 bucket if it doesn't exist"""
|
"""Create S3 bucket if it doesn't exist"""
|
||||||
s3_client = get_s3_client()
|
s3_client = get_s3_client()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
s3_client.head_bucket(Bucket=bucket_name)
|
s3_client.head_bucket(Bucket=bucket_name)
|
||||||
except ClientError as e:
|
except ClientError as e:
|
||||||
error_code = int(e.response['Error']['Code'])
|
error_code = int(e.response["Error"]["Code"])
|
||||||
if error_code == 404:
|
if error_code == 404:
|
||||||
# Bucket doesn't exist, create it
|
# Bucket doesn't exist, create it
|
||||||
try:
|
try:
|
||||||
@@ -60,49 +62,55 @@ def create_bucket_if_not_exists(bucket_name):
|
|||||||
print(f"Error checking bucket: {e}")
|
print(f"Error checking bucket: {e}")
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
# Create bucket on startup
|
# Create bucket on startup
|
||||||
try:
|
try:
|
||||||
create_bucket_if_not_exists(S3_BUCKET_NAME)
|
create_bucket_if_not_exists(S3_BUCKET_NAME)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Warning: Could not create/check S3 bucket: {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"""
|
"""Upload file data to S3 bucket"""
|
||||||
s3_client = get_s3_client()
|
s3_client = get_s3_client()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
s3_client.put_object(
|
s3_client.put_object(
|
||||||
Bucket=bucket_name,
|
Bucket=bucket_name,
|
||||||
Key=object_name,
|
Key=object_name,
|
||||||
Body=file_data,
|
Body=file_data,
|
||||||
ContentType=content_type
|
ContentType=content_type,
|
||||||
)
|
)
|
||||||
return True
|
return True
|
||||||
except ClientError as e:
|
except ClientError as e:
|
||||||
print(f"Error uploading to S3: {e}")
|
print(f"Error uploading to S3: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def download_from_s3(bucket_name, object_name):
|
def download_from_s3(bucket_name, object_name):
|
||||||
"""Download file data from S3 bucket"""
|
"""Download file data from S3 bucket"""
|
||||||
s3_client = get_s3_client()
|
s3_client = get_s3_client()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = s3_client.get_object(Bucket=bucket_name, Key=object_name)
|
response = s3_client.get_object(Bucket=bucket_name, Key=object_name)
|
||||||
return response['Body'].read()
|
return response["Body"].read()
|
||||||
except ClientError as e:
|
except ClientError as e:
|
||||||
print(f"Error downloading from S3: {e}")
|
print(f"Error downloading from S3: {e}")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def list_objects_in_s3(bucket_name):
|
def list_objects_in_s3(bucket_name):
|
||||||
"""List all objects in S3 bucket (sorted from newest to oldest)"""
|
"""List all objects in S3 bucket (sorted from newest to oldest)"""
|
||||||
s3_client = get_s3_client()
|
s3_client = get_s3_client()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = s3_client.list_objects_v2(Bucket=bucket_name)
|
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)
|
# 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 = [
|
||||||
pdf_files.sort(key=lambda x: x['LastModified'], reverse=True)
|
obj for obj in response["Contents"] if obj["Key"].endswith(".pdf")
|
||||||
|
]
|
||||||
|
pdf_files.sort(key=lambda x: x["LastModified"], reverse=True)
|
||||||
return pdf_files
|
return pdf_files
|
||||||
else:
|
else:
|
||||||
return []
|
return []
|
||||||
@@ -110,10 +118,11 @@ def list_objects_in_s3(bucket_name):
|
|||||||
print(f"Error listing objects in S3: {e}")
|
print(f"Error listing objects in S3: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def delete_from_s3(bucket_name, object_name):
|
def delete_from_s3(bucket_name, object_name):
|
||||||
"""Delete file from S3 bucket"""
|
"""Delete file from S3 bucket"""
|
||||||
s3_client = get_s3_client()
|
s3_client = get_s3_client()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
s3_client.delete_object(Bucket=bucket_name, Key=object_name)
|
s3_client.delete_object(Bucket=bucket_name, Key=object_name)
|
||||||
return True
|
return True
|
||||||
@@ -121,43 +130,59 @@ def delete_from_s3(bucket_name, object_name):
|
|||||||
print(f"Error deleting from S3: {e}")
|
print(f"Error deleting from S3: {e}")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class MathExercisesPDF(FPDF):
|
class MathExercisesPDF(FPDF):
|
||||||
def header(self):
|
def header(self):
|
||||||
self.set_font('Helvetica', 'B', 16)
|
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.cell(
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
"Exercices de Multiplication et Division",
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
"C",
|
||||||
|
new_x="LMARGIN",
|
||||||
|
new_y="NEXT",
|
||||||
|
)
|
||||||
self.ln(10)
|
self.ln(10)
|
||||||
|
|
||||||
def footer(self):
|
def footer(self):
|
||||||
self.set_y(-15)
|
self.set_y(-15)
|
||||||
self.set_font('Helvetica', 'I', 8)
|
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.cell(
|
||||||
|
0, 10, f"Page {self.page_no()}", 0, 0, "C", new_x="RIGHT", new_y="TOP"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ExerciseRequest(BaseModel):
|
class ExerciseRequest(BaseModel):
|
||||||
min_table: int
|
min_table: int
|
||||||
max_table: int
|
max_table: int
|
||||||
num_exercises: int = 15
|
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"""
|
"""Génère des exercices de multiplication et division aléatoires mélangés sans doublons"""
|
||||||
exercises = []
|
exercises: List[str] = []
|
||||||
used_operations = set() # Pour éviter les doublons
|
used_operations: set = set() # Pour éviter les doublons
|
||||||
|
|
||||||
# Générer le nombre exact d'exercices demandé
|
# Générer le nombre exact d'exercices demandé
|
||||||
attempts = 0
|
attempts = 0
|
||||||
max_attempts = num_exercises * 10 # Limite pour éviter une boucle infinie
|
max_attempts = num_exercises * 10 # Limite pour éviter une boucle infinie
|
||||||
|
|
||||||
while len(exercises) < num_exercises and attempts < max_attempts:
|
while len(exercises) < num_exercises and attempts < max_attempts:
|
||||||
attempts += 1
|
attempts += 1
|
||||||
|
|
||||||
# Choisir deux nombres aléatoires entre min_table et max_table
|
# Choisir deux nombres aléatoires entre min_table et max_table
|
||||||
a = random.randint(min_table, max_table)
|
a = random.randint(min_table, max_table)
|
||||||
b = random.randint(min_table, max_table)
|
b = random.randint(min_table, max_table)
|
||||||
result = a * b
|
result = a * b
|
||||||
|
|
||||||
# Choisir aléatoirement le type d'exercice (seulement multiplication ou division)
|
# 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
|
# Exercice de multiplication
|
||||||
operation_key = f"mult_{a}_{b}" # Clé unique pour cette opération
|
operation_key = f"mult_{a}_{b}" # Clé unique pour cette opération
|
||||||
if operation_key not in used_operations:
|
if operation_key not in used_operations:
|
||||||
@@ -172,7 +197,7 @@ def generate_exercises(min_table: int, max_table: int, num_exercises: int = 15)
|
|||||||
exercise = f"{result} : {divisor} = ____"
|
exercise = f"{result} : {divisor} = ____"
|
||||||
exercises.append(exercise)
|
exercises.append(exercise)
|
||||||
used_operations.add(operation_key)
|
used_operations.add(operation_key)
|
||||||
|
|
||||||
# Si nous n'avons pas pu générer suffisamment d'exercices uniques,
|
# Si nous n'avons pas pu générer suffisamment d'exercices uniques,
|
||||||
# compléter avec des variations
|
# compléter avec des variations
|
||||||
if len(exercises) < num_exercises:
|
if len(exercises) < num_exercises:
|
||||||
@@ -182,56 +207,61 @@ def generate_exercises(min_table: int, max_table: int, num_exercises: int = 15)
|
|||||||
a = random.randint(min_table, max_table)
|
a = random.randint(min_table, max_table)
|
||||||
b = random.randint(min_table, max_table)
|
b = random.randint(min_table, max_table)
|
||||||
result = a * b
|
result = a * b
|
||||||
|
|
||||||
# Choisir aléatoirement le type d'exercice
|
# 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} = ____"
|
exercise = f"{a} · {b} = ____"
|
||||||
else: # division
|
else: # division
|
||||||
divisor = random.choice([a, b])
|
divisor = random.choice([a, b])
|
||||||
exercise = f"{result} : {divisor} = ____"
|
exercise = f"{result} : {divisor} = ____"
|
||||||
|
|
||||||
exercises.append(exercise)
|
exercises.append(exercise)
|
||||||
|
|
||||||
return exercises
|
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"""
|
"""Crée un fichier PDF avec des exercices de mathématiques mélangés en 3 colonnes et l'upload sur S3"""
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
# Add timestamp to filename
|
# Add timestamp to filename
|
||||||
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
|
||||||
# Ajouter des informations sur la plage de tables
|
# Ajouter des informations sur la plage de tables
|
||||||
if min_table == max_table:
|
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}"
|
||||||
pdf_filename = f'exercices_mathematiques_table_{min_table}_{num_exercises}_exercices_{timestamp}.pdf'
|
pdf_filename = f"exercices_mathematiques_table_{min_table}_{num_exercises}_exercices_{timestamp}.pdf"
|
||||||
else:
|
else:
|
||||||
table_info = f'Tables de multiplication et division de {min_table} à {max_table}'
|
table_info = (
|
||||||
pdf_filename = f'exercices_mathematiques_tables_{min_table}_a_{max_table}_{num_exercises}_exercices_{timestamp}.pdf'
|
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 = MathExercisesPDF()
|
||||||
pdf.add_page()
|
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)
|
pdf.ln(5)
|
||||||
|
|
||||||
# Générer les exercices
|
# Générer les exercices
|
||||||
exercises = generate_exercises(min_table, max_table, num_exercises)
|
exercises = generate_exercises(min_table, max_table, num_exercises)
|
||||||
|
|
||||||
# Pas d'en-têtes de colonnes
|
# Pas d'en-têtes de colonnes
|
||||||
|
|
||||||
# Répartir les exercices en 3 colonnes
|
# Répartir les exercices en 3 colonnes
|
||||||
num_per_column = (num_exercises + 2) // 3 # Arrondi vers le haut
|
num_per_column = (num_exercises + 2) // 3 # Arrondi vers le haut
|
||||||
col1_exercises = exercises[:num_per_column]
|
col1_exercises = exercises[:num_per_column]
|
||||||
col2_exercises = exercises[num_per_column:num_per_column*2]
|
col2_exercises = exercises[num_per_column : num_per_column * 2]
|
||||||
col3_exercises = exercises[num_per_column*2:]
|
col3_exercises = exercises[num_per_column * 2 :]
|
||||||
|
|
||||||
# Ajouter les exercices numérotés de 1 à n dans les colonnes
|
# Ajouter les exercices numérotés de 1 à n dans les colonnes
|
||||||
max_rows = max(len(col1_exercises), len(col2_exercises), len(col3_exercises))
|
max_rows = max(len(col1_exercises), len(col2_exercises), len(col3_exercises))
|
||||||
|
|
||||||
for i in range(max_rows):
|
for i in range(max_rows):
|
||||||
# Colonne 1
|
# Colonne 1
|
||||||
if i < len(col1_exercises):
|
if i < len(col1_exercises):
|
||||||
@@ -239,42 +269,42 @@ def create_math_exercises_pdf(min_table: int, max_table: int, num_exercises: int
|
|||||||
col1_text = f"{exercise_num}. {col1_exercises[i]}"
|
col1_text = f"{exercise_num}. {col1_exercises[i]}"
|
||||||
else:
|
else:
|
||||||
col1_text = ""
|
col1_text = ""
|
||||||
|
|
||||||
# Colonne 2
|
# Colonne 2
|
||||||
if i < len(col2_exercises):
|
if i < len(col2_exercises):
|
||||||
exercise_num = i + 1 + len(col1_exercises)
|
exercise_num = i + 1 + len(col1_exercises)
|
||||||
col2_text = f"{exercise_num}. {col2_exercises[i]}"
|
col2_text = f"{exercise_num}. {col2_exercises[i]}"
|
||||||
else:
|
else:
|
||||||
col2_text = ""
|
col2_text = ""
|
||||||
|
|
||||||
# Colonne 3
|
# Colonne 3
|
||||||
if i < len(col3_exercises):
|
if i < len(col3_exercises):
|
||||||
exercise_num = i + 1 + len(col1_exercises) + len(col2_exercises)
|
exercise_num = i + 1 + len(col1_exercises) + len(col2_exercises)
|
||||||
col3_text = f"{exercise_num}. {col3_exercises[i]}"
|
col3_text = f"{exercise_num}. {col3_exercises[i]}"
|
||||||
else:
|
else:
|
||||||
col3_text = ""
|
col3_text = ""
|
||||||
|
|
||||||
# Ajouter la ligne
|
# Ajouter la ligne
|
||||||
pdf.cell(60, 10, col1_text, 0, 0, 'L', new_x="RIGHT", new_y="TOP")
|
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, 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, col3_text, 0, 1, "L", new_x="LMARGIN", new_y="NEXT")
|
||||||
|
|
||||||
# Sauvegarder le PDF en mémoire
|
# Sauvegarder le PDF en mémoire
|
||||||
pdf_data = pdf.output()
|
pdf_data = pdf.output()
|
||||||
|
|
||||||
# Upload to S3
|
# Upload to S3
|
||||||
upload_success = upload_to_s3(pdf_data, S3_BUCKET_NAME, pdf_filename)
|
upload_success = upload_to_s3(pdf_data, S3_BUCKET_NAME, pdf_filename)
|
||||||
|
|
||||||
if not upload_success:
|
if not upload_success:
|
||||||
raise Exception("Failed to upload PDF to S3")
|
raise Exception("Failed to upload PDF to S3")
|
||||||
|
|
||||||
return pdf_filename
|
return pdf_filename
|
||||||
|
|
||||||
|
|
||||||
@app.get("/", response_class=HTMLResponse)
|
@app.get("/", response_class=HTMLResponse)
|
||||||
async def read_root(request: Request):
|
async def read_root(request: Request):
|
||||||
return templates.TemplateResponse("index.html", {"request": request})
|
return templates.TemplateResponse("index.html", {"request": request})
|
||||||
|
|
||||||
from fastapi.responses import RedirectResponse
|
|
||||||
|
|
||||||
@app.post("/generate")
|
@app.post("/generate")
|
||||||
async def generate_exercises_endpoint(request: ExerciseRequest):
|
async def generate_exercises_endpoint(request: ExerciseRequest):
|
||||||
@@ -282,37 +312,39 @@ async def generate_exercises_endpoint(request: ExerciseRequest):
|
|||||||
if request.min_table < 1 or request.max_table < 1:
|
if request.min_table < 1 or request.max_table < 1:
|
||||||
return {"error": "Les tables doivent être supérieures à 0"}
|
return {"error": "Les tables doivent être supérieures à 0"}
|
||||||
if request.min_table > request.max_table:
|
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:
|
if request.num_exercises < 1:
|
||||||
return {"error": "Le nombre d'exercices doit être supérieur à 0"}
|
return {"error": "Le nombre d'exercices doit être supérieur à 0"}
|
||||||
|
|
||||||
pdf_filename = create_math_exercises_pdf(
|
pdf_filename = create_math_exercises_pdf(
|
||||||
request.min_table,
|
request.min_table, request.max_table, request.num_exercises
|
||||||
request.max_table,
|
|
||||||
request.num_exercises
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Return redirect to automatically download the file
|
# Return redirect to automatically download the file
|
||||||
return RedirectResponse(url=f"/download/{pdf_filename}", status_code=303)
|
return RedirectResponse(url=f"/download/{pdf_filename}", status_code=303)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"error": f"Erreur lors de la création du PDF: {str(e)}"}
|
return {"error": f"Erreur lors de la création du PDF: {str(e)}"}
|
||||||
|
|
||||||
|
|
||||||
@app.get("/download/{filename}")
|
@app.get("/download/{filename}")
|
||||||
async def download_pdf(filename: str):
|
async def download_pdf(filename: str):
|
||||||
# Download file from S3
|
# Download file from S3
|
||||||
file_data = download_from_s3(S3_BUCKET_NAME, filename)
|
file_data = download_from_s3(S3_BUCKET_NAME, filename)
|
||||||
|
|
||||||
if file_data is None:
|
if file_data is None:
|
||||||
return {"error": "File not found"}
|
return {"error": "File not found"}
|
||||||
|
|
||||||
# Return streaming response with PDF data
|
# Return streaming response with PDF data
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
io.BytesIO(file_data),
|
io.BytesIO(file_data),
|
||||||
media_type='application/pdf',
|
media_type="application/pdf",
|
||||||
headers={'Content-Disposition': f'attachment; filename="{filename}"'}
|
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/list")
|
@app.get("/list")
|
||||||
async def list_pdfs(page: int = 1, page_size: int = 10):
|
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)"""
|
"""List PDF files in the S3 bucket with pagination (sorted from newest to oldest)"""
|
||||||
@@ -322,18 +354,18 @@ async def list_pdfs(page: int = 1, page_size: int = 10):
|
|||||||
page = 1
|
page = 1
|
||||||
if page_size < 1 or page_size > 100:
|
if page_size < 1 or page_size > 100:
|
||||||
page_size = 10
|
page_size = 10
|
||||||
|
|
||||||
pdf_files = list_objects_in_s3(S3_BUCKET_NAME)
|
pdf_files = list_objects_in_s3(S3_BUCKET_NAME)
|
||||||
|
|
||||||
# Calculate pagination
|
# Calculate pagination
|
||||||
total_files = len(pdf_files)
|
total_files = len(pdf_files)
|
||||||
total_pages = (total_files + page_size - 1) // page_size # Ceiling division
|
total_pages = (total_files + page_size - 1) // page_size # Ceiling division
|
||||||
|
|
||||||
# Slice the files for the current page
|
# Slice the files for the current page
|
||||||
start_index = (page - 1) * page_size
|
start_index = (page - 1) * page_size
|
||||||
end_index = start_index + page_size
|
end_index = start_index + page_size
|
||||||
paginated_files = pdf_files[start_index:end_index]
|
paginated_files = pdf_files[start_index:end_index]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"files": paginated_files,
|
"files": paginated_files,
|
||||||
"pagination": {
|
"pagination": {
|
||||||
@@ -342,90 +374,97 @@ async def list_pdfs(page: int = 1, page_size: int = 10):
|
|||||||
"total_files": total_files,
|
"total_files": total_files,
|
||||||
"total_pages": total_pages,
|
"total_pages": total_pages,
|
||||||
"has_next": page < total_pages,
|
"has_next": page < total_pages,
|
||||||
"has_prev": page > 1
|
"has_prev": page > 1,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"error": f"Error listing files: {str(e)}"}
|
return {"error": f"Error listing files: {str(e)}"}
|
||||||
|
|
||||||
|
|
||||||
@app.delete("/delete/{filename}")
|
@app.delete("/delete/{filename}")
|
||||||
async def delete_pdf(filename: str):
|
async def delete_pdf(filename: str):
|
||||||
"""Delete a PDF file from S3 bucket"""
|
"""Delete a PDF file from S3 bucket"""
|
||||||
try:
|
try:
|
||||||
# Decode URL encoded filename
|
# Decode URL encoded filename
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
|
|
||||||
decoded_filename = urllib.parse.unquote(filename)
|
decoded_filename = urllib.parse.unquote(filename)
|
||||||
|
|
||||||
success = delete_from_s3(S3_BUCKET_NAME, decoded_filename)
|
success = delete_from_s3(S3_BUCKET_NAME, decoded_filename)
|
||||||
if success:
|
if success:
|
||||||
return {"message": f"Fichier {decoded_filename} supprimé avec succès"}
|
return {"message": f"Fichier {decoded_filename} supprimé avec succès"}
|
||||||
else:
|
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:
|
except Exception as e:
|
||||||
return {"error": f"Error deleting file: {str(e)}"}
|
return {"error": f"Error deleting file: {str(e)}"}
|
||||||
|
|
||||||
|
|
||||||
@app.post("/bulk-delete")
|
@app.post("/bulk-delete")
|
||||||
async def bulk_delete(filenames: str = Form(...)):
|
async def bulk_delete(filenames: str = Form(...)):
|
||||||
"""Delete multiple PDF files from S3 bucket"""
|
"""Delete multiple PDF files from S3 bucket"""
|
||||||
try:
|
try:
|
||||||
import json
|
|
||||||
filename_list = json.loads(filenames)
|
filename_list = json.loads(filenames)
|
||||||
deleted_files = []
|
deleted_files = []
|
||||||
errors = []
|
errors = []
|
||||||
|
|
||||||
for filename in filename_list:
|
for filename in filename_list:
|
||||||
success = delete_from_s3(S3_BUCKET_NAME, filename)
|
success = delete_from_s3(S3_BUCKET_NAME, filename)
|
||||||
if success:
|
if success:
|
||||||
deleted_files.append(filename)
|
deleted_files.append(filename)
|
||||||
else:
|
else:
|
||||||
errors.append(filename)
|
errors.append(filename)
|
||||||
|
|
||||||
if errors:
|
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:
|
else:
|
||||||
return {"message": f"{len(deleted_files)} fichiers supprimés avec succès"}
|
return {"message": f"{len(deleted_files)} fichiers supprimés avec succès"}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"error": f"Error deleting files: {str(e)}"}
|
return {"error": f"Error deleting files: {str(e)}"}
|
||||||
|
|
||||||
from fastapi import Form
|
|
||||||
|
|
||||||
@app.post("/bulk-download")
|
@app.post("/bulk-download")
|
||||||
async def bulk_download(filenames: str = Form(...)):
|
async def bulk_download(filenames: str = Form(...)):
|
||||||
"""Download multiple PDF files as a zip archive"""
|
"""Download multiple PDF files as a zip archive"""
|
||||||
try:
|
try:
|
||||||
import zipfile
|
|
||||||
import tempfile
|
|
||||||
import json
|
|
||||||
|
|
||||||
# Parse the JSON string to get the list of filenames
|
# Parse the JSON string to get the list of filenames
|
||||||
filename_list = json.loads(filenames)
|
filename_list = json.loads(filenames)
|
||||||
|
|
||||||
# Create a temporary zip file
|
# Create a temporary zip file
|
||||||
with tempfile.NamedTemporaryFile(suffix='.zip', delete=False) as tmp_zip:
|
with tempfile.NamedTemporaryFile(suffix=".zip", delete=False) as tmp_zip:
|
||||||
with zipfile.ZipFile(tmp_zip.name, 'w') as zipf:
|
with zipfile.ZipFile(tmp_zip.name, "w") as zipf:
|
||||||
for filename in filename_list:
|
for filename in filename_list:
|
||||||
file_data = download_from_s3(S3_BUCKET_NAME, filename)
|
file_data = download_from_s3(S3_BUCKET_NAME, filename)
|
||||||
if file_data:
|
if file_data:
|
||||||
zipf.writestr(filename, file_data)
|
zipf.writestr(filename, file_data)
|
||||||
|
|
||||||
# Read the zip file
|
# Read the zip file
|
||||||
with open(tmp_zip.name, 'rb') as f:
|
with open(tmp_zip.name, "rb") as f:
|
||||||
zip_data = f.read()
|
zip_data = f.read()
|
||||||
|
|
||||||
# Clean up temporary file
|
# Clean up temporary file
|
||||||
import os
|
import os
|
||||||
|
|
||||||
os.unlink(tmp_zip.name)
|
os.unlink(tmp_zip.name)
|
||||||
|
|
||||||
# Return streaming response with zip data
|
# Return streaming response with zip data
|
||||||
zip_filename = f"math_exercises_{len(filename_list)}_files.zip"
|
zip_filename = f"math_exercises_{len(filename_list)}_files.zip"
|
||||||
return StreamingResponse(
|
return StreamingResponse(
|
||||||
io.BytesIO(zip_data),
|
io.BytesIO(zip_data),
|
||||||
media_type='application/zip',
|
media_type="application/zip",
|
||||||
headers={'Content-Disposition': f'attachment; filename="{zip_filename}"'}
|
headers={
|
||||||
|
"Content-Disposition": f'attachment; filename="{zip_filename}"'
|
||||||
|
},
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return {"error": f"Error downloading files: {str(e)}"}
|
return {"error": f"Error downloading files: {str(e)}"}
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import uvicorn
|
import uvicorn
|
||||||
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|
||||||
|
|||||||
+87
-54
@@ -5,38 +5,51 @@ import subprocess
|
|||||||
import os
|
import os
|
||||||
from fpdf import FPDF
|
from fpdf import FPDF
|
||||||
|
|
||||||
|
|
||||||
class MathExercisesPDF(FPDF):
|
class MathExercisesPDF(FPDF):
|
||||||
def header(self):
|
def header(self):
|
||||||
self.set_font('Helvetica', 'B', 16)
|
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.cell(
|
||||||
|
0,
|
||||||
|
10,
|
||||||
|
"Exercices de Multiplication et Division",
|
||||||
|
0,
|
||||||
|
1,
|
||||||
|
"C",
|
||||||
|
new_x="LMARGIN",
|
||||||
|
new_y="NEXT",
|
||||||
|
)
|
||||||
self.ln(10)
|
self.ln(10)
|
||||||
|
|
||||||
def footer(self):
|
def footer(self):
|
||||||
self.set_y(-15)
|
self.set_y(-15)
|
||||||
self.set_font('Helvetica', 'I', 8)
|
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.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):
|
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"""
|
"""Génère des exercices de multiplication et division aléatoires mélangés sans doublons"""
|
||||||
exercises = []
|
exercises = []
|
||||||
used_operations = set() # Pour éviter les doublons
|
used_operations = set() # Pour éviter les doublons
|
||||||
|
|
||||||
# Générer le nombre exact d'exercices demandé
|
# Générer le nombre exact d'exercices demandé
|
||||||
attempts = 0
|
attempts = 0
|
||||||
max_attempts = num_exercises * 10 # Limite pour éviter une boucle infinie
|
max_attempts = num_exercises * 10 # Limite pour éviter une boucle infinie
|
||||||
|
|
||||||
while len(exercises) < num_exercises and attempts < max_attempts:
|
while len(exercises) < num_exercises and attempts < max_attempts:
|
||||||
attempts += 1
|
attempts += 1
|
||||||
|
|
||||||
# Choisir deux nombres aléatoires entre min_table et max_table
|
# Choisir deux nombres aléatoires entre min_table et max_table
|
||||||
a = random.randint(min_table, max_table)
|
a = random.randint(min_table, max_table)
|
||||||
b = random.randint(min_table, max_table)
|
b = random.randint(min_table, max_table)
|
||||||
result = a * b
|
result = a * b
|
||||||
|
|
||||||
# Choisir aléatoirement le type d'exercice (seulement multiplication ou division)
|
# 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
|
# Exercice de multiplication
|
||||||
operation_key = f"mult_{a}_{b}" # Clé unique pour cette opération
|
operation_key = f"mult_{a}_{b}" # Clé unique pour cette opération
|
||||||
if operation_key not in used_operations:
|
if operation_key not in used_operations:
|
||||||
@@ -51,7 +64,7 @@ def generate_exercises(min_table, max_table, num_exercises=15):
|
|||||||
exercise = f"{result} : {divisor} = ____"
|
exercise = f"{result} : {divisor} = ____"
|
||||||
exercises.append(exercise)
|
exercises.append(exercise)
|
||||||
used_operations.add(operation_key)
|
used_operations.add(operation_key)
|
||||||
|
|
||||||
# Si nous n'avons pas pu générer suffisamment d'exercices uniques,
|
# Si nous n'avons pas pu générer suffisamment d'exercices uniques,
|
||||||
# compléter avec des variations
|
# compléter avec des variations
|
||||||
if len(exercises) < num_exercises:
|
if len(exercises) < num_exercises:
|
||||||
@@ -61,49 +74,52 @@ def generate_exercises(min_table, max_table, num_exercises=15):
|
|||||||
a = random.randint(min_table, max_table)
|
a = random.randint(min_table, max_table)
|
||||||
b = random.randint(min_table, max_table)
|
b = random.randint(min_table, max_table)
|
||||||
result = a * b
|
result = a * b
|
||||||
|
|
||||||
# Choisir aléatoirement le type d'exercice
|
# 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} = ____"
|
exercise = f"{a} · {b} = ____"
|
||||||
else: # division
|
else: # division
|
||||||
divisor = random.choice([a, b])
|
divisor = random.choice([a, b])
|
||||||
exercise = f"{result} : {divisor} = ____"
|
exercise = f"{result} : {divisor} = ____"
|
||||||
|
|
||||||
exercises.append(exercise)
|
exercises.append(exercise)
|
||||||
|
|
||||||
return exercises
|
return exercises
|
||||||
|
|
||||||
|
|
||||||
def create_math_exercises_pdf(min_table, max_table, num_exercises=15):
|
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"""
|
"""Crée un fichier PDF avec des exercices de mathématiques mélangés en 3 colonnes"""
|
||||||
pdf = MathExercisesPDF()
|
pdf = MathExercisesPDF()
|
||||||
pdf.add_page()
|
pdf.add_page()
|
||||||
pdf.set_font('Helvetica', '', 12)
|
pdf.set_font("Helvetica", "", 12)
|
||||||
|
|
||||||
# Ajouter des informations sur la plage de tables
|
# Ajouter des informations sur la plage de tables
|
||||||
if min_table == max_table:
|
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:
|
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)
|
pdf.ln(5)
|
||||||
|
|
||||||
# Générer les exercices
|
# Générer les exercices
|
||||||
exercises = generate_exercises(min_table, max_table, num_exercises)
|
exercises = generate_exercises(min_table, max_table, num_exercises)
|
||||||
|
|
||||||
# Pas d'en-têtes de colonnes
|
# Pas d'en-têtes de colonnes
|
||||||
|
|
||||||
# Répartir les exercices en 3 colonnes
|
# Répartir les exercices en 3 colonnes
|
||||||
num_per_column = (num_exercises + 2) // 3 # Arrondi vers le haut
|
num_per_column = (num_exercises + 2) // 3 # Arrondi vers le haut
|
||||||
col1_exercises = exercises[:num_per_column]
|
col1_exercises = exercises[:num_per_column]
|
||||||
col2_exercises = exercises[num_per_column:num_per_column*2]
|
col2_exercises = exercises[num_per_column : num_per_column * 2]
|
||||||
col3_exercises = exercises[num_per_column*2:]
|
col3_exercises = exercises[num_per_column * 2 :]
|
||||||
|
|
||||||
# Ajouter les exercices numérotés de 1 à n dans les colonnes
|
# Ajouter les exercices numérotés de 1 à n dans les colonnes
|
||||||
max_rows = max(len(col1_exercises), len(col2_exercises), len(col3_exercises))
|
max_rows = max(len(col1_exercises), len(col2_exercises), len(col3_exercises))
|
||||||
|
|
||||||
for i in range(max_rows):
|
for i in range(max_rows):
|
||||||
# Colonne 1
|
# Colonne 1
|
||||||
if i < len(col1_exercises):
|
if i < len(col1_exercises):
|
||||||
@@ -111,34 +127,37 @@ def create_math_exercises_pdf(min_table, max_table, num_exercises=15):
|
|||||||
col1_text = f"{exercise_num}. {col1_exercises[i]}"
|
col1_text = f"{exercise_num}. {col1_exercises[i]}"
|
||||||
else:
|
else:
|
||||||
col1_text = ""
|
col1_text = ""
|
||||||
|
|
||||||
# Colonne 2
|
# Colonne 2
|
||||||
if i < len(col2_exercises):
|
if i < len(col2_exercises):
|
||||||
exercise_num = i + 1 + len(col1_exercises)
|
exercise_num = i + 1 + len(col1_exercises)
|
||||||
col2_text = f"{exercise_num}. {col2_exercises[i]}"
|
col2_text = f"{exercise_num}. {col2_exercises[i]}"
|
||||||
else:
|
else:
|
||||||
col2_text = ""
|
col2_text = ""
|
||||||
|
|
||||||
# Colonne 3
|
# Colonne 3
|
||||||
if i < len(col3_exercises):
|
if i < len(col3_exercises):
|
||||||
exercise_num = i + 1 + len(col1_exercises) + len(col2_exercises)
|
exercise_num = i + 1 + len(col1_exercises) + len(col2_exercises)
|
||||||
col3_text = f"{exercise_num}. {col3_exercises[i]}"
|
col3_text = f"{exercise_num}. {col3_exercises[i]}"
|
||||||
else:
|
else:
|
||||||
col3_text = ""
|
col3_text = ""
|
||||||
|
|
||||||
# Ajouter la ligne
|
# Ajouter la ligne
|
||||||
pdf.cell(60, 10, col1_text, 0, 0, 'L', new_x="RIGHT", new_y="TOP")
|
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, 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, col3_text, 0, 1, "L", new_x="LMARGIN", new_y="NEXT")
|
||||||
|
|
||||||
# Sauvegarder le PDF
|
# Sauvegarder le PDF
|
||||||
if min_table == max_table:
|
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:
|
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)
|
pdf.output(filename)
|
||||||
return filename
|
return filename
|
||||||
|
|
||||||
|
|
||||||
def open_pdf(filename):
|
def open_pdf(filename):
|
||||||
"""Ouvre le fichier PDF avec la commande système appropriée"""
|
"""Ouvre le fichier PDF avec la commande système appropriée"""
|
||||||
try:
|
try:
|
||||||
@@ -152,32 +171,41 @@ def open_pdf(filename):
|
|||||||
except (subprocess.CalledProcessError, FileNotFoundError, OSError):
|
except (subprocess.CalledProcessError, FileNotFoundError, OSError):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Fonction principale"""
|
"""Fonction principale"""
|
||||||
# Vérifier les arguments de ligne de commande
|
# Vérifier les arguments de ligne de commande
|
||||||
if len(sys.argv) < 3:
|
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("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(
|
||||||
print(" python generate_math_exercises.py 5 5 10 (pour seulement la table de 5)")
|
" 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)
|
sys.exit(1)
|
||||||
|
|
||||||
# Vérifier si l'option --open est présente
|
# Vérifier si l'option --open est présente
|
||||||
open_after_creation = "--open" in sys.argv
|
open_after_creation = "--open" in sys.argv
|
||||||
if open_after_creation:
|
if open_after_creation:
|
||||||
sys.argv.remove("--open") # Retirer l'option de la liste des arguments
|
sys.argv.remove("--open") # Retirer l'option de la liste des arguments
|
||||||
|
|
||||||
try:
|
try:
|
||||||
min_table = int(sys.argv[1])
|
min_table = int(sys.argv[1])
|
||||||
max_table = int(sys.argv[2])
|
max_table = int(sys.argv[2])
|
||||||
if min_table < 1 or max_table < 1:
|
if min_table < 1 or max_table < 1:
|
||||||
raise ValueError("Les tables doivent être supérieures à 0")
|
raise ValueError("Les tables doivent être supérieures à 0")
|
||||||
if min_table > max_table:
|
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:
|
except ValueError as e:
|
||||||
print(f"Erreur: {e}")
|
print(f"Erreur: {e}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Nombre d'exercices (par défaut 15)
|
# Nombre d'exercices (par défaut 15)
|
||||||
num_exercises = 15
|
num_exercises = 15
|
||||||
if len(sys.argv) > 3:
|
if len(sys.argv) > 3:
|
||||||
@@ -188,7 +216,7 @@ def main():
|
|||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
print(f"Erreur: {e}")
|
print(f"Erreur: {e}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Créer le PDF
|
# Créer le PDF
|
||||||
try:
|
try:
|
||||||
filename = create_math_exercises_pdf(min_table, max_table, num_exercises)
|
filename = create_math_exercises_pdf(min_table, max_table, num_exercises)
|
||||||
@@ -197,21 +225,26 @@ def main():
|
|||||||
print(f"- Exercices pour la table de {min_table}")
|
print(f"- Exercices pour la table de {min_table}")
|
||||||
else:
|
else:
|
||||||
print(f"- Exercices pour les tables de {min_table} à {max_table}")
|
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("- Types d'exercices : multiplications (·) et divisions (:) uniquement")
|
||||||
print("- Présentés en 3 colonnes pour économiser l'espace")
|
print("- Présentés en 3 colonnes pour économiser l'espace")
|
||||||
print("- Aucune opération en double (quand c'est possible)")
|
print("- Aucune opération en double (quand c'est possible)")
|
||||||
|
|
||||||
# Ouvrir le PDF si demandé
|
# Ouvrir le PDF si demandé
|
||||||
if open_after_creation:
|
if open_after_creation:
|
||||||
if open_pdf(filename):
|
if open_pdf(filename):
|
||||||
print(f"\nPDF ouvert automatiquement avec succès !")
|
print("\nPDF ouvert automatiquement avec succès !")
|
||||||
else:
|
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:
|
except Exception as e:
|
||||||
print(f"Erreur lors de la création du PDF: {e}")
|
print(f"Erreur lors de la création du PDF: {e}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
Reference in New Issue
Block a user