- Added HistoriquePage component to display user's voting history with detailed statistics and vote cards. - Created UpcomingVotesPage component to show upcoming elections with a similar layout. - Developed CSS styles for both pages to enhance visual appeal and responsiveness. - Integrated API calls to fetch user's votes and upcoming elections. - Added a rebuild script for Docker environment setup and data restoration. - Created a Python script to populate the database with sample data for testing.
413 lines
15 KiB
Python
413 lines
15 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Script pour repeupler la base de données avec :
|
|
- 1700 utilisateurs
|
|
- 10 élections passées (historique)
|
|
- 5 élections actives
|
|
- 15 élections futures
|
|
- Votes distribués aléatoirement (pas tous les users dans chaque election)
|
|
"""
|
|
|
|
import subprocess
|
|
import random
|
|
from datetime import datetime, timedelta
|
|
import bcrypt
|
|
import tempfile
|
|
import os
|
|
|
|
# Configuration
|
|
DB_USER = "evoting_user"
|
|
DB_PASSWORD = "evoting_pass123"
|
|
DB_NAME = "evoting_db"
|
|
HASHED_PASSWORD = bcrypt.hashpw(b"epita1234", b"$2b$12$zxCiC3MJpa32FfpX8u7Lx.").decode('utf-8')
|
|
|
|
def run_sql(sql):
|
|
"""Exécuter du SQL via docker exec"""
|
|
cmd = f'docker exec -i evoting_db mariadb -u {DB_USER} -p{DB_PASSWORD} {DB_NAME}'
|
|
result = subprocess.run(cmd, shell=True, input=sql, capture_output=True, text=True)
|
|
return result
|
|
|
|
def log(msg, emoji="📝"):
|
|
"""Afficher un message formaté"""
|
|
print(f"{emoji} {msg}")
|
|
|
|
# ============================================================================
|
|
# PHASE 0: NETTOYER LA BASE DE DONNÉES
|
|
# ============================================================================
|
|
log("PHASE 0: Nettoyage de la base de données...", "🧹")
|
|
|
|
# Utiliser mysql client directement pour avoir plus de control
|
|
cleanup_sql = """
|
|
SET FOREIGN_KEY_CHECKS=0;
|
|
DROP TABLE IF EXISTS votes;
|
|
DROP TABLE IF EXISTS candidates;
|
|
DROP TABLE IF EXISTS elections;
|
|
DROP TABLE IF EXISTS voters;
|
|
SET FOREIGN_KEY_CHECKS=1;
|
|
"""
|
|
|
|
# Écrire dans un fichier temporaire et exécuter
|
|
import tempfile
|
|
import os
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.sql', delete=False) as f:
|
|
f.write(cleanup_sql)
|
|
temp_file = f.name
|
|
|
|
try:
|
|
cmd = f'docker exec -i evoting_db mariadb -u {DB_USER} -p{DB_PASSWORD} {DB_NAME} < {temp_file}'
|
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
|
if result.returncode == 0:
|
|
log("✓ Anciennes tables supprimées", "✅")
|
|
else:
|
|
log(f"⚠️ Nettoyage échoué: {result.stderr}", "⚠️")
|
|
finally:
|
|
os.unlink(temp_file)
|
|
|
|
# Recréer les tables
|
|
create_tables_sql = """
|
|
CREATE TABLE voters (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
email VARCHAR(255) UNIQUE NOT NULL,
|
|
password_hash VARCHAR(255) NOT NULL,
|
|
first_name VARCHAR(100),
|
|
last_name VARCHAR(100),
|
|
citizen_id VARCHAR(50) UNIQUE,
|
|
public_key LONGBLOB,
|
|
has_voted BOOLEAN DEFAULT FALSE,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
certificate_hash VARCHAR(255),
|
|
INDEX idx_email (email)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
CREATE TABLE elections (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
start_date DATETIME NOT NULL,
|
|
end_date DATETIME NOT NULL,
|
|
elgamal_p INT,
|
|
elgamal_g INT,
|
|
public_key LONGBLOB,
|
|
is_active BOOLEAN DEFAULT FALSE,
|
|
results_published BOOLEAN DEFAULT FALSE,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
INDEX idx_active (is_active)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
CREATE TABLE candidates (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
election_id INT NOT NULL,
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
`order` INT,
|
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (election_id) REFERENCES elections(id) ON DELETE CASCADE,
|
|
INDEX idx_election (election_id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
|
|
CREATE TABLE votes (
|
|
id INT PRIMARY KEY AUTO_INCREMENT,
|
|
voter_id INT NOT NULL,
|
|
election_id INT NOT NULL,
|
|
candidate_id INT NOT NULL,
|
|
encrypted_vote LONGBLOB NOT NULL,
|
|
zero_knowledge_proof LONGBLOB,
|
|
ballot_hash VARCHAR(255),
|
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
ip_address VARCHAR(45),
|
|
FOREIGN KEY (voter_id) REFERENCES voters(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (election_id) REFERENCES elections(id) ON DELETE CASCADE,
|
|
FOREIGN KEY (candidate_id) REFERENCES candidates(id) ON DELETE CASCADE,
|
|
INDEX idx_voter (voter_id),
|
|
INDEX idx_election (election_id)
|
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
|
"""
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', suffix='.sql', delete=False) as f:
|
|
f.write(create_tables_sql)
|
|
temp_file = f.name
|
|
|
|
try:
|
|
cmd = f'docker exec -i evoting_db mariadb -u {DB_USER} -p{DB_PASSWORD} {DB_NAME} < {temp_file}'
|
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
|
if result.returncode == 0:
|
|
log("✓ Tables recréées", "✅")
|
|
else:
|
|
log(f"Erreur: {result.stderr}", "❌")
|
|
finally:
|
|
os.unlink(temp_file)
|
|
|
|
# ============================================================================
|
|
log("PHASE 1: Création de 1700 utilisateurs...", "👥")
|
|
|
|
sql_users = "INSERT INTO voters (email, password_hash, first_name, last_name, citizen_id, certificate_hash, public_key, created_at) VALUES\n"
|
|
values = []
|
|
|
|
# 1698 utilisateurs normaux
|
|
for i in range(1, 1699):
|
|
email = f"voter_{i}@voting.local"
|
|
first_name = f"User{i}"
|
|
last_name = f"Voter{i}"
|
|
citizen_id = f"ID_{i:06d}"
|
|
values.append(f"('{email}', '{HASHED_PASSWORD}', '{first_name}', '{last_name}', '{citizen_id}', 'cert_{i}', 'pk_{i}', NOW())")
|
|
|
|
# 2 utilisateurs spéciaux
|
|
special_users = [
|
|
("new_user_e13_157@voting.local", "NewUser157", "Election13_157", "ID_SPEC_157"),
|
|
("new_user_e13_192@voting.local", "NewUser192", "Election13_192", "ID_SPEC_192"),
|
|
]
|
|
|
|
for email, first_name, last_name, citizen_id in special_users:
|
|
values.append(f"('{email}', '{HASHED_PASSWORD}', '{first_name}', '{last_name}', '{citizen_id}', 'cert_special', 'pk_special', NOW())")
|
|
|
|
sql_users += ",\n".join(values) + ";"
|
|
|
|
result = run_sql(sql_users)
|
|
if result.returncode == 0:
|
|
log("✓ 1700 utilisateurs créés", "✅")
|
|
else:
|
|
log(f"Erreur: {result.stderr}", "❌")
|
|
|
|
# ============================================================================
|
|
# PHASE 2: CRÉER LES 30 ÉLECTIONS (10 passées + 5 actives + 15 futures)
|
|
# ============================================================================
|
|
log("PHASE 2: Création des 30 élections...", "🗳️")
|
|
|
|
now = datetime.now()
|
|
elections = []
|
|
|
|
# 10 élections passées (historique)
|
|
for i in range(1, 11):
|
|
start = (now - timedelta(days=365-i*30)).strftime("%Y-%m-%d")
|
|
end = (now - timedelta(days=365-(i*30)-7)).strftime("%Y-%m-%d")
|
|
elections.append({
|
|
"name": f"Historical Election {i}",
|
|
"description": f"Past election {i}",
|
|
"start": start,
|
|
"end": end,
|
|
"is_active": 0
|
|
})
|
|
|
|
# 5 élections actives (en cours)
|
|
for i in range(1, 6):
|
|
start = (now - timedelta(days=7-i)).strftime("%Y-%m-%d")
|
|
end = (now + timedelta(days=30+i)).strftime("%Y-%m-%d")
|
|
elections.append({
|
|
"name": f"Active Election {i}",
|
|
"description": f"Current election {i}",
|
|
"start": start,
|
|
"end": end,
|
|
"is_active": 1
|
|
})
|
|
|
|
# 15 élections futures
|
|
for i in range(1, 16):
|
|
start = (now + timedelta(days=30+i*5)).strftime("%Y-%m-%d")
|
|
end = (now + timedelta(days=37+i*5)).strftime("%Y-%m-%d")
|
|
elections.append({
|
|
"name": f"Upcoming Election {i}",
|
|
"description": f"Future election {i}",
|
|
"start": start,
|
|
"end": end,
|
|
"is_active": 0
|
|
})
|
|
|
|
sql_elections = "INSERT INTO elections (name, description, start_date, end_date, elgamal_p, elgamal_g, public_key, is_active, results_published) VALUES\n"
|
|
election_values = []
|
|
|
|
for idx, election in enumerate(elections, 1):
|
|
election_values.append(
|
|
f"('{election['name']}', '{election['description']}', '{election['start']}', '{election['end']}', '23', '5', 'pk_{idx}', {election['is_active']}, 0)"
|
|
)
|
|
|
|
sql_elections += ",\n".join(election_values) + ";"
|
|
|
|
result = run_sql(sql_elections)
|
|
if result.returncode == 0:
|
|
log(f"✓ 30 élections créées (10 passées + 5 actives + 15 futures)", "✅")
|
|
else:
|
|
log(f"Erreur: {result.stderr}", "❌")
|
|
|
|
# ============================================================================
|
|
# PHASE 3: CRÉER LES CANDIDATS
|
|
# ============================================================================
|
|
log("PHASE 3: Création des candidats...", "🎭")
|
|
|
|
candidate_names = [
|
|
"Alice Johnson", "Bob Smith", "Carol White", "David Brown",
|
|
"Emily Davis", "Frank Miller", "Grace Wilson", "Henry Moore"
|
|
]
|
|
|
|
sql_candidates = "INSERT INTO candidates (election_id, name, description, `order`, created_at) VALUES\n"
|
|
candidate_values = []
|
|
|
|
for election_id in range(1, 31):
|
|
# 4-8 candidats par élection
|
|
num_candidates = random.randint(4, 8)
|
|
selected_candidates = random.sample(candidate_names, min(num_candidates, len(candidate_names)))
|
|
|
|
for order, name in enumerate(selected_candidates, 1):
|
|
candidate_values.append(
|
|
f"({election_id}, '{name}', 'Candidate {name}', {order}, NOW())"
|
|
)
|
|
|
|
sql_candidates += ",\n".join(candidate_values) + ";"
|
|
|
|
result = run_sql(sql_candidates)
|
|
if result.returncode == 0:
|
|
log(f"✓ Candidats créés pour toutes les élections", "✅")
|
|
else:
|
|
log(f"Erreur: {result.stderr}", "❌")
|
|
|
|
# ============================================================================
|
|
# PHASE 4: CRÉER LES VOTES
|
|
# ============================================================================
|
|
log("PHASE 4: Création des votes...", "🗳️")
|
|
|
|
sql_votes = "INSERT INTO votes (voter_id, election_id, candidate_id, encrypted_vote, ballot_hash, timestamp) VALUES\n"
|
|
vote_values = []
|
|
|
|
# Pour chaque élection
|
|
for election_id in range(1, 31):
|
|
# Récupérer les candidats de cette élection
|
|
result_candidates = run_sql(f"SELECT id FROM candidates WHERE election_id = {election_id};")
|
|
candidate_ids = []
|
|
if result_candidates.returncode == 0:
|
|
lines = result_candidates.stdout.strip().split('\n')[1:]
|
|
for line in lines:
|
|
if line.strip():
|
|
candidate_ids.append(int(line.split()[0]))
|
|
|
|
if not candidate_ids:
|
|
continue
|
|
|
|
# Déterminer le nombre de votants pour cette élection
|
|
# Entre 20% et 80% des utilisateurs votent pour cette élection
|
|
num_voters = random.randint(int(1700 * 0.2), int(1700 * 0.8))
|
|
|
|
# Sélectionner les votants aléatoirement
|
|
voter_ids = random.sample(range(1, 1701), num_voters)
|
|
|
|
for voter_id in voter_ids:
|
|
candidate_id = random.choice(candidate_ids)
|
|
ballot_hash = f"hash_{voter_id}_{election_id}"
|
|
vote_values.append(
|
|
f"({voter_id}, {election_id}, {candidate_id}, 'encrypted_{voter_id}_{election_id}', '{ballot_hash}', NOW())"
|
|
)
|
|
|
|
# Insérer tous les votes en batch
|
|
batch_size = 1000
|
|
for i in range(0, len(vote_values), batch_size):
|
|
batch = vote_values[i:i+batch_size]
|
|
sql_batch = "INSERT INTO votes (voter_id, election_id, candidate_id, encrypted_vote, ballot_hash, timestamp) VALUES\n"
|
|
sql_batch += ",\n".join(batch) + ";"
|
|
|
|
result = run_sql(sql_batch)
|
|
if result.returncode != 0:
|
|
log(f"Erreur batch {i//batch_size}: {result.stderr}", "❌")
|
|
|
|
log(f"✓ {len(vote_values)} votes créés", "✅")
|
|
|
|
# ============================================================================
|
|
# PHASE 5: VOTES SPÉCIAUX POUR new_user_e13_192
|
|
# ============================================================================
|
|
log("PHASE 5: Configuration des votes pour new_user_e13_192...", "⚙️")
|
|
|
|
# Récupérer l'ID de new_user_e13_192
|
|
result = run_sql("SELECT id FROM voters WHERE email = 'new_user_e13_192@voting.local';")
|
|
special_user_id = None
|
|
if result.returncode == 0 and result.stdout.strip():
|
|
lines = result.stdout.strip().split('\n')
|
|
if len(lines) > 1:
|
|
special_user_id = int(lines[1].split()[0])
|
|
|
|
if special_user_id:
|
|
# Supprimer tous les votes actuels pour cet utilisateur
|
|
run_sql(f"DELETE FROM votes WHERE voter_id = {special_user_id};")
|
|
|
|
special_votes = []
|
|
|
|
# 10 votes pour les élections passées (1-10)
|
|
for election_id in range(1, 11):
|
|
result_cand = run_sql(f"SELECT id FROM candidates WHERE election_id = {election_id} LIMIT 1;")
|
|
if result_cand.returncode == 0 and result_cand.stdout.strip():
|
|
lines = result_cand.stdout.strip().split('\n')
|
|
if len(lines) > 1:
|
|
candidate_id = int(lines[1].split()[0])
|
|
ballot_hash = f"hash_{special_user_id}_{election_id}"
|
|
special_votes.append(
|
|
f"({special_user_id}, {election_id}, {candidate_id}, 'encrypted_{special_user_id}_{election_id}', '{ballot_hash}', NOW())"
|
|
)
|
|
|
|
# 4 votes pour les élections actives (11, 12, 14, 15) - PAS 13
|
|
for election_id in [11, 12, 14, 15]:
|
|
result_cand = run_sql(f"SELECT id FROM candidates WHERE election_id = {election_id} LIMIT 1;")
|
|
if result_cand.returncode == 0 and result_cand.stdout.strip():
|
|
lines = result_cand.stdout.strip().split('\n')
|
|
if len(lines) > 1:
|
|
candidate_id = int(lines[1].split()[0])
|
|
ballot_hash = f"hash_{special_user_id}_{election_id}"
|
|
special_votes.append(
|
|
f"({special_user_id}, {election_id}, {candidate_id}, 'encrypted_{special_user_id}_{election_id}', '{ballot_hash}', NOW())"
|
|
)
|
|
|
|
# 4 votes pour les élections futures (21, 24, 27, 30)
|
|
for election_id in [21, 24, 27, 30]:
|
|
result_cand = run_sql(f"SELECT id FROM candidates WHERE election_id = {election_id} LIMIT 1;")
|
|
if result_cand.returncode == 0 and result_cand.stdout.strip():
|
|
lines = result_cand.stdout.strip().split('\n')
|
|
if len(lines) > 1:
|
|
candidate_id = int(lines[1].split()[0])
|
|
ballot_hash = f"hash_{special_user_id}_{election_id}"
|
|
special_votes.append(
|
|
f"({special_user_id}, {election_id}, {candidate_id}, 'encrypted_{special_user_id}_{election_id}', '{ballot_hash}', NOW())"
|
|
)
|
|
|
|
if special_votes:
|
|
sql_special = "INSERT INTO votes (voter_id, election_id, candidate_id, encrypted_vote, ballot_hash, timestamp) VALUES\n"
|
|
sql_special += ",\n".join(special_votes) + ";"
|
|
|
|
result = run_sql(sql_special)
|
|
if result.returncode == 0:
|
|
log(f"✓ 18 votes spéciaux créés pour new_user_e13_192 (10 + 4 + 4)", "✅")
|
|
else:
|
|
log(f"Erreur: {result.stderr}", "❌")
|
|
|
|
# ============================================================================
|
|
# PHASE 6: STATISTIQUES FINALES
|
|
# ============================================================================
|
|
log("PHASE 6: Vérification des données...", "📊")
|
|
|
|
result = run_sql("""
|
|
SELECT
|
|
(SELECT COUNT(*) FROM elections) as total_elections,
|
|
(SELECT COUNT(*) FROM voters) as total_voters,
|
|
(SELECT COUNT(*) FROM candidates) as total_candidates,
|
|
(SELECT COUNT(*) FROM votes) as total_votes;
|
|
""")
|
|
|
|
if result.returncode == 0:
|
|
print("\n" + "="*60)
|
|
print("✅ BASE DE DONNÉES REPUPLÉE AVEC SUCCÈS!")
|
|
print("="*60)
|
|
print(result.stdout)
|
|
print("="*60)
|
|
|
|
# Afficher les utilisateurs spéciaux
|
|
result = run_sql("SELECT id, email, first_name, last_name FROM voters WHERE email LIKE 'new_user_e13_%' ORDER BY id;")
|
|
|
|
if result.returncode == 0:
|
|
print("\n" + "="*60)
|
|
print("👤 UTILISATEURS SPÉCIAUX:")
|
|
print("="*60)
|
|
print(result.stdout)
|
|
print("\n✅ Vous pouvez maintenant vous connecter avec:")
|
|
print(" Email: new_user_e13_192@voting.local")
|
|
print(" Mot de passe: epita1234")
|
|
print(" Votes: 10 historiques + 4 actifs + 4 futurs")
|
|
print(" Élection sans vote: Active Election 3 (ID 13)")
|
|
print("="*60)
|