Architecture technique - PT2QE¶
Vue d'ensemble du système¶
PT2QE (Pricing Tier 2 Quote Engine) est un système de calcul de recommandations de prix personnalisées à la maille client × article. Il s'appuie sur les corridors optimaux générés par PT1CE (Tier 1) et applique une logique décisionnelle à 3 chemins avec cascade de cappings.
Périmètre strict : ZOOM1 uniquement (pas de ZOOM2 ni ZOOM3).
Prérequis obligatoires : - PT1CE doit avoir été exécuté (Option 1 + Option 2) pour créer les tables PT1CE_OPTIMAL_* - Tables de mapping PT0CE_TYPE_CLIENT_MAPPING et PT0CE_TYPE_RESTAURANT_MAPPING doivent exister - Fichier inputs/capping_type_client.csv doit être présent
Structure modulaire complète¶
1. Orchestrateur principal¶
Fichier : pt2qe_main.py
Responsabilités :
- Parse les arguments ligne de commande
- Initialise la configuration depuis config/pt2qe_config.json
- Détermine les périodes d'analyse via PeriodManager
- Orchestre l'exécution des 4 phases
- Gère les modes d'exécution (nouveau calcul, corrections, export seul)
- Crée les dossiers de sortie horodatés
Modes d'exécution :
-
Mode complet (défaut) : Toutes les phases
-
Mode corrections : Saute l'extraction, applique corrections
-
Mode export final : Génère uniquement l'export final
Arguments disponibles :
- database : Base Oracle (obligatoire)
- --capping-file : Fichier CSV cappings (défaut : inputs/capping_type_client.csv)
- --corrections-folder : Dossier avec corrections (défaut : corrections/)
- --output-folder : Dossier de sortie (défaut : outputs/)
- --apply-corrections : Applique les corrections de capping
- --skip-extraction : Réutilise tables existantes
- --generate-final-only : Export final uniquement
- --dry-run : Affiche le plan d'exécution sans l'exécuter
- -q / -qq : Niveaux de log (quiet / very quiet)
- -vv : Mode verbeux (debug)
Structure de sortie :
outputs/
├── run_YYYYMMDD_HHMMSS/ # Nouveau calcul
├── corrections_YYYYMMDD_HHMMSS/ # Avec corrections
└── final_YYYYMMDD_HHMMSS/ # Export final
2. Module d'extraction¶
Fichier : extract_price_offers.py
Classe : PriceOffersExtractor
Responsabilités :
- Extraction offres ZOOM1 depuis SYS_TARIF_SIMULATION
- Enrichissement avec historique transactionnel 4 derniers trimestres
- Récupération du dernier FG_HM connu (mercuriale/hors mercuriale)
- Détermination de l'UNIVERS selon les Business Rules
- Jointure avec mappings PT0CE_TYPE_CLIENT_MAPPING et PT0CE_TYPE_RESTAURANT_MAPPING
- Calcul des métriques de performance : CA, volumes (UF, KG), répartition prix fermes/indexés
Scope temporel : 4 derniers trimestres fiscaux COMPLETS
- Déterminé automatiquement par PeriodManager depuis SYS_MD_CALENDRIER_SYSCO
- Un trimestre est complet si sa dernière semaine est passée
- Période affichée au démarrage du traitement
Méthode principale :
Requête SQL produite : 1. PRP_LABELS : Exclusion des prix promo 2. CURRENT_OFFERS : Offres actuelles avec filtres 3. LAST_FG_HM : Dernier FG_HM par client × article 4. HISTORICAL_PERFORMANCE : Métriques 4Q (CA, volumes, fermes, indexés) 5. LAST_QT_UF : Dernière quantité connue 6. OFFERS_WITH_UNIVERS : Calcul UNIVERS selon Business Rules 7. Jointure finale : Enrichissement TYPE_CLIENT, TYPE_RESTAURANT
Filtres appliqués :
- UNIVERS = 'ZOOM1' (filtre strict)
- cnd.FG_CND_VLD = 'X'
- cnd.DT_FIN > SYSDATE (conditions valides)
- cnd.ID_TYP_CND IN ('ZFAP', 'ZFSP', 'ZIAP', 'ZISP')
- cnd.ID_SEQ IN ('A305', 'A565')
- ID_TC_CG <> 'AUTRES'
- ID_GMM <> 'PSN'
- LC_HIC_SYSCO_N2 NOT IN ('Divers', 'Freezer')
Table créée : PT2QE_PRICE_OFFERS
Compression : COMPRESS NOLOGGING
Index créés :
- IX_PT2QE_PRICE_OFFERS_CLN sur ID_CLN
- IX_PT2QE_PRICE_OFFERS_ART sur ID_ART
- IX_PT2QE_PRICE_OFFERS_MAIN sur (ID_CLN, ID_ART)
- IX_PT2QE_PRICE_OFFERS_TC sur (ID_TC_CG, ID_TC_CIBLE, FG_HM)
Statistiques affichées : - Nombre d'offres extraites - Nombre de clients/articles uniques - Taux d'enrichissement TYPE_CLIENT, TYPE_RESTAURANT, FG_HM
3. Module de calcul des recommandations¶
Fichier : calculate_recommandations.py
Classe : RecommendationCalculator
Composants utilisés :
- CorridorMatcher : Matching offres × corridors PT1CE
- RecommendationCalculator (utils) : Calculs RECO1/RECO2
- CappingManager : Gestion cappings par cube
Méthode principale :
def calculate_all_recommendations(self, offers_table: str, output_table: str, capping_file: Path = None)
Étapes d'exécution :
- Chargement cappings type client
- Lecture du fichier CSV
capping_file -
Stockage des cappings HIGH/MEDIUM/LOW par TYPE_CLIENT
-
Jointure offres × corridors PT1CE
- Appel de
corridor_matcher.enrich_offers_with_corridors() -
Stratégie de matching en cascade :
- Tentative 1 : Match MASTER (4 dimensions : ID_ART, TYPE_CLIENT, TYPE_RESTAURANT, GEO)
- Tentative 2 : Fallback NATIONAL (ID_ART seul)
- Marquage
MATCH_TYPE: 'MASTER', 'NATIONAL', 'NO_MATCH'
-
Génération cappings par cube
- Création table
PT2QE_CAPPING_CUBES - Génération des combinaisons UNIVERS × TYPE_CLIENT × TYPE_RESTAURANT × GEO
- Inclusion des cubes NATIONAL pour fallback
-
Application des cappings depuis le fichier CSV
-
Calcul des recommandations
- Requête SQL unique avec CTEs successives :
ENRICHED_DATA: Récupération données + cappingsRECO_CALC: Calcul RECO1_BASE (paramétrable) et RECO2RECO_WITH_CAPPING_SENSITIVITY: Capping sensibilité sur RECO1RECO_WITH_CAPPING_BASIQUES: Capping basiques 50% en cascadeDECISION_TREE: Arbre de décision à 3 cheminsFINAL_PRICES: Application capping PRB finalFINAL_PRICES_WITH_CAPPING: Détermination capping appliqué
-
Table créée :
PT2QE_RECOMMENDATIONS -
Export cappings générés
- Export de
PT2QE_CAPPING_CUBESen CSV - Fichier :
capping_cubes_generated.csv
Arbre de décision (3 chemins) :
CHEMIN 1 : PAS_BAISSE_GEL_PRIX
- Condition : NEW_PAS < PAS_ACTIF
- Action : PRIX_RECOMMANDE = PRIX_TARIF_ACTUEL
- Rationale : Ne pas répercuter les baisses de coût d'achat
CHEMIN 2 : PL1_CONSERVATION_PREMIUM
- Condition : Prix actuel dans PL1 anciennes bornes (PRB_ACTIF ≥ Prix > BORNE_PL1_PL2)
- Action : PRIX_RECOMMANDE = MAX(PRIX_TARIF_ACTUEL, NEW_BORNE_PL2_PL3) puis capping PRB
- Rationale : Maintenir clients premium en position haute avec plancher sécurité
CHEMIN 3 : OPTIMISATION_STANDARD
- Condition : Tous les autres cas
- Action : PRIX_RECOMMANDE = MAX(RECO1_avec_cappings, RECO2) puis capping PRB
- Cascade de cappings :
1. Capping sensibilité (HIGH/MEDIUM/LOW)
2. Capping basiques (50% max)
3. Capping PRB final
Règles RECO1 (paramétrables via config) :
Définies dans config/pt2qe_config.json :
{
"recommendations": {
"reco1_rules": [
{
"position": "ABOVE_PL1",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL1_PL2",
"action": "NO_CHANGE",
"target": "PRIX_TARIF_ACTUEL",
"comment": "Prix déjà en PL1, pas de changement"
},
{
"position": "PL1_PL2",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL2_PL3",
"action": "TO_PL1",
"target": "NEW_BORNE_PL1_PL2",
"comment": "Remonter vers PL1"
}
// ... autres règles
]
}
}
Comment modifier les règles RECO1 :
1. Éditer config/pt2qe_config.json
2. Modifier le tableau reco1_rules
3. Pour chaque règle, spécifier :
- position : Nom de la position
- condition : Condition SQL (avec préfixe table "ed.")
- action : Description de l'action
- target : Colonne SQL cible
- comment : Explication
4. La méthode build_reco1_sql_case() génère le CASE SQL dynamiquement
RECO2 (fixe) :
Cascade de cappings (CHEMIN 3) :
-
Capping sensibilité (étape 1) :
OùCAPPING_SENSITIVITY= HIGH (2.5%), MEDIUM (5%), LOW (7.5%) -
Capping basiques (étape 2, en cascade) :
-
Capping PRB final (tous chemins) :
Champs calculés :
- DECISION_PATH : Chemin emprunté
- RECO_TYPE : Type reco gagnante (description courte)
- RECO_SELECTIONNEE : Nom explicite de la reco retenue
- CAPPING_APPLIED : Type de capping le plus contraignant
- PRIX_RECOMMANDE : Prix final recommandé
- PCT_HAUSSE_FINALE : % de hausse
- POSITION_TARIF_ACTUEL_DANS_ANCIENNES_BORNES : Position 1
- PALIER_TARIF_ACTUEL_VS_NOUVELLES_BORNES : Position 2
- POSITION_NOUVEAU_PRIX_DANS_NOUVELLES_BORNES : Position 3
Table créée : PT2QE_RECOMMENDATIONS
Compression : COMPRESS NOLOGGING
4. Module d'application des corrections¶
Fichier : apply_capping_rules.py
Classe : CappingApplicator
Responsabilités :
- Lecture du fichier corrections/capping_cubes_corrections.csv
- Application des nouveaux cappings sur les recommandations existantes
- Recalcul complet selon l'arbre de décision avec nouveaux paramètres
Méthode principale :
Processus d'exécution :
- Chargement corrections
- Lecture CSV
capping_cubes_corrections.csv - Validation colonnes : UNIVERS, TYPE_CLIENT, TYPE_RESTAURANT, GEO
-
Validation présence cappings (CAPPING_HIGH, CAPPING_MEDIUM, CAPPING_LOW)
-
Upload corrections
- Création table temporaire
TEMP_CAPPING_CORRECTIONS -
Insertion des corrections
-
Mise à jour recommandations
- Requête
MERGE INTOsurPT2QE_RECOMMENDATIONS - Jointure avec corrections par cube
-
Recalcul de tous les champs :
RECO1_APRES_CAPPING_SENSIBILITERECO1_AVEC_CAPPINGPRIX_RECOMMANDE(selon DECISION_PATH existant)PCT_HAUSSE_FINALEPOSITION_NOUVEAU_PRIX_DANS_NOUVELLES_BORNESCAPPING_APPLIEDRECO_TYPERECO_SELECTIONNEECALCULATION_DATE
-
Nettoyage
- Suppression table temporaire
Comment appliquer des corrections :
- Exécuter d'abord l'Option 1 (calcul initial)
- Analyser
capping_cubes_generated.csvdans le dossier output - Copier
capping_cubes_generated.csvdanscorrections/ - Renommer en
capping_cubes_corrections.csv - Éditer les valeurs
CAPPING_HIGH,CAPPING_MEDIUM,CAPPING_LOW - Exécuter l'Option 2 (ajustement cappings)
- Comparer les résultats avant/après
Format fichier corrections :
UNIVERS;TYPE_CLIENT;TYPE_RESTAURANT;GEO;CAPPING_HIGH;CAPPING_MEDIUM;CAPPING_LOW
ZOOM1;RCI PI GI;TRADITIONNEL;NORD;0,030;0,060;0,090
ZOOM1;RSI HM;RESTAURATION_RAPIDE;SUD;0,020;0,040;0,060
5. Module d'analyse¶
Fichier : analyze_recommendations.py
Classe : RecommendationAnalyzer
Responsabilités : - Génération de 6 exports analytiques - Statistiques par dimension - Distribution des hausses - Analyse des chemins de décision - Impact des cappings
Méthode principale :
6 exports générés :
1. recommendations_detail.csv - Export ligne à ligne de TOUTES les recommandations - Contient cascade complète : RECO1_BASE → RECO1_APRES_CAPPING_SENSIBILITE → RECO1_AVEC_CAPPING - Traçabilité : DECISION_PATH, RECO_SELECTIONNEE, CAPPING_APPLIED - Tri : PCT_HAUSSE_FINALE DESC
2. statistics_by_dimension.csv - Agrégations par TYPE_CLIENT, TYPE_RESTAURANT, UNIVERS - Métriques : nb offres, clients, articles, prix moyens, hausses (min/max/moy/stddev)
3. impact_analysis.csv - Impact CA par TYPE_CLIENT × UNIVERS - Distribution hausses par tranches (0%, 0-2%, 2-5%, etc.) - Impact € et % sur le CA
4. price_increase_distribution.csv - Histogramme hausses par tranches (0-2%, 2-5%, 5-7%, etc.) - % offres et % cumulé par tranche - Statistiques par tranche (min/max/moy)
5. decision_path_analysis.csv - Statistiques par DECISION_PATH × RECO_SELECTIONNEE - Détail cappings appliqués par chemin - Volumétrie et hausses moyennes
6. capping_distribution.csv - Croisement CAPPING_APPLIED × DECISION_PATH × RECO_SELECTIONNEE - Vision détaillée impact de chaque type de capping
Scope des analyses : - ZOOM1 uniquement - Période : 4 derniers trimestres fiscaux complets (affichée dans summary.txt)
6. Module de génération finale¶
Fichier : generate_final_quotes.py
Classe : FinalQuoteGenerator
Responsabilités : - Génération fichier final client × article - Rapport de validation - Résumé textuel
Méthode principale :
Fichiers générés :
1. final_price_offers.csv - Format : Client × Article avec prix recommandés - Colonnes : - Identifiants : ID_CLN, ID_ART, ID_CND - Dimensions : TYPE_CLIENT, TYPE_RESTAURANT, GEO, UNIVERS - Dates condition : DT_DEB_CONDITION, DT_FIN_CONDITION - Prix : PRIX_TARIF_ACTUEL, NOUVEAU_PRIX - Impact : PCT_HAUSSE, HAUSSE_EUROS - 3 positions : POSITION_TARIF_ACTUEL_DANS_ANCIENNES_BORNES, PALIER_TARIF_ACTUEL_VS_NOUVELLES_BORNES, POSITION_NOUVEAU_PRIX_DANS_NOUVELLES_BORNES - Contexte : PRICE_SENSITIVITY, TYPE_HAUSSE - Validation : VALIDATION_STATUS, DATE_CALCUL
2. validation_report.csv - Statistiques de validation - Catégories : GLOBAL, HAUSSES, TYPE_HAUSSE, VALIDATION
3. export_summary.txt - Résumé textuel avec volumétrie et hausses
Règles de validation :
- VALIDE : Normal
- A_VERIFIER : Hausse > 20%
- ANOMALIE : Hausse < 0% (ne devrait pas arriver)
7. Utilitaires¶
Fichier : utils/corridor_matcher.py
Classe : CorridorMatcher
Responsabilités : - Matching offres × corridors PT1CE - Stratégie de fallback MASTER → NATIONAL - Calcul des positions dans corridors
Méthode principale :
Stratégie de matching :
-
Tentative MASTER (4 dimensions)
-
Fallback NATIONAL (ID_ART seul)
-
NO_MATCH
- Offres sans corridor disponible
HAS_CORRIDOR = 0
Statistiques affichées : - Nombre d'offres par MATCH_TYPE - Taux de couverture global
Fichier : utils/capping_manager.py
Classe : CappingManager
Responsabilités : - Chargement cappings depuis CSV - Génération table cappings par cube - Application corrections
Méthodes :
def load_type_client_capping(self, capping_file: Path)
def generate_cube_capping_table(self, enriched_table: str) -> str
def apply_cube_corrections(self, capping_table: str, corrections_file: Path)
Fichier : utils/period_manager.py
Classe : PeriodManager
Responsabilités :
- Détermination périodes fiscales depuis SYS_MD_CALENDRIER_SYSCO
- Calcul 4 derniers trimestres complets
- Validation périodes
Méthodes principales :
def get_last_four_complete_fiscal_quarters(self, reference_date: date) -> Dict
def get_quarter_bounds(self, quarter_code: str) -> Dict
def get_fiscal_year_for_date(self, ref_date: date) -> str
Logique de détermination :
1. Récupère le trimestre en cours depuis FG_SEM_EN_COURS = 'X'
2. Remonte dans le temps pour trouver 4 trimestres COMPLETS
3. Un trimestre est complet si END_DATE < reference_date
4. Retourne start_date, end_date, et liste des trimestres
Fichier : utils/recommendation_calculator.py
Classe : RecommendationCalculator
Responsabilités : - Calcul RECO1 selon règles paramétrables - Calcul RECO2 (hausse proportionnelle) - Application cappings - Validation recommandations
Fichier : utils/impact_analyzer.py
Classe : ImpactAnalyzer
Responsabilités : - Analyse d'impact global - Analyse par dimension - Distribution des hausses - Top hausses
8. Configuration¶
Fichier : config/pt2qe_config.py
Classe : PT2QEConfig
Responsabilités : - Chargement configuration depuis JSON - Valeurs par défaut si fichier absent - Génération SQL dynamique pour RECO1
Méthodes :
def load()
def get_batch_size() -> int
def get_default_cappings() -> Dict[str, float]
def get_capping_basiques() -> float
def get_reco1_rules() -> List[Dict[str, str]]
def build_reco1_sql_case(table_alias: str = "ed") -> str
Comment modifier la configuration :
-
Modifier les cappings par défaut :
-
Modifier les règles RECO1 : Éditer le tableau
reco1_rules(voir section Module de calcul) -
Modifier les paramètres de sortie :
Flux de données détaillé¶
Schéma général¶
SYS_TARIF_SIMULATION ──┐
SYS_FACTURE_LIGNE ─────┼──> PT2QE_PRICE_OFFERS
PT0CE_TYPE_*_MAPPING ──┘
PT2QE_PRICE_OFFERS ────┐
PT1CE_OPTIMAL_* ───────┼──> PT2QE_PRICE_OFFERS_ENRICHED
PT2QE_CAPPING_CUBES ───┘
PT2QE_PRICE_OFFERS_ENRICHED ──> PT2QE_RECOMMENDATIONS
PT2QE_RECOMMENDATIONS ──> 6 fichiers CSV + final_price_offers.csv
Phase 1 : Extraction (ZOOM1 uniquement)¶
Durée scope temporel : 4 derniers trimestres fiscaux complets
Sources :
- SYS_TARIF_SIMULATION : Offres actuelles
- SYS_FACTURE_LIGNE : Historique transactionnel
- PT0CE_TYPE_CLIENT_MAPPING : Enrichissement TYPE_CLIENT
- PT0CE_TYPE_RESTAURANT_MAPPING : Enrichissement TYPE_RESTAURANT
- SYS_MD_CALENDRIER_SYSCO : Détermination périodes fiscales
CTEs dans la requête :
1. PRP_LABELS : Exclusion prix promo
2. ORIGINE_CDE_UNIQUE : Origine commande
3. CURRENT_OFFERS : Offres actuelles ZOOM1
4. LAST_FG_HM : Dernier FG_HM par client × article (période 4Q)
5. HISTORICAL_PERFORMANCE : Métriques 4Q (CA, volumes)
6. LAST_QT_UF : Dernière quantité (période 4Q)
7. OFFERS_WITH_UNIVERS : Calcul UNIVERS selon Business Rules
8. Jointure finale : Enrichissement dimensions
Transformation des données :
- FG_HM : 'X' → '1', sinon '0'
- ID_KAM : NULL ou vide → 'NO_KAM'
- UNIVERS : Calcul selon règles ZOOM1 (voir doc CONCEPTS.md)
Sortie :
- Table : PT2QE_PRICE_OFFERS
- Colonnes : ~50 colonnes (identifiants, dimensions, prix, métriques 4Q)
Phase 2 : Enrichissement corridors¶
Sources :
- PT2QE_PRICE_OFFERS
- PT1CE_OPTIMAL_ZOOM1 (MASTER + NATIONAL)
CTEs dans la requête :
1. CORRIDORS_ZOOM1_ONLY : Filtrage ZOOM1 strict
2. MATCH_MASTER : Tentative match 4 dimensions
3. MATCH_NATIONAL : Fallback ID_ART seul
4. NO_MATCH : Offres sans corridor
Champs ajoutés :
- Anciennes bornes : PAS_ACTIF, PRB_ACTIF, BORNE_PL1_PL2, etc.
- Nouvelles bornes : NEW_PAS, NEW_PRB, NEW_BORNE_PL1_PL2, etc.
- Métadonnées : PRICE_SENSITIVITY, ECART_TYPE, PCT_HAUSSE_PAS
- Matching : HAS_CORRIDOR, MATCH_TYPE, SOURCE_ZOOM, CUBE_TYPE
Sortie :
- Table : PT2QE_PRICE_OFFERS_ENRICHED
Phase 3 : Calcul recommandations¶
Sources :
- PT2QE_PRICE_OFFERS_ENRICHED
- PT2QE_CAPPING_CUBES
- config/pt2qe_config.json (règles RECO1)
CTEs dans la requête :
1. ENRICHED_DATA : Jointure cappings + détection flags
- IS_PAS_BAISSE : 1 si NEW_PAS < PAS_ACTIF
- IS_PL1_ANCIENNES : 1 si prix dans PL1 anciennes bornes
RECO_CALC: Calcul recommandations de baseRECO1_BASE: Selon règles paramétrables-
RECO2: Hausse proportionnelle PAS -
RECO_WITH_CAPPING_SENSITIVITY: Capping sensibilité RECO1_APRES_CAPPING_SENSIBILITE-
CAPPING_SENSIBILITE_APPLIED: Flag -
RECO_WITH_CAPPING_BASIQUES: Capping basiques en cascade RECO1_AVEC_CAPPING-
CAPPING_BASIQUES_APPLIED: Flag -
DECISION_TREE: Arbre de décision DECISION_PATH: Chemin empruntéPRIX_AVANT_CAPPING_PRB: Prix avant capping finalPLANCHER_PL2_APPLIED: Flag (CHEMIN 2)RECO_TYPE: Type reco gagnante-
RECO_SELECTIONNEE: Nom explicite -
FINAL_PRICES: Application capping PRB PRIX_RECOMMANDE: Prix final-
CAPPING_PRB_APPLIED: Flag -
FINAL_PRICES_WITH_CAPPING: Synthèse capping CAPPING_APPLIED: Type le plus contraignant-
PCT_HAUSSE_FINALE: % hausse -
Sélection finale : Tous les champs + 3 positions
Sortie :
- Table : PT2QE_RECOMMENDATIONS
- Colonnes : ~70 colonnes (tout + cascade calculs + 3 positions)
Phase 4 : Analyses et export¶
Source :
- PT2QE_RECOMMENDATIONS
Génération 6 fichiers :
1. recommendations_detail.csv : Export brut
2. statistics_by_dimension.csv : Agrégations
3. impact_analysis.csv : Impact CA
4. price_increase_distribution.csv : Histogramme
5. decision_path_analysis.csv : Stats par chemin
6. capping_distribution.csv : Impact cappings
Génération export final (Option 3) :
- final_price_offers.csv
- validation_report.csv
- export_summary.txt
Gestion des tables Oracle¶
Tables créées automatiquement¶
1. PT2QE_PRICE_OFFERS
- Scope : Offres ZOOM1 actuelles + historique 4Q
- Compression : COMPRESS NOLOGGING
- Index : 4 index automatiques
- Durée de vie : Recréée à chaque run (Option 1)
- Comment supprimer : DROP TABLE PT2QE_PRICE_OFFERS PURGE
2. PT2QE_PRICE_OFFERS_ENRICHED
- Scope : Offres + corridors PT1CE
- Compression : COMPRESS NOLOGGING
- Index : Aucun
- Durée de vie : Recréée à chaque calcul
- Comment supprimer : DROP TABLE PT2QE_PRICE_OFFERS_ENRICHED PURGE
3. PT2QE_CAPPING_CUBES
- Scope : Cappings par cube (MASTER + NATIONAL)
- Compression : Non compressée
- Index : Aucun
- Durée de vie : Recréée à chaque calcul
- Comment supprimer : DROP TABLE PT2QE_CAPPING_CUBES PURGE
4. PT2QE_RECOMMENDATIONS
- Scope : Recommandations finales avec cascade complète
- Compression : COMPRESS NOLOGGING
- Index : Aucun
- Durée de vie : Recréée à chaque calcul, mise à jour par corrections
- Comment supprimer : DROP TABLE PT2QE_RECOMMENDATIONS PURGE
5. TEMP_CAPPING_CORRECTIONS (temporaire) - Scope : Corrections de cappings - Compression : Non compressée - Index : Aucun - Durée de vie : Supprimée après application corrections - Comment supprimer : Automatique
Stratégie DROP/CREATE¶
Tous les modules utilisent :
try:
q = self.db.create_query(f"DROP TABLE {table_name} PURGE")
q.execute()
q.close()
except:
pass
# Puis CREATE TABLE ...
Avantages :
- Pas de problème si table existe déjà
- PURGE libère l'espace immédiatement (pas de corbeille)
- Clean entre les runs
Index créés¶
Sur PT2QE_PRICE_OFFERS :
CREATE INDEX IX_PT2QE_PRICE_OFFERS_CLN ON PT2QE_PRICE_OFFERS (ID_CLN) COMPRESS;
CREATE INDEX IX_PT2QE_PRICE_OFFERS_ART ON PT2QE_PRICE_OFFERS (ID_ART) COMPRESS;
CREATE INDEX IX_PT2QE_PRICE_OFFERS_MAIN ON PT2QE_PRICE_OFFERS (ID_CLN, ID_ART) COMPRESS;
CREATE INDEX IX_PT2QE_PRICE_OFFERS_TC ON PT2QE_PRICE_OFFERS (ID_TC_CG, ID_TC_CIBLE, FG_HM) COMPRESS;
Rationale :
- Accélère les jointures client × article
- Accélère les recherches par TYPE_CLIENT
- COMPRESS réduit l'espace disque
Commandes utiles¶
Vérifier l'existence d'une table :
Compter les lignes :
Voir les index :
SELECT index_name, table_name, column_name
FROM user_ind_columns
WHERE table_name LIKE 'PT2QE%'
ORDER BY table_name, index_name, column_position;
Voir l'espace utilisé :
SELECT
segment_name,
ROUND(bytes/1024/1024, 2) as MB
FROM user_segments
WHERE segment_name LIKE 'PT2QE%'
ORDER BY bytes DESC;
Supprimer toutes les tables PT2QE :
BEGIN
FOR t IN (SELECT table_name FROM user_tables WHERE table_name LIKE 'PT2QE%') LOOP
EXECUTE IMMEDIATE 'DROP TABLE ' || t.table_name || ' PURGE';
END LOOP;
END;
/
Comment forcer la recréation d'une table : 1. Option manuelle :
2. Option automatique : Relancer le calcul (Option 1 ou 2)Stratégies d'optimisation¶
Optimisation des requêtes SQL¶
1. Parallélisation
Directive PARALLEL(8) sur les requêtes lourdes :
Où appliqué :
- Extraction historique 4Q (HISTORICAL_PERFORMANCE)
- Récupération LAST_FG_HM
- Récupération LAST_QT_UF
Comment modifier le degré de parallélisme :
1. Éditer extract_price_offers.py
2. Chercher /*+ PARALLEL(8) */
3. Remplacer 8 par la valeur souhaitée (ex: PARALLEL(4))
4. Attention : Ne pas dépasser les ressources disponibles
2. Compression et NOLOGGING
Tables avec COMPRESS NOLOGGING :
Avantages :
- COMPRESS : Réduit espace disque (~50%)
- NOLOGGING : Accélère création (pas d'écriture redo logs)
Inconvénient :
- NOLOGGING : Table non récupérable en cas de crash (acceptable car recréée)
3. Index compressés
Tous les index avec COMPRESS :
Avantage : - Réduit espace disque index (~30-50%) - Accélère scans d'index
4. Pré-calcul des cappings par cube
Au lieu de joindre à chaque calcul :
-- ❌ Lent : Join répété
SELECT ...
FROM offres o
JOIN ( calcul capping dynamique ) cap
ON ...
-- ✅ Rapide : Pre-calculé
SELECT ...
FROM offres o
JOIN PT2QE_CAPPING_CUBES cap
ON o.UNIVERS = cap.UNIVERS
AND o.TYPE_CLIENT = cap.TYPE_CLIENT
...
Table PT2QE_CAPPING_CUBES : - Générée une fois avant calculs - Contient tous les cappings par cube - Jointure simple et rapide
5. CTEs vs tables temporaires
PT2QE utilise des CTEs (WITH) plutôt que tables temporaires :
Avantages : - Pas de création/suppression de tables - Optimiseur Oracle peut inliner - Code plus lisible
Inconvénient : - CTEs réexécutées si référencées plusieurs fois - (Mais PT2QE référence chaque CTE une seule fois)
Optimisation mémoire¶
1. Pas de chargement complet en mémoire Python
Toutes les opérations en SQL :
- Calculs côté base Oracle
- Pas de .read() massif en Python
- Python orchestre, Oracle calcule
2. Lecture par chunks pour exports
Dans analyze_recommendations.py :
q = self.db.create_query(f"SELECT ... ORDER BY ... FETCH FIRST 10000 ROWS ONLY")
df = q.read()
q.close()
Comment modifier la taille des chunks :
- Pas de paramètre chunk explicite dans PT2QE
- Si nécessaire, éditer les requêtes SQL avec FETCH FIRST N ROWS ONLY
3. Fermeture systématique des curseurs
Pattern utilisé partout :
q = self.db.create_query(query)
result = q.read() # ou q.read_value() ou q.execute()
q.close() # ← Toujours présent
Pourquoi important : - Libère les ressources Oracle - Évite les fuites mémoire - Limite le nombre de curseurs ouverts
Optimisation des index¶
Stratégie de placement : - Index sur colonnes de jointure fréquentes - Index composites pour accélerer les WHERE composés - Pas d'index sur les tables intermédiaires (CTE)
Comment ajouter un index :
1. Éditer extract_price_offers.py
2. Modifier la méthode _create_indexes()
3. Ajouter une ligne dans le tableau indexes :
indexes = [
(f"IX_{table_name}_CLN", "ID_CLN"),
(f"IX_{table_name}_NOUVEL_INDEX", "COLONNE1, COLONNE2"), # Nouveau
]
Comment supprimer un index :
Configuration et paramétrage¶
Fichier de configuration¶
Emplacement : config/pt2qe_config.json
Structure complète :
{
"processing": {
"batch_size": 100000,
"parallel_degree": 8
},
"capping": {
"default_high": 0.05,
"default_medium": 0.15,
"default_low": 0.20,
"basiques": 0.50,
"allow_overrides": true
},
"recommendations": {
"reco1_rules": [
{
"position": "ABOVE_PL1",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL1_PL2",
"action": "NO_CHANGE",
"target": "PRIX_TARIF_ACTUEL",
"comment": "Prix déjà en PL1, pas de changement"
},
{
"position": "PL1_PL2",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL2_PL3",
"action": "TO_PL1",
"target": "NEW_BORNE_PL1_PL2",
"comment": "Remonter vers PL1"
},
{
"position": "PL2_PL3",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL3_PL4",
"action": "TO_PL1",
"target": "NEW_BORNE_PL1_PL2",
"comment": "Remonter vers PL1"
},
{
"position": "PL3_PL4",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL4_PL5",
"action": "TO_PL2",
"target": "NEW_BORNE_PL2_PL3",
"comment": "Remonter vers PL2"
},
{
"position": "PL4_PL5",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL5_PL6",
"action": "TO_PL3",
"target": "NEW_BORNE_PL3_PL4",
"comment": "Remonter vers PL3"
},
{
"position": "PL5_PL6",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL6_PLX",
"action": "TO_PL5",
"target": "NEW_BORNE_PL5_PL6",
"comment": "Remonter vers PL5"
},
{
"position": "PL6_PLX",
"condition": "PRIX_TARIF_ACTUEL >= NEW_PAS",
"action": "TO_PL6",
"target": "NEW_BORNE_PL6_PLX",
"comment": "Remonter vers PL6"
},
{
"position": "BELOW_PAS",
"condition": "DEFAULT",
"action": "TO_PAS",
"target": "NEW_PAS",
"comment": "Remonter au PAS minimum"
}
],
"selection_rule": "max"
},
"output": {
"csv_separator": ";",
"csv_encoding": "cp1252",
"decimal_places": 3,
"percentage_format": 2
}
}
Paramètres modifiables¶
1. Processing
"processing": {
"batch_size": 100000, // Non utilisé actuellement
"parallel_degree": 8 // Non utilisé actuellement (hardcodé dans SQL)
}
Comment modifier : - Pour parallélisme : Éditer directement les requêtes SQL (voir section Optimisation)
2. Cappings
"capping": {
"default_high": 0.05, // 5% pour sensibilité HIGH
"default_medium": 0.15, // 15% pour sensibilité MEDIUM
"default_low": 0.20, // 20% pour sensibilité LOW
"basiques": 0.50, // 50% max pour produits basiques
"allow_overrides": true // Autorise corrections par cube
}
Comment modifier :
1. Éditer config/pt2qe_config.json
2. Changer les valeurs (format décimal : 0.05 = 5%)
3. Relancer le calcul (Option 1)
Attention : Ces valeurs sont utilisées par défaut si pas de fichier capping_type_client.csv
3. Règles RECO1
Voir section "Module de calcul des recommandations" pour détails.
Comment ajouter une règle :
1. Éditer config/pt2qe_config.json
2. Ajouter un élément dans reco1_rules :
{
"position": "NOUVELLE_POSITION",
"condition": "PRIX_TARIF_ACTUEL > SEUIL",
"action": "DESCRIPTION_ACTION",
"target": "COLONNE_CIBLE",
"comment": "Explication"
}
"condition": "DEFAULT" (ELSE)
Comment modifier une règle existante :
1. Éditer config/pt2qe_config.json
2. Modifier les champs condition, target, ou comment
3. Exemple : Changer PL2_PL3 pour remonter vers PL2 au lieu de PL1 :
{
"position": "PL2_PL3",
"condition": "PRIX_TARIF_ACTUEL > NEW_BORNE_PL3_PL4",
"action": "TO_PL2", // ← Changé
"target": "NEW_BORNE_PL2_PL3", // ← Changé
"comment": "Remonter vers PL2" // ← Changé
}
4. Output
"output": {
"csv_separator": ";", // Séparateur CSV (défaut: point-virgule)
"csv_encoding": "cp1252", // Encodage (Windows)
"decimal_places": 3, // Décimales pour prix
"percentage_format": 2 // Décimales pour pourcentages
}
Comment modifier :
1. Éditer config/pt2qe_config.json
2. Changer les valeurs
3. Impact sur tous les exports CSV
Fichier de cappings type client¶
Emplacement : inputs/capping_type_client.csv
Format :
TYPE_CLIENT;CAPPING_HIGH;CAPPING_MEDIUM;CAPPING_LOW
RCI PI GI;0,025;0,05;0,075
RSI HM;0,025;0,05;0,075
RSC HM;0,025;0,05;0,075
NATIONAL;0,025;0,05;0,075
Colonnes obligatoires :
- TYPE_CLIENT : Type client (doit correspondre au mapping PT0CE)
- CAPPING_HIGH : Capping pour sensibilité HIGH (format décimal avec virgule)
- CAPPING_MEDIUM : Capping pour sensibilité MEDIUM
- CAPPING_LOW : Capping pour sensibilité LOW
Comment modifier :
1. Ouvrir inputs/capping_type_client.csv avec Excel ou éditeur texte
2. Modifier les valeurs (format : 0,025 = 2.5%)
3. Sauvegarder en CSV avec séparateur point-virgule
4. Relancer le calcul (Option 1)
Ligne NATIONAL : - Spéciale : Appliquée aux corridors NATIONAL (fallback) - Si absente, valeurs par défaut de la config
Variables d'environnement¶
PYTHONPATH :
Pourquoi nécessaire :
- Charge les modules sysco.* (application, db.oracle)
- Défini dans les fichiers .bat
Comment modifier :
1. Éditer START.bat, 1_calculer_recommendations.bat, etc.
2. Changer la ligne set PYTHONPATH=...
3. Attention : Ne pas supprimer, sinon erreurs d'import
Gestion des erreurs et récupération¶
Validation des prérequis¶
Au démarrage de pt2qe_main.py :
-
Vérification fichier capping :
-
Vérification tables PT1CE : Via
test_connection.py --check-pt1ce: -
Vérification mappings : Via
test_connection.py --check-mappings: -
Détermination périodes : Via
PeriodManager.get_last_four_complete_fiscal_quarters(): - Si échec :
ValueErroravec message explicite - Si < 4 trimestres complets :
ValueError
Comment vérifier manuellement les prérequis :
python test_connection.py TARIFAIRE --check-pt1ce
python test_connection.py TARIFAIRE --check-mappings
Logging détaillé¶
Niveaux de log :
- INFO : Étapes principales, statistiques
- WARNING : Situations anormales non bloquantes
- ERROR : Erreurs bloquantes
- DEBUG : Détails techniques (avec -vv)
Structure des logs :
2025-01-15 14:30:15 - INFO - Démarrage PRICING_TIER2_QUOTE_ENGINE v1.0.0
2025-01-15 14:30:15 - INFO - ⚠️ MODE ZOOM1 EXCLUSIF
2025-01-15 14:30:16 - INFO - Période d'analyse: 2024-02-01 → 2024-12-31
2025-01-15 14:30:16 - INFO - Trimestres fiscaux: 2024_Q01, 2024_Q02, 2024_Q03, 2024_Q04
=== Phase 1: Extraction des offres de prix avec historique ===
2025-01-15 14:30:20 - INFO - Extraction des offres avec historique de performance
2025-01-15 14:35:42 - INFO - ✓ 2,345,678 offres de prix extraites (ZOOM1 uniquement)
2025-01-15 14:35:42 - INFO - Statistiques d'enrichissement:
2025-01-15 14:35:42 - INFO - - Total offres: 2,345,678
2025-01-15 14:35:42 - INFO - - Clients uniques: 12,345
2025-01-15 14:35:42 - INFO - - Articles uniques: 23,456
...
Comment activer le mode debug :
Comment désactiver les logs :
Points de contrôle et validation¶
1. Après extraction (Phase 1) : - Vérifier nb offres > 0 - Vérifier taux enrichissement TYPE_CLIENT/TYPE_RESTAURANT > 80%
2. Après matching corridors (Phase 2) : - Vérifier taux matching > 70% - Afficher répartition MASTER/NATIONAL/NO_MATCH
3. Après calcul recommandations (Phase 3) : - Vérifier nb recommandations > 0 - Afficher hausse moyenne - Vérifier pas de hausses < 0% (anomalie)
4. Après génération finale (Phase 4) : - Vérifier nb offres exportées - Afficher répartition VALIDATION_STATUS
Comment ajouter un point de contrôle : 1. Éditer le module concerné 2. Ajouter une requête SQL de validation :
q = self.db.create_query(f"SELECT COUNT(*) FROM {table} WHERE condition_anomalie")
anomalies = q.read_value()
q.close()
if anomalies > 0:
self.app.log.warning(f"⚠️ {anomalies} anomalies détectées")
Gestion des cas limites¶
1. Division par zéro :
-- Protection dans PCT_HAUSSE_FINALE
CASE
WHEN PRIX_TARIF_ACTUEL > 0 THEN
(PRIX_RECOMMANDE - PRIX_TARIF_ACTUEL) / PRIX_TARIF_ACTUEL
ELSE 0
END
2. Valeurs NULL :
3. Offres sans corridor :
- Marquées HAS_CORRIDOR = 0, MATCH_TYPE = 'NO_MATCH'
- Exclues des calculs de recommandations
- Comptabilisées dans les stats
4. Cappings manquants : - Si TYPE_CLIENT absent du fichier : Utilise valeurs par défaut config - Si NATIONAL absent : Utilise valeurs par défaut config
5. Périodes invalides :
- Si < 4 trimestres complets : ValueError avec message explicite
- Si période incohérente : ValueError
Récupération après erreur¶
Stratégie :
- Pas de checkpoint automatique
- Tables recréées à chaque run
- Mode --skip-extraction permet de reprendre après Phase 1
Scénarios de récupération :
1. Erreur en Phase 1 (extraction) : - Corriger le problème (connexion, prérequis, etc.) - Relancer Option 1 complète
2. Erreur en Phase 2 ou 3 (calculs) : - Si erreur transitoire (connexion) : Relancer Option 1 - Si erreur de données : Corriger les données sources, relancer Option 1
3. Erreur en Phase 4 (analyses) :
- Les tables PT2QE_RECOMMENDATIONS existent déjà
- Corriger le problème
- Relancer le calcul : Les analyses seront régénérées
4. Corruption de tables :
-- Supprimer toutes les tables PT2QE
DROP TABLE PT2QE_PRICE_OFFERS PURGE;
DROP TABLE PT2QE_PRICE_OFFERS_ENRICHED PURGE;
DROP TABLE PT2QE_CAPPING_CUBES PURGE;
DROP TABLE PT2QE_RECOMMENDATIONS PURGE;
-- Relancer Option 1
Comment forcer une récupération propre : 1. Supprimer toutes les tables PT2QE (voir commandes ci-dessus) 2. Vérifier les prérequis (PT1CE, mappings) 3. Relancer Option 1 complète
Messages d'erreur courants¶
1. "Fichier capping manquant" :
Solution : Créer le fichier avec le bon format2. "Tables PT1CE_OPTIMAL_* manquantes" :
Solution : Exécuter PT1CE Option 1 puis Option 23. "Aucune offre extraite" :
Solution : Vérifier filtres, vérifier données sources4. "Impossible de trouver 4 trimestres complets" :
Solution : Vérifier données dansSYS_MD_CALENDRIER_SYSCO
5. "Connexion Oracle perdue" :
Solution : Vérifier connexion réseau, relancerPoints techniques d'implémentation¶
Connexions Oracle¶
Classe utilisée : SysOraDB (de sysco.db.oracle)
Initialisation :
Gestion des curseurs :
# Créer un curseur
q = db.create_query(sql_query)
# Exécuter et récupérer données
df = q.read() # DataFrame pandas
value = q.read_value() # Valeur unique
rows = q.execute() # Nombre de lignes affectées
# TOUJOURS fermer
q.close()
Commit :
Dispose :
Gestion des dates¶
Classe utilisée : date et datetime de Python
Conversion Python → Oracle :
from datetime import date
def format_date_oracle(dt: date) -> str:
return f"TO_DATE('{dt.strftime('%Y-%m-%d')}', 'YYYY-MM-DD')"
Utilisation dans requêtes :
start_date = date(2024, 1, 1)
query = f"""
SELECT * FROM table
WHERE DT >= {format_date_oracle(start_date)}
"""
Récupération depuis Oracle :
# Les dates Oracle sont converties automatiquement en datetime.date Python
q = db.create_query("SELECT ID_SEM FROM SYS_MD_CALENDRIER_SYSCO FETCH FIRST 1 ROW ONLY")
sem_date = q.read_value() # Type: date
q.close()
Pandas et exports¶
Lecture depuis Oracle :
Export CSV :
df.to_csv(
output_file,
index=False, # Pas d'index de ligne
sep=';', # Point-virgule (Excel français)
decimal=',', # Virgule décimale (Excel français)
encoding='cp1252' # Windows-1252 (Excel Windows)
)
Pourquoi cp1252 : - Encodage natif Excel Windows - Supporte accents français - Alternative : 'utf-8-sig' (avec BOM)
Arrondis :
# Montants : 3 décimales
df['PRIX'] = df['PRIX'].round(3)
# Pourcentages : 2 décimales (après conversion en %)
df['PCT_HAUSSE'] = (df['PCT_HAUSSE'] * 100).round(2)
Gestion des chemins¶
Classe utilisée : pathlib.Path
Création dossiers :
from pathlib import Path
output_folder = Path('outputs') / 'run_20250115_143000'
output_folder.mkdir(parents=True, exist_ok=True)
Concaténation chemins :
output_file = output_folder / 'recommendations_detail.csv'
# Équivalent: Path('outputs/run_20250115_143000/recommendations_detail.csv')
Vérification existence :
Lecture :
Construction de requêtes SQL dynamiques¶
1. Avec f-strings :
table_name = "PT2QE_PRICE_OFFERS"
query = f"""
SELECT COUNT(*) FROM {table_name}
WHERE UNIVERS = 'ZOOM1'
"""
Attention : Valider table_name pour éviter injection SQL
2. Avec paramètres :
# Pas de paramètres bindés dans PT2QE (car DDL et DML complexes)
# Alternative : Validation stricte des valeurs
if table_name not in ['PT2QE_PRICE_OFFERS', 'PT2QE_RECOMMENDATIONS']:
raise ValueError(f"Table invalide: {table_name}")
3. Génération CASE SQL depuis config :
def build_reco1_sql_case(self, table_alias: str = "ed") -> str:
rules = self.get_reco1_rules()
case_parts = ["CASE"]
for rule in rules:
condition = rule['condition'].replace("PRIX_TARIF_ACTUEL", f"{table_alias}.PRIX_TARIF_ACTUEL")
target = rule['target'].replace("NEW_", f"{table_alias}.NEW_")
if condition == "DEFAULT":
case_parts.append(f" ELSE {target}")
else:
case_parts.append(f" WHEN {condition} THEN {target}")
case_parts.append("END")
return "\n".join(case_parts)
Pattern de traitement¶
Structure standard :
class ModuleProcessor:
def __init__(self, app: SysApplication, db: SysOraDB):
self.app = app
self.db = db
def process(self, input_table: str, output_table: str):
self.app.log.info(" Traitement en cours")
# 1. Drop table si existe
try:
q = self.db.create_query(f"DROP TABLE {output_table} PURGE")
q.execute()
q.close()
except:
pass
# 2. Créer table
query = f"CREATE TABLE {output_table} COMPRESS NOLOGGING AS SELECT ..."
q = self.db.create_query(query)
q.execute()
q.close()
# 3. Stats
q = self.db.create_query(f"SELECT COUNT(*) FROM {output_table}")
count = q.read_value()
q.close()
self.app.log.info(f" ✓ {count:,} lignes traitées")
Formattage des nombres¶
Dans les logs :
# Séparateurs de milliers
print(f"{value:,}") # Ex: 1,234,567
# Pourcentages
print(f"{pct:.1%}") # Ex: 12.3%
# Décimales
print(f"{montant:.2f}") # Ex: 123.45
Dans les DataFrames :
# Arrondi
df['MONTANT'] = df['MONTANT'].round(2)
# Conversion pourcentages
df['PCT'] = (df['RATIO'] * 100).round(2)
# Format avec NVL
df['VALEUR'] = df['VALEUR'].fillna(0)
Maintenance et évolution¶
Comment ajouter une nouvelle métrique¶
Exemple : Ajouter le nombre de commandes 4Q
-
Modifier l'extraction (
extract_price_offers.py) : -
Ajouter dans la sélection finale :
-
Propager dans les tables suivantes :
PT2QE_PRICE_OFFERS_ENRICHED(via jointure)-
PT2QE_RECOMMENDATIONS(via SELECT) -
Ajouter dans les exports (
analyze_recommendations.py) : - Ajouter colonne dans
_export_recommendations_detail()
Comment ajouter un nouveau type de capping¶
Exemple : Ajouter un capping "produits fragiles" à 30%
-
Modifier la cascade (
calculate_recommandations.py) :-- Après RECO_WITH_CAPPING_BASIQUES, ajouter : RECO_WITH_CAPPING_FRAGILES AS ( SELECT rwb.*, CASE WHEN rwb.LC_ATTRIBUT = 'Fragile' THEN LEAST(rwb.RECO1_AVEC_CAPPING, rwb.PRIX_TARIF_ACTUEL * 1.30) ELSE rwb.RECO1_AVEC_CAPPING END as RECO1_AVEC_CAPPING_FRAGILE, CASE WHEN rwb.LC_ATTRIBUT = 'Fragile' AND rwb.RECO1_AVEC_CAPPING > rwb.PRIX_TARIF_ACTUEL * 1.30 THEN 1 ELSE 0 END as CAPPING_FRAGILE_APPLIED FROM RECO_WITH_CAPPING_BASIQUES rwb ), -
Modifier l'arbre de décision :
- Remplacer
RECO_WITH_CAPPING_BASIQUESparRECO_WITH_CAPPING_FRAGILES -
Utiliser
RECO1_AVEC_CAPPING_FRAGILEau lieu deRECO1_AVEC_CAPPING -
Modifier le calcul de CAPPING_APPLIED :
-
Documenter :
- Ajouter dans CAPPING.md
- Ajouter dans CONCEPTS.md
Comment modifier l'arbre de décision¶
Exemple : Ajouter un chemin pour clients premium avec KAM
-
Définir la condition :
-
Modifier DECISION_TREE :
-
Définir l'action :
-
Ajouter le RECO_TYPE :
-
Documenter :
- Ajouter dans WORKFLOW.md
- Ajouter dans CALCULS.md
Comment ajouter une nouvelle dimension d'analyse¶
Exemple : Ajouter une analyse par GEO
-
Ajouter dans analyze_recommendations.py :
def _export_statistics_by_geo(self, table: str, output_folder: Path): output_file = output_folder / 'statistics_by_geo.csv' query = f""" SELECT 'GEO' as DIMENSION, GEO as VALEUR, COUNT(*) as NB_OFFRES, COUNT(DISTINCT ID_CLN) as NB_CLIENTS, COUNT(DISTINCT ID_ART) as NB_ARTICLES, AVG(PRIX_TARIF_ACTUEL) as PRIX_MOY_ACTUEL, AVG(PRIX_RECOMMANDE) as PRIX_MOY_RECOMMANDE, AVG(PCT_HAUSSE_FINALE) as PCT_HAUSSE_MOY FROM {table} GROUP BY GEO ORDER BY GEO """ q = self.db.create_query(query) df = q.read() q.close() df.to_csv(output_file, index=False, sep=';', decimal=',', encoding='cp1252') self.app.log.info(f" → Statistiques par GEO exportées") -
Appeler dans analyze_and_export() :
-
Documenter :
- Ajouter dans EXPORTS.md
Checklist de déploiement¶
Avant le premier run¶
- [ ] PT1CE Option 1 exécutée (application PAS/PRB)
- [ ] PT1CE Option 2 exécutée (finalisation corridors → création PT1CE_OPTIMAL_*)
- [ ] Tables PT0CE_TYPE_CLIENT_MAPPING créée et remplie
- [ ] Tables PT0CE_TYPE_RESTAURANT_MAPPING créée et remplie
- [ ] Fichier
inputs/capping_type_client.csvcréé avec les cappings souhaités - [ ] Dossier
outputs/existe (créé automatiquement sinon) - [ ] Dossier
corrections/existe si corrections prévues (créé automatiquement sinon) - [ ] Connexion Oracle testée :
python test_connection.py TARIFAIRE - [ ] Prérequis validés :
python test_connection.py TARIFAIRE --check-pt1ce --check-mappings
Après chaque run¶
- [ ] Vérifier logs dans la console (pas d'erreurs)
- [ ] Vérifier fichiers générés dans
outputs/run_*/ - [ ] Analyser
recommendations_detail.csv(hausses cohérentes) - [ ] Analyser
decision_path_analysis.csv(répartition chemins) - [ ] Vérifier
capping_distribution.csv(cappings appliqués) - [ ] Comparer avec run précédent si applicable
Avant mise en production¶
- [ ] Tests sur plusieurs scénarios de cappings
- [ ] Validation métier des recommandations
- [ ] Documentation à jour
- [ ] Backup des tables PT2QE existantes (si nécessaire)
- [ ] Communication aux utilisateurs (changements, impacts)
Maintenance régulière¶
- [ ] Vérifier espace disque Oracle
- [ ] Archiver anciens runs dans
outputs/ - [ ] Purger tables PT2QE anciennes si nécessaire
- [ ] Mettre à jour PT1CE si changements de corridors
- [ ] Réviser cappings selon évolution du marché
Annexes techniques¶
Hiérarchie des tables Oracle¶
SYS_* (données sources)
├── SYS_TARIF_SIMULATION
├── SYS_FACTURE_LIGNE
├── SYS_MD_CLIENT
├── SYS_MD_CONDITION
├── SYS_MD_ARTICLE
├── SYS_MD_CALENDRIER_SYSCO
└── SYS_MD_ORIGINE_PRIX
PT0CE_* (mappings Tier 0)
├── PT0CE_TYPE_CLIENT_MAPPING
└── PT0CE_TYPE_RESTAURANT_MAPPING
PT1CE_* (corridors Tier 1)
├── PT1CE_OPTIMAL_ZOOM1
├── PT1CE_OPTIMAL_ZOOM2
└── PT1CE_OPTIMAL_ZOOM3
PT2QE_* (recommandations Tier 2)
├── PT2QE_PRICE_OFFERS
├── PT2QE_PRICE_OFFERS_ENRICHED
├── PT2QE_CAPPING_CUBES
└── PT2QE_RECOMMENDATIONS
Dépendances Python¶
Fichier : requirements.txt
oracledb>=1.4.0 # Connexion Oracle
pandas>=2.0.0 # Manipulation données
numpy>=1.24.0 # Calculs numériques
python-dateutil>=2.8.2 # Gestion dates
openpyxl>=3.1.0 # Export Excel (si nécessaire)
tqdm>=4.64.0 # Barre progression (non utilisé)
scipy>=1.10.0 # Stats avancées (non utilisé)
Installation :
Remarque : Les modules sysco.* sont dans P:\PRD\Python\00_Commun\libs
Conventions de nommage¶
Tables Oracle :
- Préfixe : PT2QE_
- Format : PT2QE_<NOM_LOGIQUE>
- Exemples : PT2QE_PRICE_OFFERS, PT2QE_RECOMMENDATIONS
Index Oracle :
- Préfixe : IX_<TABLE>_
- Format : IX_<TABLE>_<COLONNES>
- Exemples : IX_PT2QE_PRICE_OFFERS_CLN, IX_PT2QE_PRICE_OFFERS_MAIN
Fichiers CSV :
- Format : <nom_descriptif>.csv
- Séparateur : Point-virgule (;)
- Encodage : CP1252
- Décimale : Virgule (,)
Dossiers de sortie :
- Format : <type>_YYYYMMDD_HHMMSS
- Exemples : run_20250115_143000, corrections_20250115_150000, final_20250115_153000
Variables Python :
- Tables : snake_case (ex: offers_table, recommendations_table)
- Classes : PascalCase (ex: RecommendationCalculator)
- Constantes : UPPER_CASE (ex: TABLE_PREFIX, APP_VERSION)
Glossaire technique¶
- CTE : Common Table Expression (WITH clause)
- COMPRESS : Compression Oracle (économise espace disque)
- NOLOGGING : Pas de logs redo (accélère création tables)
- PURGE : Suppression immédiate (pas de corbeille)
- PARALLEL : Exécution parallèle (utilise plusieurs threads Oracle)
- Fallback : Solution de secours (ex: NATIONAL si MASTER échoue)
- Cascade : Traitement séquentiel (ex: cappings successifs)
- Tier : Niveau de traitement (Tier 1 = corridors, Tier 2 = recommandations)
- Scope temporel : Période d'analyse des données
- 4Q : 4 derniers quarters (trimestres fiscaux)