126 lines
3.6 KiB
Python
126 lines
3.6 KiB
Python
|
|
"""
|
||
|
|
Utilitaires pour charger et manipuler les données projets depuis le JSON.
|
||
|
|
Modifier uniquement data/projects.json pour mettre à jour le portfolio.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import json
|
||
|
|
import re
|
||
|
|
from pathlib import Path
|
||
|
|
from typing import Optional, List, Dict
|
||
|
|
from django.conf import settings
|
||
|
|
|
||
|
|
|
||
|
|
def load_config() -> Dict:
|
||
|
|
"""
|
||
|
|
Charge la configuration globale du site depuis data/config.json.
|
||
|
|
Contient : profil, navbar, compétences, expériences, contact, footer, SEO.
|
||
|
|
Pour modifier les infos du site, éditez uniquement data/config.json.
|
||
|
|
"""
|
||
|
|
config_path: Path = settings.CONFIG_JSON_PATH
|
||
|
|
|
||
|
|
if not config_path.exists():
|
||
|
|
return {}
|
||
|
|
|
||
|
|
with open(config_path, 'r', encoding='utf-8') as f:
|
||
|
|
return json.load(f)
|
||
|
|
|
||
|
|
|
||
|
|
def load_projects() -> List[Dict]:
|
||
|
|
"""
|
||
|
|
Charge tous les projets depuis le fichier JSON.
|
||
|
|
Retourne une liste de dictionnaires projets.
|
||
|
|
"""
|
||
|
|
json_path: Path = settings.PROJECTS_JSON_PATH
|
||
|
|
|
||
|
|
if not json_path.exists():
|
||
|
|
return []
|
||
|
|
|
||
|
|
with open(json_path, 'r', encoding='utf-8') as f:
|
||
|
|
data = json.load(f)
|
||
|
|
|
||
|
|
projects = data.get('projects', [])
|
||
|
|
|
||
|
|
# Générer un slug unique pour chaque projet (utilisé dans les URLs)
|
||
|
|
for i, project in enumerate(projects):
|
||
|
|
project['slug'] = _slugify(project.get('title', f'projet-{i}'))
|
||
|
|
project['id'] = i # Index pour retrouver le projet facilement
|
||
|
|
|
||
|
|
return projects
|
||
|
|
|
||
|
|
|
||
|
|
def get_project_by_slug(slug: str) -> Optional[Dict]:
|
||
|
|
"""
|
||
|
|
Retourne un projet spécifique par son slug.
|
||
|
|
"""
|
||
|
|
projects = load_projects()
|
||
|
|
for project in projects:
|
||
|
|
if project.get('slug') == slug:
|
||
|
|
return project
|
||
|
|
return None
|
||
|
|
|
||
|
|
|
||
|
|
def get_all_categories(projects: List[Dict]) -> List[str]:
|
||
|
|
"""
|
||
|
|
Retourne la liste unique de toutes les catégories disponibles.
|
||
|
|
"""
|
||
|
|
categories = set()
|
||
|
|
for project in projects:
|
||
|
|
cat = project.get('category', '').strip()
|
||
|
|
if cat:
|
||
|
|
categories.add(cat)
|
||
|
|
return sorted(list(categories))
|
||
|
|
|
||
|
|
|
||
|
|
def get_all_technologies(projects: List[Dict]) -> List[str]:
|
||
|
|
"""
|
||
|
|
Retourne la liste unique de toutes les technologies utilisées,
|
||
|
|
triées par fréquence décroissante (les plus utilisées en premier).
|
||
|
|
"""
|
||
|
|
freq: Dict[str, int] = {}
|
||
|
|
for project in projects:
|
||
|
|
for tech in project.get('technologies', []):
|
||
|
|
t = tech.strip()
|
||
|
|
freq[t] = freq.get(t, 0) + 1
|
||
|
|
return sorted(freq.keys(), key=lambda t: (-freq[t], t))
|
||
|
|
|
||
|
|
|
||
|
|
def filter_projects(projects: List[Dict], category: str = '', tech: str = '') -> List[Dict]:
|
||
|
|
"""
|
||
|
|
Filtre les projets par catégorie et/ou technologie.
|
||
|
|
"""
|
||
|
|
filtered = projects
|
||
|
|
|
||
|
|
if category:
|
||
|
|
filtered = [p for p in filtered if p.get('category', '').lower() == category.lower()]
|
||
|
|
|
||
|
|
if tech:
|
||
|
|
filtered = [
|
||
|
|
p for p in filtered
|
||
|
|
if any(t.lower() == tech.lower() for t in p.get('technologies', []))
|
||
|
|
]
|
||
|
|
|
||
|
|
return filtered
|
||
|
|
|
||
|
|
|
||
|
|
def _slugify(text: str) -> str:
|
||
|
|
"""
|
||
|
|
Convertit un titre en slug URL-safe.
|
||
|
|
Ex: "Mon Super Projet" → "mon-super-projet"
|
||
|
|
"""
|
||
|
|
text = text.lower().strip()
|
||
|
|
# Remplacer les caractères accentués
|
||
|
|
replacements = {
|
||
|
|
'à': 'a', 'â': 'a', 'ä': 'a',
|
||
|
|
'é': 'e', 'è': 'e', 'ê': 'e', 'ë': 'e',
|
||
|
|
'î': 'i', 'ï': 'i',
|
||
|
|
'ô': 'o', 'ö': 'o',
|
||
|
|
'ù': 'u', 'û': 'u', 'ü': 'u',
|
||
|
|
'ç': 'c', 'ñ': 'n',
|
||
|
|
}
|
||
|
|
for char, replacement in replacements.items():
|
||
|
|
text = text.replace(char, replacement)
|
||
|
|
# Remplacer tout caractère non alphanumérique par un tiret
|
||
|
|
text = re.sub(r'[^a-z0-9]+', '-', text)
|
||
|
|
text = text.strip('-')
|
||
|
|
return text
|