-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #109 from SocialGouv/feat/obiz-scripts
feat: obiz scripts & new fields for offer
- Loading branch information
Showing
16 changed files
with
4,663 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
# Byte-compiled / optimized / DLL files | ||
__pycache__/ | ||
*.py[cod] | ||
|
||
# C extensions | ||
*.so | ||
|
||
# Distribution / packaging | ||
bin/ | ||
build/ | ||
develop-eggs/ | ||
dist/ | ||
eggs/ | ||
lib/ | ||
lib64/ | ||
parts/ | ||
sdist/ | ||
var/ | ||
*.egg-info/ | ||
.installed.cfg | ||
*.egg | ||
|
||
# Installer logs | ||
pip-log.txt | ||
pip-delete-this-directory.txt | ||
|
||
# Unit test / coverage reports | ||
.tox/ | ||
.coverage | ||
.cache | ||
nosetests.xml | ||
coverage.xml | ||
|
||
# Translations | ||
*.mo | ||
|
||
# Mr Developer | ||
.mr.developer.cfg | ||
.project | ||
.pydevproject | ||
|
||
# Rope | ||
.ropeproject | ||
|
||
# Django stuff: | ||
*.log | ||
*.pot | ||
|
||
# Sphinx documentation | ||
docs/_build/ | ||
|
||
# idea | ||
.idea/ | ||
|
||
# Specific | ||
inputs/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
from typing import Dict, Optional | ||
from enum import Enum | ||
|
||
class Category(Enum): | ||
""" | ||
Énumération des catégories selon la structure fournie | ||
""" | ||
TELEPHONY = "Internet et abonnements" | ||
BANK = "Banque et assurance" | ||
CULTURE = "Livres, presse et culture" | ||
EQUIPMENT = "High Tech et équipements" | ||
HYGIENE = "Hygiène et beauté" | ||
SPORT = "Sport" | ||
HOBBY = "Loisirs et sorties" | ||
MOBILITY = "Transport et voyage" | ||
FASHION = "Mode et vêtements" | ||
SHOP = "Courses et restauration" | ||
|
||
class GenreMapper: | ||
""" | ||
Classe pour gérer les correspondances entre genres et catégories | ||
""" | ||
def __init__(self, mapping_file: str = None): | ||
self._mapping: Dict[str, Category] = { | ||
# reduccine | ||
"Abonnements": Category.TELEPHONY, | ||
"E-billets": Category.HOBBY, | ||
"Billets papier": Category.HOBBY, | ||
"Achats groupés par lot": Category.SHOP, | ||
|
||
# reduckdo | ||
"Sport": Category.SPORT, | ||
"Alimentation, Gastronomie": Category.SHOP, | ||
"Brico, Déco, Aménagement": Category.EQUIPMENT, | ||
"Chèques Culture": Category.CULTURE, | ||
"Cours en Ligne, Coaching": Category.CULTURE, | ||
"Escape Game, Autres Loisirs": Category.HOBBY, | ||
"Jeux, Livres, Puériculture": Category.CULTURE, | ||
"Loisirs": Category.HOBBY, | ||
"Mode, Beauté, Parfumerie": Category.FASHION, | ||
"Multi-Enseigne, Gd Distrib": Category.SHOP, | ||
"Produits Artisanaux": Category.SHOP, | ||
"Voyages": Category.MOBILITY, | ||
"VR / Coaching eSport": Category.HOBBY, | ||
"Abonnement Digital, Multimédia": Category.EQUIPMENT, | ||
"Echèques": Category.SHOP, | ||
|
||
# reducparc | ||
"Aquariums": Category.HOBBY, | ||
"E-coffret Expériences": Category.HOBBY, | ||
"Grands Parcs": Category.HOBBY, | ||
"Loisirs & Tourisme": Category.HOBBY, | ||
"Offres Séjours": Category.MOBILITY, | ||
"Parcs animaliers": Category.HOBBY, | ||
"Parcs aquatiques": Category.HOBBY, | ||
"Parcs culturels": Category.HOBBY, | ||
"Parcs d'attractions": Category.HOBBY | ||
} | ||
|
||
def get_category(self, genre: str) -> Optional[str]: | ||
""" | ||
Retourne la catégorie correspondant au genre | ||
""" | ||
category = self._mapping.get(genre) | ||
return category.value if category else None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
sousgenre_ids = [ | ||
'4de46793-f99e-4134-9060-f3f25eaf54cb', | ||
'7c0c156b-a34e-42df-93e7-6d9b580e9d2c', | ||
'993a12af-7bcf-4c23-8d99-54b3c58dd136', | ||
'fca7f854-2304-48d5-bb4b-b674b4f98c69', | ||
'a8ce74c6-5214-4737-b04c-8267069066b6' | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import json | ||
from parsers.articles import get_article_names | ||
from parsers.sousgenres import get_sousgenre_names | ||
from parsers.genres import get_genre_names | ||
from finders.sousgenre import search_sousgenre_articles, print_sousgenre_details | ||
|
||
def main(): | ||
file_paths = [ | ||
'inputs/reduccine.fr-preprod.json', | ||
'inputs/reduckdo.fr-preprod.json', | ||
'inputs/reducparc.fr-preprod.json' | ||
] | ||
|
||
try: | ||
while True: | ||
print("\nQue souhaitez-vous faire ?") | ||
print("1. Afficher tous les noms d'articles") | ||
print("2. Afficher tous les noms de sous-genres") | ||
print("3. Afficher tous les noms de genres") | ||
print("4. Rechercher un sous-genre spécifique") | ||
print("5. Quitter") | ||
|
||
choice = input("\nVotre choix (1-4): ") | ||
|
||
if choice == "1": | ||
all_articles = get_article_names(file_paths) | ||
print("\nListe des noms d'articles par source :") | ||
for source, articles in all_articles.items(): | ||
print(f"\n{'-' * 20} {source} {'-' * 20}") | ||
for name in articles: | ||
print(f"- {name}") | ||
|
||
elif choice == "2": | ||
all_sousgenres = get_sousgenre_names(file_paths) | ||
print("\nListe des noms de sous-genres par source :") | ||
for source, sousgenres in all_sousgenres.items(): | ||
print(f"\n{'-' * 20} {source} {'-' * 20}") | ||
for name in sousgenres: | ||
print(f"- {name}") | ||
|
||
elif choice == "3": | ||
all_genres = get_genre_names(file_paths) | ||
print("\nListe des noms de genres par source :") | ||
for source, genres in all_genres.items(): | ||
print(f"\n{'-' * 20} {source} {'-' * 20}") | ||
for name in genres: | ||
print(f"- {name}") | ||
|
||
elif choice == "4": | ||
search_name = input("\nEntrez le nom du sous-genre à rechercher: ") | ||
results = search_sousgenre_articles(file_paths, search_name) | ||
print_sousgenre_details(results) | ||
|
||
elif choice == "5": | ||
print("Au revoir!") | ||
break | ||
|
||
else: | ||
print("Choix invalide. Veuillez réessayer.") | ||
|
||
except FileNotFoundError as e: | ||
print(f"Le fichier n'a pas été trouvé: {str(e)}") | ||
except json.JSONDecodeError as e: | ||
print(f"Erreur lors de la lecture du fichier JSON: {str(e)}") | ||
except Exception as e: | ||
print(f"Une erreur est survenue : {str(e)}") | ||
|
||
if __name__ == "__main__": | ||
main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
import json | ||
from typing import Dict, List | ||
from colorama import init, Fore, Style | ||
|
||
def format_reduction(reduction: float, variable_remise: str, is_variable: str) -> str: | ||
""" | ||
Formate la réduction en fonction des conditions | ||
""" | ||
if is_variable == 'True': | ||
return variable_remise | ||
return f"{reduction:.1f}" | ||
|
||
|
||
def format_price(price_str: str) -> str: | ||
""" | ||
Formate un prix en string avec 2 décimales et le symbole € | ||
""" | ||
try: | ||
# Remplace la virgule par un point pour la conversion | ||
price_float = float(price_str.replace(',', '.')) | ||
# Formate le prix avec 2 décimales et le symbole € | ||
return f"{price_float:.2f} €" | ||
except (ValueError, AttributeError): | ||
return "Prix non disponible" | ||
|
||
|
||
def calculate_reduction(prix_public: str, prix_reduc: str) -> float: | ||
""" | ||
Calcule le pourcentage de réduction entre deux prix | ||
""" | ||
try: | ||
prix_public_float = float(prix_public.replace(',', '.')) | ||
prix_reduc_float = float(prix_reduc.replace(',', '.')) | ||
|
||
if prix_public_float == 0: | ||
return 0.0 | ||
|
||
reduction = ((prix_public_float - prix_reduc_float) / prix_public_float) * 100 | ||
return reduction | ||
except (ValueError, AttributeError): | ||
return 0.0 | ||
|
||
|
||
def search_sousgenre_articles(file_paths: List[str], sousgenre_name: str) -> Dict[str, List[Dict]]: | ||
""" | ||
Recherche un sous-genre par son nom dans plusieurs fichiers et retourne tous ses articles associés | ||
""" | ||
all_results = {} | ||
|
||
for file_path in file_paths: | ||
source_name = file_path.split('/')[-1].split('.')[0] | ||
|
||
with open(file_path, 'r', encoding='utf-8') as file: | ||
data = json.load(file) | ||
|
||
results = [] | ||
|
||
for catalogue in data.get('catalogues', []): | ||
for cat in catalogue.get('catalogue', []): | ||
for genre in cat.get('genres', []): | ||
for gen in genre.get('genre', []): | ||
for sousgenre in gen.get('sousgenres', []): | ||
for sg in sousgenre.get('sousgenre', []): | ||
if sg.get('sousgenres_nom', '').lower() == sousgenre_name.lower(): | ||
sousgenre_info = { | ||
'nom': sg.get('sousgenres_nom', ''), | ||
'id': sg.get('sousgenres_id'), | ||
'genre_nom': gen.get('genres_nom', ''), | ||
'url': sg.get('sousgenres_url', ''), | ||
'description': sg.get('sousgenres_descriptif', {}).get('#cdata-section', ''), | ||
'articles': [] | ||
} | ||
|
||
for articles in sg.get('articles', []): | ||
for article in articles.get('article', []): | ||
if article['articles_actif'] == "True": | ||
article_info = { | ||
'nom': article.get('articles_nom', ''), | ||
'prix_public': article.get('articles_prix_public', ''), | ||
'prix_reduc_ttc': article.get('articles_puttc', ''), | ||
'variable': article.get('articles_valeur_variable', ''), | ||
'variable_remise': article.get('articles_remise_btob', ''), | ||
'code': article.get('articles_code', ''), | ||
'type': article.get('articles_type', ''), | ||
'description': article.get('articles_descriptif', {}).get( | ||
'#cdata-section', '') | ||
} | ||
sousgenre_info['articles'].append(article_info) | ||
|
||
results.append(sousgenre_info) | ||
|
||
if results: | ||
all_results[source_name] = results | ||
|
||
return all_results | ||
|
||
|
||
def print_sousgenre_details(results: Dict[str, List[Dict]]) -> None: | ||
""" | ||
Affiche les détails d'un sous-genre et ses articles de manière formatée pour chaque source | ||
""" | ||
if not results: | ||
print("Aucun sous-genre trouvé avec ce nom dans aucune source.") | ||
return | ||
|
||
for source, source_results in results.items(): | ||
print(f"\n{'=' * 20} Source: {source} {'=' * 20}") | ||
|
||
for result in source_results: | ||
print(f"\nSous-genre: {result['nom']} (genre - {result['genre_nom']})") | ||
print(f"ID : {result['id']}") | ||
print(f"URL: {result['url']}") | ||
print("\nArticles associés:") | ||
print("-" * 30) | ||
|
||
if not result['articles']: | ||
print("Aucun article trouvé pour ce sous-genre.") | ||
|
||
for article in result['articles']: | ||
prix_public = article['prix_public'] | ||
prix_reduc = article['prix_reduc_ttc'] | ||
reduction = calculate_reduction(prix_public, prix_reduc) | ||
|
||
formatted_reduction = format_reduction( | ||
reduction, | ||
article['variable_remise'], | ||
article['variable'] | ||
) | ||
|
||
print(f"\nNom: {Style.BRIGHT}{Fore.BLUE}{article['nom']}{Style.RESET_ALL}") | ||
print(f"Réduction: {Style.BRIGHT}{Fore.GREEN}{formatted_reduction}%{Style.RESET_ALL}") | ||
print(f"Valeur variable : {'Oui' if article['variable'] == 'True' else 'Non'}") | ||
print(f"Code: {article['code']}") | ||
print(f"Type: {article['type']}") | ||
print(f"Prix public: {format_price(prix_public)}") | ||
print(f"Prix avec réduc ttc : {format_price(prix_reduc)}") |
Oops, something went wrong.