diff --git a/README.md b/README.md deleted file mode 100644 index f3cdde6..0000000 --- a/README.md +++ /dev/null @@ -1,80 +0,0 @@ -# 🚅 Afficheurs Ferrovia - -Application web de tĂ©lĂ©affichage ferroviaire et d'information voyageurs en temps rĂ©el (PIDS - Passenger Information Display Systems), reproduisant fidĂšlement les Ă©crans d'affichage des gares françaises (SNCF). - ---- - -## ✹ FonctionnalitĂ©s - -- **📡 DonnĂ©es RĂ©elles & ComplĂštes :** - - Horaires thĂ©oriques issus de l'archive officielle **GTFS SNCF** (3 507 gares, 41 847 circulations : TGV INOUI, OUIGO, TER, INTERCITÉS, ICE, TGV Lyria). - - Retards, perturbations et suppressions en direct via **GTFS-RT**. - - DĂ©tection et affichage des **voies et quais rĂ©els** via les flux **SIRI-Lite**. -- **đŸ–„ïž Multiples GĂ©nĂ©rations d'Afficheurs :** - - **AFL (Afficheurs Fluides & Lumineux) :** Écrans modernes pour halls et quais (DĂ©parts / ArrivĂ©es). - - **Classiques :** Écrans d'affichage historiques (DĂ©parts, ArrivĂ©es, Écrans de Quai avec liste des arrĂȘts desservis). - - **EVA :** Écrans vidĂ©o voyageurs enrichis. -- **🔊 Module d'Annonces Sonores :** Jingles et annonces synthĂ©tiques en temps rĂ©el. -- **🔍 Recherche Intelligente des Gares :** Reconnaissance instantanĂ©e des gares, codes UIC, variantes d'Ă©criture et abrĂ©viations (`Valence-TGV`, `Paris-Montparnasse`, `Lyon-Part-Dieu`, etc.). - ---- - -## 📋 Patchnotes / Historique des Versions - -Pour consulter le journal dĂ©taillĂ© de toutes les versions, consultez le fichier **[CHANGELOG.md](./CHANGELOG.md)**. - -
-

🚀 Version 4.0.0 (V4) - Derniùre version

- -- **IntĂ©gration GTFS & GTFS-RT Exhaustive :** 3 507 gares et 41 847 circulations rĂ©elles intĂ©grĂ©es sans donnĂ©es mockĂ©es. -- **Quais RĂ©els SIRI-Lite :** Extraction en continu des quais rĂ©els avec isolation stricte par gare sĂ©lectionnĂ©e et par arrĂȘt. -- **Recherche Intelligente :** Algorithme tolĂ©rant aux tirets, accents et alias pour tous les noms de gares. -- **Normalisation des Horaires :** Prise en charge des trains de nuit et horaires modulo 24h. -- **Codebase Propre :** RĂ©solution complĂšte de tous les avertissements ESLint (`Compiled successfully`). - -
- -
-

🚆 Version 3.0.0 (V3)

- -- Nouveaux types d'afficheurs AFL, Classiques et EVA. -- Module d'annonces sonores en temps réel. -- Gestion avancée des trains combinés / multi-tranches et des substitutions par autocar. - -
- -
-

🚉 Version 2.0.0 (V2)

- -- Écrans dĂ©diĂ©s par voie et quai avec liste dĂ©roulante des arrĂȘts aval. -- Alternance dynamique des Ă©tats de circulation et synchronisation automatique. - -
- -
-

🏁 Version 1.0.0 (V1)

- -- Lancement initial de la plateforme avec horloge temps réel et reproduction de la signalétique ferroviaire. - -
- ---- - -## đŸ› ïž Installation & DĂ©marrage - -```bash -# Installer les dĂ©pendances -npm install - -# Lancer le serveur de dĂ©veloppement (http://localhost:3000) -npm start - -# Compiler pour la production -npm run build -``` - ---- - -## 📄 Licence - -Projet dĂ©veloppĂ© pour la simulation et la restitution d'Ă©crans d'information ferroviaire. DonnĂ©es fournies sous licence ouverte via transport.data.gouv.fr et SNCF Open Data. diff --git a/espacerails.html b/espacerails.html new file mode 100644 index 0000000..63e9831 --- /dev/null +++ b/espacerails.html @@ -0,0 +1,1005 @@ + + + + + +EspaceRails.com - RĂ©el - La numerotation des trains + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ home + + + + +
+
+ + + + +
+
+ +
+ +
ï»ż

Les Articles ...

+ +
+ +
+ La numérotation des trains +
Mis à jour le Dimanche 04 novembre 2012 à 20h09min dans Réel - Version 2
+

Autant vous le dire tout de +suite vous aurez mal de crùne à la fin de la présente page et pourtant je +peux vous dire que j'ai fait light. C'est donc pourquoi les cas généraux sont +uniquement répertoriés dans cette page.

+

Six +chiffres maxi un point c'est tout ! :

+

Oui c'est vrai chaque train qui +roule sur le rĂ©seau français est porteur d'un numĂ©ro (son numĂ©ro de sĂ©cu en +sorte), c'est ainsi que ce numĂ©ro en dit long sur le train considĂ©rĂ©. 

+

Afin +d'éviter toute confusion un numéro n'est attribué qu'à une seule circulation +à la fois. Il permet en temps réel de connaßtre le type de train, l'axe de +circulation sur lequel il circule et en plus son sens de circulation.

+

Question +fondamentale : Le sens de circulation :

+

Sens Pair +:

+
    +
  • Nous sommes ici dans le sens Province Paris, + ceux ci circulent sur la voie 2 ou 2 bis du rĂ©seau.
  • +
+

Sens +Impair :

+
    +
  • +

    Ici, c'est le sens Paris + Province qui est privilégié, les trains circulant normalement sur la voie + 1 ou 1 bis

  • +
+

ConcrĂštement certains trains pourront prendre +successivement un sens pair ou impair (et donc une terminaison de numĂ©ro) en +fonction de leur typologie de circulation. 

+

Oui un train remontant provisoirement +sur Paris puis bifurquant au sud est pour (sens impair) se verra attribué deux +terminaisons de numéros sur son parcours.

+

Des tranches de numéros +par activités :

+

Les Trains FRET :

+

 

+

Les trains Internationaux +:

+
    +
  • En principe ceux ci gardent le mĂȘme numĂ©ro + sur tout leur parcours...  Ca commence !
  • +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type de TrainsTranche + de numéro du trainObs
Trains CombinĂ©s40 000A43 999Trains de + conteneurs et remorques routiĂšres   
Trains du Lotissement 44 000A46 429Trains + composĂ©s de tout types de Wagons circulant entre deux triages
Trains Entiers46 430 A49 999Trains d'un + seul bloc ne transitant pas par un triage
+
+
+

 

+

Les trains Nationaux :

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Type de TrainsTranche + de numéro du trainObs
Trains de + l'Organisation SpĂ©cialeTrains + qui ne passent pas par un triage ils sont acheminĂ©s d'un bloc chez un + client (voitures) 
 TGV Poste6990A6999 
 Trains ME + (Messagerie Express) V>/= Ă  160 Km/h4190 A4199 
49904999
57205739
 Trains ME + Vitesse > 140 Km/h50000A50099Autres que ME
5010050199ME
 Trains Ă  + vitesse Ă©gale Ă  120 Km/h50200A50999Trains + CombinĂ©s
5100051099Automobiles + et PiÚces détachés y afférant
5110051699ME
5180051999Autres
 Trains Ă  + vitesse </= Ă  100 Km/h52000A52999Trains + CombinĂ©s
5300053699Automobiles + et PiÚces détachés y afférant
5370053799ME
5390053999Autres
Trains du + lotissement inter triagesTrains + circulant entre deux triages
  54000 59999 
Trains EntiersTrains + circulant entre deux triages
  70000 99999Tranches + 86000 Ă  86999 et 96000 Ă  96999 non attribuĂ©e Ă  ces trains
+
+
+

 

+

Les trains +Régionaux :

+

Ceux ci sont classés dans la +tranche 400 000 à 439 999. Chaque région disposant d'une tranche qui lui est +attribuée.

+

C'est +ainsi par exemple que la rĂ©gion de Bordeaux se voit attribuĂ©e la tranche de +trains Fret rĂ©gionaux sous la sous numĂ©rotation suivante : 414 000  +Ă   414 699

+

 

+

Les trains +de machines et de machines seules :

+

Les +marches haut le pied c'est Ă  dire une ou deux machines, ou encore les trains de +machines composĂ©s de trois Ă  13 machines sont Ă  inscrire dans la tranche de +300 000 Ă  399 999. Cette mĂȘme tranche est dĂ©coupĂ©e en deux sous tranches que +nous n'Ă©tudierons pas par simplification... Merci !

+

Pour simplifier également, les +trains spéciaux, les trains militaires et ceux de la grande ceinture +Parisienne, mais aussi les trains de service ne sont pas traités dans cette +page.

+

 

+

Les Trains +Voyageurs :

+

La numérotation des trains +voyageurs dans les tableaux ci dessous concerne les trains acheminant des +voyageurs. Les trains W (voyageur vide) ceux ci se classent dans la tranche 700 +000 à 749000 s'il est en route pour assurer un train en charge. Dans les autres +cas, retour à vide, acheminement on les trouvera classé dans la tranche 750 +000 à 799 999....

+

En outre les trains dits trains +des neiges peuvent ĂȘtre classĂ©s dans la tranche 820 000 Ă  820 999....

+

 

+

TER :

+
    +
  • +

    la numérotation des TER + permet de connaßtre la région qui est organisatrice du transport.

  • +
  • +

    Dans le tableau ci dessous par + région, la premiÚre tranche constitue les trains ou la réservation est + possible. La deuxiÚme tranche concerne les autres trains. Attention les + régions frontaliÚres avec des tranches spéciales ne sont pas reprises + dans le tableau dans un but de simplification.

  • +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RégionTranche + de numéro du trainRégionTranche + de numéro du train
Alsace16000A16099Lorraine16100A16199
830000832999833000837999
Aquitaine16900A16999Midi-Pyrénées17100A17199
865000867999870000872999
Auvergne17200A17299Nord + Pas de Calais16350A16449
873000875999841000846999
Bourgogne17750A17849Basse + Normandie16550A16599
891000893999852000853999
Bretagne16600A16649Haute + Normandie16500A16549
854000856999850000851999
Centre16750A168749Pays + de Loire16650A16749
860000862999857000859999
Champagne + Ardenne16200A16349Picardie16450A16499
838000840999847000849999
Franche-Comté17850A17899Poitou + Charentes16850A16899
894000895999863000864999
Languedoc- + Roussillon17300A17349Provence + Alpes Cote d'Azur17350A17499
876000878999879000881999
Limousin17000A17099RhĂŽne + Alpes17500 + et 17900A17749 + et 17999
868000869999882000890999
+
+
+
+
    +
  • +

    Navette St Pierre des Corps + - Tours, et Orléans - les Aubrais : Tranche 100000 à 109999

  • +
+
+

  +

Grandes +Lignes :

+

Ici on trouve et on classe des +trains du service GL qui comprend des TGV, des trains de voyageurs nationaux et +internationaux...

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Type de Trains

+
Tranche + de numéro du trainObs
+

Trains + Classiques GL Internationaux

+
1A499  
+

Autres Trains + Classiques GL Internationaux 

+
1050A1299Temporaires + ou Doublés
+

Auto Train + Internationaux

+
1300 A1499  
+

Auto Train + Nationaux

+
24000A24999 
+

Trains GL Nord + Est

+
1000 + et 1500A1049 + et 2999  
+

GL Nord Est, les + TGV

+
7000A7999  
+

Trains GL + Atlantique

+
3000 A4199  
+

GL Atlantique, + les TGV

+
8000A8999  
+

Trains GL Sud + Est

+
5000A5999  
+

GL Sud Est, les + TGV

+
6000A6999  
+

Trains Grandes + Lignes Intersecteurs

+
5500A5999  
+

Grandes Lignes + Intersecteurs, les TGV

+
6000 A6999  
+

Trains Spéciaux + tractés par les Dir Region.

+
20000A24999  
+

Trains Spéciaux + Nord Est

+
25000 A25199  
+

Trains Spéciaux + TGV Nord Est

+
26000A26199 
+

Trains Spéciaux + Atlantique

+
25200 A25399  
+

Trains Spéciaux + TGV Atlantique

+
26200A26299 
+

Trains Spéciaux + Sud Est

+
25400 A25699  
+

Trains Spéciaux + TGV Sud Est

+
26400A26699 
+

Trains Spéciaux + Intersecteurs

+
25700 A25999  
+

Trains Spéciaux + TGV Intersecteurs

+
26700A26999  
+

Trains Affrétés

+
27000A28999 
+

Trains de PĂšlerins

+
29000A29999 
+
+
+  +

Transilien :

+

Les trains du service Parisien sont +immatriculĂ©s en fonction de la gare Parisienne tĂȘte de ligne ou de la ligne +RER sur laquelle ils circulent.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+

Type de Trains

+
Tranche + de numéro du trainObs
+

Paris Est et RER + E

+
110000A119999   
+

Paris Nord et + RER D

+
120000A129999 
+

Paris St Lazare

+
130000A139999   
+

RER C

+
140000A149999  
+

Paris Gare de + Lyon et RER D

+
150000A159999   
+

Paris + Montparnasse

+
160000A169999   
+

RER A

+
   Ces + trains exploitĂ©s en collaboration avec la RATP, bĂ©nĂ©ficient d'une + numĂ©rotation particuliĂšre
+
+
Posté initialement par Eric LIMOUSIN le Dimanche 04 novembre 2012 à 20h09min

Retour >
+
+ +
+ + +
Réagir sur cet Article ?
Pas de commentaires !
ï»ż
+ + + +
+ + + + + + + +
+ + +
+ +
+ + + +
+ + + + + + diff --git a/patch_afficheurs.js b/patch_afficheurs.js deleted file mode 100644 index 04d5183..0000000 --- a/patch_afficheurs.js +++ /dev/null @@ -1,35 +0,0 @@ -const fs = require('fs'); -const path = require('path'); - -const files = [ - { p: 'src/pages/afficheurs/afl/AFLDeparts.js', arr: false }, - { p: 'src/pages/afficheurs/afl/AFLArrivees.js', arr: true }, - { p: 'src/pages/afficheurs/classiques/quais/ClassiquesDepartsQuais.js', arr: false }, - { p: 'src/pages/afficheurs/classiques/quais/ClassiquesArriveesQuais.js', arr: true }, - { p: 'src/pages/afficheurs/afl/quais/AFLDepartsQuais.js', arr: false }, - { p: 'src/pages/afficheurs/afl/quais/AFLArriveesQuais.js', arr: true }, -]; - -for (const { p, arr } of files) { - const fullPath = path.join(process.cwd(), p); - if (!fs.existsSync(fullPath)) { - console.log(`File not found: ${fullPath}`); - continue; - } - let content = fs.readFileSync(fullPath, 'utf8'); - - if (!content.includes('import useAutoAnnonce from')) { - content = content.replace( - "import useAnnonceSonoreRealtime from '../../../hooks/useAnnonceSonoreRealtime';", - "import useAnnonceSonoreRealtime from '../../../hooks/useAnnonceSonoreRealtime';\nimport useAutoAnnonce from '../../../hooks/useAutoAnnonce';" - ); - } - - if (!content.includes('useAutoAnnonce(')) { - const hookCall = ` useAnnonceSonoreRealtime(resolvedGareId);\n useAutoAnnonce(horaires, currentTime, ${arr});`; - content = content.replace(" useAnnonceSonoreRealtime(resolvedGareId);", hookCall); - } - - fs.writeFileSync(fullPath, content, 'utf8'); - console.log(`Patched ${p}`); -} diff --git a/plan_implementation_ter.md b/plan_implementation_ter.md new file mode 100644 index 0000000..1dda48a --- /dev/null +++ b/plan_implementation_ter.md @@ -0,0 +1,61 @@ +# Plan d'implĂ©mentation : Correction de l'affichage des types de trains (TER RĂ©gionaux) + +## ProblĂ©matique analysĂ©e +Les donnĂ©es brutes issues des flux GTFS de la SNCF indiquent souvent simplement le type commercial gĂ©nĂ©rique `"TER"`, sans prĂ©ciser la marque rĂ©gionale (Mobigo, liO, RĂ©mi, Hauts-de-France, etc.). +Actuellement, l'application tente de dĂ©duire ces marques via un patch "en dur" dans `src/utils/trainLogoHelper.js` pour Mobigo (`startsWith('89')` ou `startsWith('84')`). +Ce patch est **erronĂ©** : +1. La sĂ©rie `84xxxx` englobe les trains des Hauts-de-France (ex: `841xxx`, `844947` sur Dunkerque/Valenciennes). C'est ce qui cause l'affichage "MOBIGO" en erreur sur l'Image 1. +2. L'Image 2 montre des trains d'Occitanie (876406) affichĂ©s comme "RĂ©mi" et des confusions entre tranches. L'absence d'identification stricte basĂ©e sur un rĂ©fĂ©rentiel fiable entraĂźne des conflits. + +## Contraintes respectĂ©es +- **Pas de variables locales / gĂ©ographiques** : Suppression totale ou non-utilisation des listes de villes associĂ©es Ă  des rĂ©gions (ex: `detectRegionByRouteScore` qui associe 'Dunkerque' Ă  HDF). +- **Utilisation du numĂ©ro de circulation de maniĂšre fiable** : Nous nous basons **strictement** sur le rĂ©fĂ©rentiel d'EspaceRails fourni pour l'immatriculation des TER. + +## Actions prĂ©vues (ImplĂ©mentation) + +### 1. Refonte du fallback rĂ©gional dans `trainLogoHelper.js` +Nous allons remplacer la condition codĂ©e en dur (et fausse) pour "MOBIGO" par un utilitaire de classification exhaustif basĂ© fidĂšlement sur les donnĂ©es d'EspaceRails. Cette fonction sera appelĂ©e si le train est identifiĂ© comme un `TER` gĂ©nĂ©rique. + +### 2. Plan de numĂ©rotation dĂ©terministe (Le "Dictionnaire" EspaceRails) +La fonction extraira le numĂ©ro (ex: `844947` => prĂ©fixe `844`) et appliquera le tableau de correspondance suivant, construit Ă  partir de votre source : + +* **Grand Est (Fluo)** + * *Alsace* : 830 Ă  832, 160 + * *Lorraine* : 833 Ă  837, 161 + * *Champagne-Ardenne* : 838 Ă  840, 162 +* **Hauts-de-France** + * *Nord-Pas-de-Calais* : 841 Ă  846, 163 Ă  164 + * *Picardie* : 847 Ă  849, 164 +* **Normandie (Nomad)** + * *Haute-Normandie* : 850 Ă  851, 165 + * *Basse-Normandie* : 852 Ă  853, 165 +* **Bretagne (BreizhGo)** : 854 Ă  856, 166 +* **Pays de la Loire (AlĂ©op)** : 857 Ă  859, 166 Ă  167 +* **Centre-Val de Loire (RĂ©mi)** : 860 Ă  862, 167 Ă  168 +* **Nouvelle-Aquitaine** + * *Poitou-Charentes* : 863 Ă  864, 168 + * *Aquitaine* : 865 Ă  867, 169 + * *Limousin* : 868 Ă  869, 170 +* **Occitanie (liO)** + * *Midi-PyrĂ©nĂ©es* : 870 Ă  872, 171 + * *Languedoc-Roussillon* : 876 Ă  878, 173 +* **Auvergne-RhĂŽne-Alpes** + * *Auvergne* : 873 Ă  875, 172 + * *RhĂŽne-Alpes* : 882 Ă  890, 175 Ă  179 +* **Provence-Alpes-CĂŽte d'Azur (ZOU)** : 879 Ă  881, 173 Ă  174 +* **Bourgogne-Franche-ComtĂ© (Mobigo)** + * *Bourgogne* : 891 Ă  893, 177 Ă  178 + * *Franche-ComtĂ©* : 894 Ă  895, 178 + +*(Ce rĂ©fĂ©rentiel montre bien que "841" est en Hauts-de-France (NPDC), ce qui corrige l'Img 1, et que "876" est bien Languedoc-Roussillon (liO), ce qui corrige l'Img 2).* + +### 3. Application dans `getTrainLogoAndMeta` +Si l'opĂ©rateur brut du GTFS est un terme gĂ©nĂ©rique (comme `"TER"`), la fonction : +1. Extraira le numĂ©ro Ă  5 ou 6 chiffres. +2. Analysera les 3 premiers chiffres (ex: `844`). +3. Consultera le dictionnaire dĂ©terministe EspaceRails. +4. Remplacera le `typeLabel` et associera le bon logo via `logoLookup`. + +> Cela garantit la fiabilitĂ© demandĂ©e, supprime l'usage de variables locales "villes", et solutionne dĂ©finitivement les bugs d'affichage des Images 1 et 2 en s'appuyant sur les prĂ©fixes documentĂ©s. + +Validez-vous l'intĂ©gration de cette logique dans `src/utils/trainLogoHelper.js` ? diff --git a/query.js b/query.js deleted file mode 100644 index dfec8ba..0000000 --- a/query.js +++ /dev/null @@ -1,19 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); - -const supabaseUrl = process.env.REACT_APP_SUPABASE_URL; -const supabaseAnonKey = process.env.REACT_APP_SUPABASE_ANON_KEY; -const supabase = createClient(supabaseUrl, supabaseAnonKey); - -async function run() { - const { data: gares, error: err1 } = await supabase.from('gares').select('*').ilike('nom', '%Ouges%').limit(1); - console.log("Gares:", gares); - if (err1) console.error(err1); - - const { data: horaires, error: err2 } = await supabase.from('horaires').select('*').limit(5); - // find one that stops at Ouges - const { data: horairesOuges, error: err3 } = await supabase.from('horaires').select('*').ilike('gares_desservies', '%Ouges%').limit(2); - console.log("Horaires Ouges:", JSON.stringify(horairesOuges, null, 2)); -} - -run(); diff --git a/query2.js b/query2.js deleted file mode 100644 index 5237c33..0000000 --- a/query2.js +++ /dev/null @@ -1,11 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); - -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const { data, error } = await supabase.from('horaires').select('numero_train, gare_depart_nom, gare_arrivee_nom, gares_desservies, quai_reel').or('gare_depart_nom.ilike.%Ouges%,gare_arrivee_nom.ilike.%Ouges%').limit(5); - console.log("Horaires Ouges:", JSON.stringify(data, null, 2)); -} - -run(); diff --git a/query3.js b/query3.js deleted file mode 100644 index 15f45ed..0000000 --- a/query3.js +++ /dev/null @@ -1,13 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); - -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const { data, error } = await supabase.from('horaires').select('numero_train, gare_depart_nom, gare_arrivee_nom, gares_desservies, quai_reel').limit(10); - // find any train with Ouges - const filtered = data.filter(h => JSON.stringify(h).includes("Ouges")); - console.log("Horaires Ouges filtered:", JSON.stringify(filtered, null, 2)); -} - -run(); diff --git a/query4.js b/query4.js deleted file mode 100644 index d62b50e..0000000 --- a/query4.js +++ /dev/null @@ -1,12 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); - -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const { data, error } = await supabase.from('horaires').select('*').limit(1); - console.log("Horaires schema:", Object.keys(data[0] || {})); - if (error) console.error("Error:", error); -} - -run(); diff --git a/query5.js b/query5.js deleted file mode 100644 index e956468..0000000 --- a/query5.js +++ /dev/null @@ -1,19 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); - -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const { data, error } = await supabase.from('horaires').select('numero_train, gare_depart_nom, gare_arrivee_nom, gares_desservies').or('gare_depart_nom.ilike.%Ouges%,gare_arrivee_nom.ilike.%Ouges%'); - - if (!data || data.length === 0) { - // try to fetch all and filter in JS - const { data: allData } = await supabase.from('horaires').select('numero_train, gare_depart_nom, gare_arrivee_nom, gares_desservies').limit(200); - const filtered = allData.filter(h => JSON.stringify(h).includes("Ouges")); - console.log("Horaires Ouges (JS filter):", JSON.stringify(filtered.slice(0, 2), null, 2)); - } else { - console.log("Horaires Ouges:", JSON.stringify(data.slice(0, 2), null, 2)); - } -} - -run(); diff --git a/query_all.js b/query_all.js deleted file mode 100644 index 7f73d07..0000000 --- a/query_all.js +++ /dev/null @@ -1,9 +0,0 @@ -require('dotenv').config(); -const { createClient } = require('@supabase/supabase-js'); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); -async function run() { - const { data, error } = await supabase.from('annonces_diffusions').select('*').order('created_at', { ascending: false }).limit(1); - if (error) console.error(error); - else console.log(JSON.stringify(data[0], null, 2)); -} -run(); diff --git a/query_annonces.js b/query_annonces.js deleted file mode 100644 index bb90ed0..0000000 --- a/query_annonces.js +++ /dev/null @@ -1,9 +0,0 @@ -require('dotenv').config(); -const { createClient } = require('@supabase/supabase-js'); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); -async function run() { - const { data, error } = await supabase.from('annonces').select('*').limit(2); - if (error) console.error(error); - else console.log(JSON.stringify(data, null, 2)); -} -run(); diff --git a/query_columns.js b/query_columns.js deleted file mode 100644 index 4d4f7b8..0000000 --- a/query_columns.js +++ /dev/null @@ -1,9 +0,0 @@ -require('dotenv').config(); -const { createClient } = require('@supabase/supabase-js'); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); -async function run() { - const { data, error } = await supabase.from('annonces_diffusions').select('*').limit(1); - if (error) console.error(error); - else console.log(Object.keys(data[0])); -} -run(); diff --git a/query_horaires.js b/query_horaires.js deleted file mode 100644 index 9dc2278..0000000 --- a/query_horaires.js +++ /dev/null @@ -1,9 +0,0 @@ -require('dotenv').config(); -const { createClient } = require('@supabase/supabase-js'); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); -async function run() { - const { data, error } = await supabase.from('horaires').select('*').limit(1); - if (error) console.error(error); - else console.log(JSON.stringify(data[0], null, 2)); -} -run(); diff --git a/query_join.js b/query_join.js deleted file mode 100644 index 54b7e28..0000000 --- a/query_join.js +++ /dev/null @@ -1,12 +0,0 @@ -require('dotenv').config(); -const { createClient } = require('@supabase/supabase-js'); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); -async function run() { - const { data, error } = await supabase.from('annonces_diffusions').select(` - *, - annonce:annonces_enregistrees(variables_values) - `).order('created_at', { ascending: false }).limit(1); - if (error) console.error(error); - else console.log(JSON.stringify(data[0], null, 2)); -} -run(); diff --git a/src/services/trainClassifier.js b/src/services/trainClassifier.js new file mode 100644 index 0000000..ae043fb --- /dev/null +++ b/src/services/trainClassifier.js @@ -0,0 +1,935 @@ +/** + * Moteur de classification et de corrĂ©lation des types de trains et opĂ©rateurs ferroviaires + * ferrovia-infogare - Compatible afficheurs classiques (Hall & Quai) + * + * HiĂ©rarchie de dĂ©cision : + * 1. Codes d'exploitation GTFS officiels (trip_id SNCF Open Data) + * 2. DĂ©tection des services spĂ©cifiques (Lyria, Ouigo Classique, IntercitĂ©s de Nuit, etc.) + * 3. Plan de NumĂ©rotation officiel SNCF (sĂ©ries rĂ©gionales TER hermĂ©tiques par blocs de numĂ©ros) + * 4. DĂ©tection gĂ©ographique par score d'arrĂȘts et de terminus (sans faux-positifs) + * 5. Re-routage vers TER gĂ©nĂ©rique ou SNCF Voyageurs en dernier recours + */ + +// Normalisation des chaĂźnes (minuscules, sans accents, espaces normalisĂ©s) +export const normalize = (str) => { + if (!str) return ''; + return String(str) + .toLowerCase() + .normalize('NFD') + .replace(/[\u0300-\u036f]/g, '') + .replace(/[^a-z0-9]/g, ' ') + .replace(/\s+/g, ' ') + .trim(); +}; + +// Extraction des chiffres d'un numĂ©ro de train (sans zĂ©ros initiaux) +export const extractTrainDigits = (num) => { + if (!num) return ''; + const digits = String(num).replace(/\D/g, ''); + return digits.replace(/^0+/, '') || digits; +}; + +// Services routiers par autocar (3xxxx / 3xxxxx) +export const isCarTrainNumber = (num) => { + const d = String(num || '').replace(/\D/g, ''); + return (d.length === 5 && d.startsWith('3')) || (d.length === 6 && d.startsWith('3')); +}; + +/** + * RĂ©fĂ©rentiel des marques, types de trains et logos (monochrome Hall & couleur Quai) + */ +export const BRANDS = { + INOUi: { + id: 'inoui', + typeLabel: 'TGV inOUI', + quaiLabel: 'TGV INOUI', + operator: 'SNCF Voyageurs', + category: 'TGV', + isTgv: true, + hallLogo: '/type/logo-inoui.svg', + quaiLogo: '/type/quais/logo-inoui-color.svg', + showVoyageursText: false + }, + TGV_GENERIC: { + id: 'tgv', + typeLabel: 'TGV', + quaiLabel: 'TGV', + operator: 'SNCF Voyageurs', + category: 'TGV', + isTgv: true, + hallLogo: '/type/logo-tgv.svg', + quaiLogo: '/type/quais/logo-tgv-color.svg', + showVoyageursText: false + }, + LYRIA: { + id: 'lyria', + typeLabel: 'TGV Lyria', + quaiLabel: 'TGV Lyria', + operator: 'Lyria SAS', + category: 'TGV', + isTgv: true, + hallLogo: '/type/logo-lyria.svg', + quaiLogo: '/type/quais/logo-lyria-color.svg', + showVoyageursText: false + }, + OUIGO_CLASSIQUE: { + id: 'ouigo-classique', + typeLabel: 'OUIGO Train Classique', + quaiLabel: 'OUIGO Classique', + operator: 'SNCF Voyageurs', + category: 'GRANDES_LIGNES', + isTgv: false, + hallLogo: '/type/logo-ouigo-classique.svg', + quaiLogo: '/type/quais/logo-ouigo-classique-color.svg', + showVoyageursText: false + }, + OUIGO_GV: { + id: 'ouigo', + typeLabel: 'OUIGO', + quaiLabel: 'OUIGO', + operator: 'SNCF Voyageurs', + category: 'TGV', + isTgv: true, + hallLogo: '/type/logo-ouigo.svg', + quaiLogo: '/type/quais/logo-ouigo-color.svg', + showVoyageursText: false + }, + INTERCITES_NUIT: { + id: 'intercites-nuit', + typeLabel: 'INTERCITÉS DE NUIT', + quaiLabel: 'IntercitĂ©s de Nuit', + operator: 'SNCF Voyageurs', + category: 'GRANDES_LIGNES', + isTgv: false, + hallLogo: '/type/logo-lunea.svg', + quaiLogo: '/type/quais/logo-lunea-color.svg', + showVoyageursText: false + }, + INTERCITES: { + id: 'intercites', + typeLabel: 'INTERCITÉS', + quaiLabel: 'INTERCITÉS', + operator: 'SNCF Voyageurs', + category: 'GRANDES_LIGNES', + isTgv: false, + hallLogo: '/type/logo-intercites.svg', + quaiLogo: '/type/quais/logo-intercite-color.svg', + showVoyageursText: false + }, + EUROSTAR: { + id: 'eurostar', + typeLabel: 'EUROSTAR', + quaiLabel: 'Eurostar', + operator: 'Eurostar Group', + category: 'INTERNATIONAL', + isTgv: true, + hallLogo: '/type/logo-eurostar.svg', + quaiLogo: '/type/quais/logo-eurostar-color.svg', + showVoyageursText: false + }, + THALYS: { + id: 'thalys', + typeLabel: 'THALYS', + quaiLabel: 'Thalys', + operator: 'Eurostar Group', + category: 'INTERNATIONAL', + isTgv: true, + hallLogo: '/type/logo-thalys.svg', + quaiLogo: '/type/quais/logo-thalys-color.svg', + showVoyageursText: false + }, + ICE: { + id: 'ice', + typeLabel: 'ICE', + quaiLabel: 'ICE', + operator: 'DB / SNCF', + category: 'INTERNATIONAL', + isTgv: true, + hallLogo: '/type/logo-ice.svg', + quaiLogo: '/type/quais/logo-ice-color.svg', + showVoyageursText: false + }, + DB: { + id: 'db', + typeLabel: 'DB', + quaiLabel: 'DB', + operator: 'Deutsche Bahn', + category: 'INTERNATIONAL', + isTgv: false, + hallLogo: '/type/logo-db.svg', + quaiLogo: '/type/quais/logo-db-color.svg', + showVoyageursText: false + }, + FRECCIAROSSA: { + id: 'frecciarossa', + typeLabel: 'FRECCIAROSSA', + quaiLabel: 'Frecciarossa', + operator: 'Trenitalia France', + category: 'INTERNATIONAL', + isTgv: true, + hallLogo: '/type/logo-frecciarossa.svg', + quaiLogo: '/type/quais/logo-frecciarossa-color.svg', + showVoyageursText: false + }, + RENFE_AVE: { + id: 'renfe-ave', + typeLabel: 'Renfe AVE', + quaiLabel: 'Renfe AVE', + operator: 'Renfe', + category: 'INTERNATIONAL', + isTgv: true, + hallLogo: '/type/logo-renfeave.svg', + quaiLogo: '/type/quais/logo-renfeave-color.svg', + showVoyageursText: false + }, + AUTOCAR: { + id: 'autocar', + typeLabel: 'AUTOCAR', + quaiLabel: 'Autocar', + operator: 'SNCF Voyageurs', + category: 'AUTOCAR', + isTgv: false, + hallLogo: '/type/track-car.svg', + quaiLogo: '/type/track-car.svg', + showVoyageursText: false + }, + TRAMTRAIN: { + id: 'tramtrain', + typeLabel: 'Tram-train', + quaiLabel: 'Tram-train', + operator: 'TER', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-tramtrain.svg', + quaiLogo: '/type/quais/logo-tramtrain-color.svg', + showVoyageursText: false + }, + MOBIGO: { + id: 'mobigo', + typeLabel: 'MOBIGO', + quaiLabel: 'Mobigo', + operator: 'RĂ©gion Bourgogne-Franche-ComtĂ©', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-mobigo.svg', + quaiLogo: '/type/quais/logo-mobigo-color.svg', + showVoyageursText: false + }, + ZOU: { + id: 'zou', + typeLabel: 'ZOU !', + quaiLabel: 'ZOU!', + operator: 'RĂ©gion Sud / PACA', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-zou.svg', + quaiLogo: '/type/quais/logo-zou-color.svg', + showVoyageursText: false + }, + FLUO: { + id: 'fluo', + typeLabel: 'FLUO', + quaiLabel: 'TER Fluo', + operator: 'RĂ©gion Grand Est', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-fluo.svg', + quaiLogo: '/type/quais/logo-fluo-color.svg', + showVoyageursText: false + }, + ALEOP: { + id: 'aleop', + typeLabel: 'ALÉOP', + quaiLabel: 'AlĂ©op', + operator: 'RĂ©gion Pays de la Loire', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-aleop.svg', + quaiLogo: '/type/quais/logo-aleop-color.svg', + showVoyageursText: false + }, + BREIZHGO: { + id: 'breizhgo', + typeLabel: 'BREIZHGO', + quaiLabel: 'BreizhGo', + operator: 'RĂ©gion Bretagne', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-breizhgo.svg', + quaiLogo: '/type/quais/logo-breizhgo-color.svg', + showVoyageursText: false + }, + NOMAD: { + id: 'nomad', + typeLabel: 'NOMAD', + quaiLabel: 'Nomad', + operator: 'RĂ©gion Normandie', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-nomad.svg', + quaiLogo: '/type/quais/logo-nomad-color.svg', + showVoyageursText: false + }, + REMI: { + id: 'remi', + typeLabel: 'RÉMI', + quaiLabel: 'RĂ©mi', + operator: 'RĂ©gion Centre-Val de Loire', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-remi.svg', + quaiLogo: '/type/quais/logo-remi-color.svg', + showVoyageursText: false + }, + LIO: { + id: 'lio', + typeLabel: 'LIÒ', + quaiLabel: 'liO', + operator: 'RĂ©gion Occitanie', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-lio.svg', + quaiLogo: '/type/quais/logo-lio-color.svg', + showVoyageursText: false + }, + HAUTS_DE_FRANCE: { + id: 'hautsdefrance', + typeLabel: 'HAUTS-DE-FRANCE', + quaiLabel: 'Hauts-de-France', + operator: 'RĂ©gion Hauts-de-France', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-hautsdefrance.svg', + quaiLogo: '/type/quais/logo-hautsdefrance-color.svg', + showVoyageursText: false + }, + AUVERGNE_RHONE_ALPES: { + id: 'auvergerhonealpes', + typeLabel: 'AUVERGNE-RHÔNE-ALPES', + quaiLabel: 'Auvergne-RhĂŽne-Alpes', + operator: 'RĂ©gion Auvergne-RhĂŽne-Alpes', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-auvergerhonealpes.svg', + quaiLogo: '/type/quais/logo-auvergerhonealpes-color.svg', + showVoyageursText: false + }, + NOUVELLE_AQUITAINE: { + id: 'nouvelleaquitaine', + typeLabel: 'NOUVELLE-AQUITAINE', + quaiLabel: 'Nouvelle-Aquitaine', + operator: 'RĂ©gion Nouvelle-Aquitaine', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-teraquitaine.svg', + quaiLogo: '/type/quais/logo-nouvelleaquitaine-color.svg', + showVoyageursText: false + }, + TER_GENERIC: { + id: 'ter', + typeLabel: 'TER', + quaiLabel: 'TER', + operator: 'SNCF Voyageurs', + category: 'TER', + isTgv: false, + hallLogo: '/type/logo-ter.svg', + quaiLogo: '/type/quais/logo-ter-color.svg', + showVoyageursText: false + }, + SBB: { + id: 'sbb', + typeLabel: 'SBB', + quaiLabel: 'SBB', + operator: 'CFF / SBB', + category: 'INTERNATIONAL', + isTgv: false, + hallLogo: '/type/logo-sbb.svg', + quaiLogo: '/type/quais/logo-sbb-color.svg', + showVoyageursText: false + }, + SNCB: { + id: 'sncb', + typeLabel: 'SNCB', + quaiLabel: 'SNCB', + operator: 'SNCB / NMBS', + category: 'INTERNATIONAL', + isTgv: false, + hallLogo: '/type/logo-sncb.svg', + quaiLogo: '/type/quais/logo-sncb-color.svg', + showVoyageursText: false + }, + CFL: { + id: 'cfl', + typeLabel: 'CFL', + quaiLabel: 'CFL', + operator: 'CFL', + category: 'INTERNATIONAL', + isTgv: false, + hallLogo: '/type/logo-cfl.svg', + quaiLogo: '/type/quais/logo-cfl-color.svg', + showVoyageursText: false + }, + SNCF_GENERIC: { + id: 'sncf', + typeLabel: 'SNCF', + quaiLabel: 'SNCF', + operator: 'SNCF Voyageurs', + category: 'SNCF', + isTgv: false, + hallLogo: '/type/logo-sncf.svg', + quaiLogo: '/type/quais/logo-sncf-color.svg', + showVoyageursText: true + } +}; + +/** + * Plan de NumĂ©rotation officiel SNCF (SĂ©ries rĂ©gionales TER) + * Chaque autoritĂ© organisatrice rĂ©gionale dispose de tranches numĂ©riques rĂ©servĂ©es. + */ +function getRegionByTrainNumber(numStr) { + const clean = String(numStr || '').replace(/\D/g, ''); + if (!clean || clean.length < 3) return null; + + // 1. Occitanie (liO) : 869xxx, 870xxx, 871xxx, 872xxx, 876xxx, 877xxx (et 5 chiffres : 869xx, 870xx, 876xx, 877xx) + if ( + clean.startsWith('869') || + clean.startsWith('870') || + clean.startsWith('871') || + clean.startsWith('872') || + clean.startsWith('876') || + clean.startsWith('877') + ) { + return BRANDS.LIO; + } + + // 2. Centre-Val de Loire (RĂ©mi) : 860xxx, 861xxx, 862xxx, 863xxx (et 860xx Ă  863xx) + if ( + clean.startsWith('860') || + clean.startsWith('861') || + clean.startsWith('862') || + clean.startsWith('863') + ) { + return BRANDS.REMI; + } + + // 3. Nouvelle-Aquitaine : 864xxx, 865xxx, 866xxx, 867xxx, 868xxx (et 864xx Ă  868xx) + if ( + clean.startsWith('864') || + clean.startsWith('865') || + clean.startsWith('866') || + clean.startsWith('867') || + clean.startsWith('868') + ) { + return BRANDS.NOUVELLE_AQUITAINE; + } + + // 4. Grand Est (Fluo) : 83xxxx (et 83xxx) + if (clean.startsWith('83')) { + return BRANDS.FLUO; + } + + // 5. Bourgogne-Franche-ComtĂ© (Mobigo) : 89xxxx, 840xxx, 841xxx (et 89xxx, 840xx, 841xx) + if ( + clean.startsWith('89') || + clean.startsWith('840') || + clean.startsWith('841') + ) { + return BRANDS.MOBIGO; + } + + // 6. Hauts-de-France : 842xxx Ă  849xxx (et 842xx Ă  849xx) + if (['842', '843', '844', '845', '846', '847', '848', '849'].some(p => clean.startsWith(p))) { + return BRANDS.HAUTS_DE_FRANCE; + } + + // 7. Normandie (Nomad) : 852xxx, 853xxx (et 852xx, 853xx) + if (clean.startsWith('852') || clean.startsWith('853')) { + return BRANDS.NOMAD; + } + + // 8. Bretagne (BreizhGo) : 854xxx, 855xxx, 856xxx (et 854xx Ă  856xx) + if (clean.startsWith('854') || clean.startsWith('855') || clean.startsWith('856')) { + return BRANDS.BREIZHGO; + } + + // 9. Pays de la Loire (AlĂ©op) : 857xxx, 858xxx, 859xxx (et 857xx Ă  859xx) + if (clean.startsWith('857') || clean.startsWith('858') || clean.startsWith('859')) { + return BRANDS.ALEOP; + } + + // 10. Auvergne-RhĂŽne-Alpes : 873xxx Ă  875xxx, 883xxx, 884xxx, 889xxx + if (['873', '874', '875', '883', '884', '889'].some(p => clean.startsWith(p))) { + return BRANDS.AUVERGNE_RHONE_ALPES; + } + + // 11. PACA / Sud (Zou !) : 880xxx, 881xxx, 882xxx, 885xxx, 886xxx, 887xxx, 888xxx + if (['880', '881', '882', '885', '886', '887', '888'].some(p => clean.startsWith(p))) { + return BRANDS.ZOU; + } + + return null; +} + +/** + * Gares de rĂ©fĂ©rence par rĂ©gion ferroviaire pour analyse de parcours + * (Seuls des noms de villes ou gares rĂ©elles sans mots gĂ©nĂ©riques comme 'centre' ou 'est') + */ +const REGIONAL_STATIONS = { + lio: [ + 'perpignan', 'narbonne', 'toulouse', 'nimes', 'beziers', 'sete', 'montpellier', + 'cerbere', 'portbou', 'villefranche', 'carcassonne', 'agde', 'elne', 'argeles', + 'collioure', 'port vendres', 'banyuls', 'rodez', 'albi', 'tarbes', 'montauban', + 'castres', 'lourdes', 'mende', 'foix', 'latour de carol', 'ax les thermes', + 'castelnaudary', 'frontignan', 'port la nouvelle', 'leucate', 'salses', 'rivesaltes' + ], + remi: [ + 'orleans', 'tours', 'bourges', 'blois', 'chartres', 'chateauroux', 'vierzon', + 'vendome', 'saint pierre des corps', 'amboise', 'nogent le rotrou', 'salbris', + 'chinon', 'loches', 'issoudun', 'romorantin', 'montargis', 'gien' + ], + mobigo: [ + 'dijon', 'besancon', 'belfort', 'auxerre', 'nevers', 'chalon sur saone', + 'montbeliard', 'macon', 'sens', 'dole', 'creusot', 'paray le monial', + 'beaune', 'lons le saunier', 'pontarlier', 'vesoul' + ], + zou: [ + 'marseille', 'nice', 'toulon', 'cannes', 'antibes', 'avignon', 'grasse', + 'aubagne', 'miramas', 'arles', 'hyeres', 'menton', 'cagnes sur mer', + 'frejus', 'saint raphael', 'la ciotat', 'gap', 'briancon', 'manosque' + ], + fluo: [ + 'strasbourg', 'nancy', 'metz', 'mulhouse', 'colmar', 'reims', 'troyes', + 'epinal', 'thionville', 'charleville', 'saint die', 'haguenau', + 'sarreguemines', 'bar le duc', 'luneville', 'sedan', 'forbach', 'selestat' + ], + aleop: [ + 'nantes', 'angers', 'le mans', 'saint nazaire', 'cholet', 'la roche sur yon', + 'saumur', 'laval', 'les sables d olonne', 'ancenis', 'clisson', 'chateaubriant' + ], + breizhgo: [ + 'rennes', 'brest', 'quimper', 'saint malo', 'vannes', 'lorient', + 'saint brieuc', 'morlaix', 'lannion', 'vitre', 'redon', 'guingamp' + ], + nomad: [ + 'rouen', 'caen', 'le havre', 'cherbourg', 'evreux', 'trouville deauville', + 'dieppe', 'lisieux', 'granville', 'alencon', 'bernay', 'bayeux' + ], + hautsdefrance: [ + 'lille', 'amiens', 'calais', 'dunkerque', 'valenciennes', 'arras', + 'boulogne sur mer', 'douai', 'creil', 'saint quentin', 'lens', 'bethune', + 'compiegne', 'cambrai', 'tourcoing', 'roubaix' + ], + auvergerhonealpes: [ + 'lyon', 'grenoble', 'chambery', 'annecy', 'valence', 'clermont ferrand', + 'saint etienne', 'bourg en bresse', 'roanne', 'vichy', 'aix les bains', + 'annemasse', 'amberieu', 'riom', 'moulins' + ], + nouvelleaquitaine: [ + 'bordeaux', 'limoges', 'poitiers', 'la rochelle', 'bayonne', 'pau', + 'angouleme', 'perigueux', 'agen', 'niort', 'brive la gaillarde', 'dax', + 'hendaye', 'saintes', 'arcachon' + ] +}; + +/** + * Calcule la rĂ©gion la plus probable selon les arrĂȘts desservis et le terminus + */ +function detectRegionByRouteScore(routeText) { + if (!routeText) return null; + const normalized = ` ${normalize(routeText)} `; + + let bestRegion = null; + let maxScore = 0; + + for (const [regionKey, stations] of Object.entries(REGIONAL_STATIONS)) { + let score = 0; + for (const station of stations) { + const stationNorm = ` ${normalize(station)} `; + if (normalized.includes(stationNorm)) { + score++; + } + } + if (score > maxScore) { + maxScore = score; + bestRegion = regionKey; + } + } + + if (maxScore > 0 && bestRegion) { + switch (bestRegion) { + case 'lio': return BRANDS.LIO; + case 'remi': return BRANDS.REMI; + case 'mobigo': return BRANDS.MOBIGO; + case 'zou': return BRANDS.ZOU; + case 'fluo': return BRANDS.FLUO; + case 'aleop': return BRANDS.ALEOP; + case 'breizhgo': return BRANDS.BREIZHGO; + case 'nomad': return BRANDS.NOMAD; + case 'hautsdefrance': return BRANDS.HAUTS_DE_FRANCE; + case 'auvergerhonealpes': return BRANDS.AUVERGNE_RHONE_ALPES; + case 'nouvelleaquitaine': return BRANDS.NOUVELLE_AQUITAINE; + default: return null; + } + } + + return null; +} + +/** + * Fonction centrale de classification d'un train Ă  partir de ses donnĂ©es SNCF + * @param {Object|string} trainOrType - Objet horaire ou type brut + * @param {string} [operatorParam] - OpĂ©rateur ou transporteur optionnel + * @param {string} [numParam] - NumĂ©ro de train optionnel + * @returns {Object} RĂ©sultat complet de classification + */ +export function classifyTrain(trainOrType, operatorParam = '', numParam = '') { + let horaire = null; + let rawType = ''; + let opRaw = operatorParam || ''; + let numRaw = numParam || ''; + + if (trainOrType && typeof trainOrType === 'object') { + horaire = trainOrType; + rawType = horaire.type_train || horaire.type || horaire.train_type || horaire.commercial_mode || horaire.mode || ''; + opRaw = horaire.operator || horaire.op || horaire.transporteur || horaire.carrier || opRaw; + numRaw = horaire.numero_train || horaire.numero || horaire.number || horaire.num || horaire.trip_short_name || numRaw; + } else { + rawType = String(trainOrType || ''); + } + + const numTrain = extractTrainDigits(numRaw); + const numInt = parseInt(numTrain, 10) || 0; + const tripId = horaire ? String(horaire.trip_id || '') : ''; + const isSubstitution = Boolean(horaire && (horaire.is_substitution || horaire.is_sub)); + + const origName = horaire ? (horaire.gare_depart_nom || horaire.orig || horaire.origin || '') : ''; + const destName = horaire ? (horaire.gare_arrivee_nom || horaire.dest || horaire.destination || '') : ''; + const stopsText = Array.isArray(horaire?.stops) ? horaire.stops.map(s => (s.nom || s.gare_nom || s.name || '')).join(' ') : ''; + const fullRouteText = `${origName} ${destName} ${stopsText}`; + const routeNormalized = normalize(fullRouteText); + + const typeNorm = normalize(rawType); + const opNorm = normalize(opRaw); + const combined = `${typeNorm} ${opNorm}`.trim(); + + // Helper pour crĂ©er le rĂ©sultat enrichi + const buildResult = (brand, customProps = {}) => ({ + ...brand, + numTrain, + rawType, + rawOperator: opRaw, + ...customProps + }); + + // ========================================================================= + // 1. SERVICES AUTOCAR / CAR TER / BUS DE SUBSTITUTION + // ========================================================================= + if ( + isSubstitution || + isCarTrainNumber(numTrain) || + combined.includes('autocar') || + combined.includes('carter') || + combined.includes('substitution') || + combined === 'car' || + combined === 'bus' + ) { + return buildResult(BRANDS.AUTOCAR); + } + + // ========================================================================= + // 2. TGV LYRIA (Paris - Suisse : GenĂšve, Lausanne, Zurich, BĂąle) + // ========================================================================= + if ( + combined.includes('lyria') || + tripId.includes(':LYR:FR') || + (numInt >= 9200 && numInt <= 9299) || + (numInt >= 9750 && numInt <= 9799) + ) { + return buildResult(BRANDS.LYRIA); + } + + // ========================================================================= + // 3. OUIGO TRAIN CLASSIQUE (4000-4399 ou 8040-8099, Paris Austerlitz/Bercy) + // ========================================================================= + const isOuigoClassique = ( + combined.includes('ouigoclassique') || + combined.includes('trainclassique') || + combined.includes('classique') || + (combined.includes('ouigo') && ((numInt >= 4000 && numInt <= 4399) || (numInt >= 8040 && numInt <= 8099))) || + (tripId.includes(':OGO:FR') && ((numInt >= 4000 && numInt <= 4399) || (numInt >= 8040 && numInt <= 8099))) + ); + if (isOuigoClassique) { + return buildResult(BRANDS.OUIGO_CLASSIQUE); + } + + // ========================================================================= + // 4. OUIGO (GRANDE VITESSE) + // ========================================================================= + if ( + combined.includes('ouigo') || + tripId.includes(':OGO:FR') || + (numInt >= 7600 && numInt <= 7999) + ) { + return buildResult(BRANDS.OUIGO_GV); + } + + // ========================================================================= + // 5. INTERCITÉS DE NUIT (LunĂ©a) + // ========================================================================= + if ( + combined.includes('denuit') || + combined.includes('nuit') || + combined.includes('lunea') || + tripId.includes(':ICN:FR') || + (numInt >= 3690 && numInt <= 3799) || + (numInt >= 3950 && numInt <= 3999) || + (numInt >= 5700 && numInt <= 5799) + ) { + return buildResult(BRANDS.INTERCITES_NUIT); + } + + // ========================================================================= + // 6. INTERCITÉS (Trains d'Équilibre du Territoire de jour) + // ========================================================================= + if ( + combined.includes('intercites') || + combined === 'ic' || + combined.startsWith('ic ') || + tripId.includes(':IC:FR') || + (numInt >= 3000 && numInt <= 3949) || + (numInt >= 4400 && numInt <= 4999) + ) { + return buildResult(BRANDS.INTERCITES); + } + + // ========================================================================= + // 7. EUROSTAR & THALYS + // ========================================================================= + const isEurostarOrThalys = ( + combined.includes('eurostar') || + combined.includes('thalys') || + (tripId.includes(':TRN:FR') && (routeNormalized.includes('bruxelles') || routeNormalized.includes('london') || routeNormalized.includes('amsterdam') || routeNormalized.includes('koln'))) || + ((numInt >= 60 && numInt <= 99) && (routeNormalized.includes('bruxelles') || routeNormalized.includes('nord'))) || + (numInt >= 9000 && numInt <= 9499 && (routeNormalized.includes('bruxelles') || routeNormalized.includes('london') || routeNormalized.includes('amsterdam'))) + ); + if (isEurostarOrThalys) { + if (combined.includes('thalys')) { + return buildResult(BRANDS.THALYS); + } + return buildResult(BRANDS.EUROSTAR); + } + + // ========================================================================= + // 8. FRECCIAROSSA / TRENITALIA (Paris Lyon - Lyon - Milan) + // ========================================================================= + if ( + combined.includes('frecciarossa') || + combined.includes('freccia') || + combined.includes('trenitalia') || + (numInt >= 6640 && numInt <= 6670) + ) { + return buildResult(BRANDS.FRECCIAROSSA); + } + + // ========================================================================= + // 9. RENFE AVE (Marseille / Lyon - Madrid / Barcelone) + // ========================================================================= + if ( + combined.includes('renfe') || + combined.includes('ave') || + [9724, 9725, 9731, 9732].includes(numInt) + ) { + return buildResult(BRANDS.RENFE_AVE); + } + + // ========================================================================= + // 10. ICE / DB (Paris Est - Allemagne) + // ========================================================================= + if ( + combined.includes('ice') || + combined === 'db' || + tripId.includes(':ICE:FR') || + (numInt >= 9550 && numInt <= 9599) + ) { + return buildResult(BRANDS.ICE); + } + + // ========================================================================= + // 11. TGV INOUI & TGV GÉNÉRIQUE + // ========================================================================= + if ( + combined.includes('inoui') || + combined.includes('tgvinoui') || + tripId.includes(':OUI:FR') || + combined.includes('tgv') + ) { + return buildResult(BRANDS.INOUi); + } + + // ========================================================================= + // 12. TRAM-TRAIN + // ========================================================================= + if ( + combined.includes('tramtrain') || + combined.includes('tram-train') || + tripId.includes(':TT:FR') + ) { + return buildResult(BRANDS.TRAMTRAIN); + } + + // ========================================================================= + // 13. TERGV + // ========================================================================= + if (combined.includes('tergv') || combined.includes('ter-gv')) { + return buildResult(BRANDS.HAUTS_DE_FRANCE, { typeLabel: 'TERGV', quaiLabel: 'TERGV' }); + } + + // ========================================================================= + // 14. OPÉRATEURS SUISSE, BELGE, LUXEMBOURGEOIS + // ========================================================================= + if (combined.includes('sbb') || combined.includes('cff') || combined.includes('ffs')) { + return buildResult(BRANDS.SBB); + } + if (combined.includes('sncb') || combined.includes('nmbs')) { + return buildResult(BRANDS.SNCB); + } + if (combined.includes('cfl')) { + return buildResult(BRANDS.CFL); + } + + // ========================================================================= + // 15. TER RÉGIONAUX - DÉTECTION ROBUSTE PAR PALIER + // ========================================================================= + + // Palier A : Mots-clĂ©s rĂ©gionaux explicites dans l'opĂ©rateur ou type + if (combined.includes('lio') || combined.includes('occitanie') || combined.includes('languedoc') || combined.includes('midipyrenees')) { + return buildResult(BRANDS.LIO); + } + if (combined.includes('mobigo') || combined.includes('bourgogne') || combined.includes('franchecomte')) { + return buildResult(BRANDS.MOBIGO); + } + if (combined.includes('zou') || combined.includes('paca') || combined.includes('ler')) { + return buildResult(BRANDS.ZOU); + } + if (combined.includes('fluo') || combined.includes('grandest') || combined.includes('alsace') || combined.includes('lorraine') || combined.includes('metrolor')) { + return buildResult(BRANDS.FLUO); + } + if (combined.includes('aleop') || combined.includes('paysdelaloire')) { + return buildResult(BRANDS.ALEOP); + } + if (combined.includes('breizh') || combined.includes('bretagne')) { + return buildResult(BRANDS.BREIZHGO); + } + if (combined.includes('nomad') || combined.includes('normandie')) { + return buildResult(BRANDS.NOMAD); + } + if (combined.includes('remi') || combined.includes('centrevaldeloire')) { + return buildResult(BRANDS.REMI); + } + if (combined.includes('hautsdefrance') || combined.includes('nordpasdecalais') || combined.includes('picardie')) { + return buildResult(BRANDS.HAUTS_DE_FRANCE); + } + if (combined.includes('auvergerhonealpes') || combined.includes('rhonealpes') || combined.includes('auvergne')) { + return buildResult(BRANDS.AUVERGNE_RHONE_ALPES); + } + if (combined.includes('nouvelleaquitaine') || combined.includes('aquitaine') || combined.includes('poitoucharentes') || combined.includes('limousin')) { + return buildResult(BRANDS.NOUVELLE_AQUITAINE); + } + + // Palier B : Plan de numĂ©rotation SNCF (Strict & DĂ©terministe) + const regionByNum = getRegionByTrainNumber(numTrain); + if (regionByNum) { + return buildResult(regionByNum); + } + + // Palier C : Analyse gĂ©ographique par score d'arrĂȘts (si train hors numĂ©rotation standard) + const regionByRoute = detectRegionByRouteScore(fullRouteText); + if (regionByRoute) { + return buildResult(regionByRoute); + } + + // ========================================================================= + // 16. TER STANDARD (Fallback) + // ========================================================================= + if (combined.includes('ter')) { + return buildResult(BRANDS.TER_GENERIC); + } + + // ========================================================================= + // 17. SNCF VOYAGEURS (Fallback par dĂ©faut) + // ========================================================================= + return buildResult(BRANDS.SNCF_GENERIC, { + typeLabel: rawType || 'SNCF', + quaiLabel: rawType || 'SNCF' + }); +} + +/** + * Interface pour les afficheurs de Quai classiques (ClassiquesDepartsQuais & ClassiquesArriveesQuais) + * @param {Object|string} trainTypeOrHoraire + * @param {string} [operator] + * @param {boolean} [isDeparture=true] + * @param {string} [numTrainOverride] + * @returns {{ logo: string, label: string, showVoyageursText: boolean, isTgv: boolean, classification: Object }} + */ +export function getTrainBrand(trainTypeOrHoraire = '', operator = '', isDeparture = true, numTrainOverride = '') { + const c = classifyTrain(trainTypeOrHoraire, operator, numTrainOverride); + + // Pour les trains sans marque spĂ©cifique : + if (c.id === 'sncf' || c.id === 'ter') { + if (isDeparture) { + return { + logo: BRANDS.SNCF_GENERIC.quaiLogo, + label: c.quaiLabel || 'SNCF', + showVoyageursText: true, + isTgv: Boolean(c.isTgv), + classification: c + }; + } + return { + logo: BRANDS.TER_GENERIC.quaiLogo, + label: c.quaiLabel || 'TER', + showVoyageursText: false, + isTgv: Boolean(c.isTgv), + classification: c + }; + } + + return { + logo: c.quaiLogo, + label: c.quaiLabel, + showVoyageursText: Boolean(c.showVoyageursText), + isTgv: Boolean(c.isTgv), + classification: c + }; +} + +/** + * Interface pour les afficheurs Hall classiques (ClassiquesDeparts & ClassiquesArrivees) + * @param {Object} horaire + * @returns {{ logoSrc: string, typeLabel: string, numTrain: string, isTgv: boolean, brand: Object, classification: Object }} + */ +export function getTrainLogoAndMeta(horaire) { + if (!horaire) { + return { + logoSrc: BRANDS.TER_GENERIC.hallLogo, + typeLabel: 'TER', + numTrain: '', + isTgv: false, + brand: getTrainBrand(), + classification: BRANDS.TER_GENERIC + }; + } + + const c = classifyTrain(horaire); + const brand = getTrainBrand(horaire, '', true); + + return { + logoSrc: c.hallLogo, + typeLabel: c.typeLabel, + numTrain: c.numTrain, + isTgv: Boolean(c.isTgv), + brand, + classification: c + }; +} diff --git a/src/utils/trainLogoHelper.js b/src/utils/trainLogoHelper.js index 88211a0..d500652 100644 --- a/src/utils/trainLogoHelper.js +++ b/src/utils/trainLogoHelper.js @@ -180,7 +180,7 @@ export function getTrainLogoAndMeta(horaire) { } else if (opNorm.includes('breizhgo')) { typeLabel = 'BREIZHGO'; } else if (opNorm.includes('hautsdefrance')) { - typeLabel = 'HAUTS-DE-FRANCE'; + typeLabel = 'TER HDF'; } else if (opNorm.includes('lio')) { typeLabel = 'LIÒ'; } else if (opNorm.includes('zou')) { @@ -199,14 +199,48 @@ export function getTrainLogoAndMeta(horaire) { typeLabel = 'TER'; } - // SpĂ©cificitĂ© rĂ©gionale Bourgogne-Franche-ComtĂ© : TER commençant par 89xxxx ou 84xxxx -> MOBIGO - if (typeLabel === 'TER' && (numTrain.startsWith('89') || numTrain.startsWith('84'))) { - typeLabel = 'MOBIGO'; - if (!matchedLogo || matchedLogo.file === 'logo-ter.svg' || matchedLogo.file === 'logo-sncf.svg') { - matchedLogo = logoLookup.get('mobigo') || matchedLogo; + // Application du plan de numĂ©rotation dĂ©terministe TER + if ((typeLabel === 'TER' || typeLabel === 'AUTOCAR' || typeLabel === 'Car TER') && numTrain) { + const cleanNum = numTrain.replace(/\D/g, ''); + let matchedBrandSlug = null; + let matchedBrandLabel = null; + + if (cleanNum.length >= 4) { + const p3 = parseInt(cleanNum.substring(0, 3), 10); + const p4 = parseInt(cleanNum.substring(0, 4), 10); + + if ((p3 >= 830 && p3 <= 840) || (p3 >= 160 && p3 <= 162)) { + matchedBrandSlug = 'fluo'; matchedBrandLabel = 'FLUO'; + } else if ((p3 >= 841 && p3 <= 849) || p3 === 163 || p3 === 164) { + matchedBrandSlug = 'hautsdefrance'; matchedBrandLabel = 'TER HDF'; + } else if ((p3 >= 850 && p3 <= 853) || p3 === 165) { + matchedBrandSlug = 'nomad'; matchedBrandLabel = 'NOMAD'; + } else if ((p3 >= 854 && p3 <= 856) || (p4 >= 1660 && p4 <= 1664)) { + matchedBrandSlug = 'breizhgo'; matchedBrandLabel = 'BREIZHGO'; + } else if ((p3 >= 857 && p3 <= 859) || (p4 >= 1665 && p4 <= 1674)) { + matchedBrandSlug = 'aleop'; matchedBrandLabel = 'ALÉOP'; + } else if ((p3 >= 860 && p3 <= 862) || (p4 >= 1675 && p4 <= 1684)) { + matchedBrandSlug = 'remi'; matchedBrandLabel = 'RÉMI'; + } else if ((p3 >= 863 && p3 <= 869) || (p4 >= 1685 && p4 <= 1709)) { + matchedBrandSlug = 'teraquitaine'; matchedBrandLabel = 'TER NOUVELLE-AQUITAINE'; + } else if ((p3 >= 870 && p3 <= 872) || (p3 >= 876 && p3 <= 878) || (p4 >= 1710 && p4 <= 1719) || (p4 >= 1730 && p4 <= 1734)) { + matchedBrandSlug = 'lio'; matchedBrandLabel = 'liO'; + } else if ((p3 >= 873 && p3 <= 875) || (p3 >= 882 && p3 <= 890) || (p4 >= 1720 && p4 <= 1729) || (p4 >= 1750 && p4 <= 1774) || (p4 >= 1790 && p4 <= 1799)) { + matchedBrandSlug = 'ter-rhone-alpes'; matchedBrandLabel = 'TER AUVERGNE-RHÔNE-ALPES'; + } else if ((p3 >= 879 && p3 <= 881) || (p4 >= 1735 && p4 <= 1749)) { + matchedBrandSlug = 'zou'; matchedBrandLabel = 'ZOU !'; + } else if ((p3 >= 891 && p3 <= 895) || (p4 >= 1775 && p4 <= 1789)) { + matchedBrandSlug = 'mobigo'; matchedBrandLabel = 'MOBIGO'; + } + + if (matchedBrandSlug && matchedBrandLabel) { + typeLabel = matchedBrandLabel; + if (!matchedLogo || matchedLogo.file === 'logo-ter.svg' || matchedLogo.file === 'logo-sncf.svg') { + matchedLogo = logoLookup.get(matchedBrandSlug) || matchedLogo; + } + } } } - const logoFile = (matchedLogo && matchedLogo.file) || 'logo-sncf.svg'; const logoSrc = `/type/${logoFile}`; diff --git a/test_date.js b/test_date.js deleted file mode 100644 index c9a9f3d..0000000 --- a/test_date.js +++ /dev/null @@ -1,12 +0,0 @@ -const horaire_date = "2026-06-14"; -const baseDate = new Date(horaire_date); -console.log("baseDate before setHours:", baseDate.toString()); -const sched = 330; // 05:30 -baseDate.setHours(Math.floor(sched / 60), sched % 60, 0, 0); -console.log("baseDate after setHours:", baseDate.toString()); - -const currentTime = new Date("2026-06-14T04:49:15+02:00"); -console.log("currentTime:", currentTime.toString()); - -const diffMinutes = (baseDate.getTime() - currentTime.getTime()) / 60000; -console.log("diffMinutes:", diffMinutes); diff --git a/test_db.js b/test_db.js deleted file mode 100644 index 650d419..0000000 --- a/test_db.js +++ /dev/null @@ -1,17 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); - -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const gareDisplay = "Ouges"; - const flexNeedle = `%${String(gareDisplay).replace(/[-_+\s]+/g, '%')}%`; - console.log("flexNeedle:", flexNeedle); - const { data: gdata, error: gerr } = await supabase.from('gares').select('id,type').ilike('nom', flexNeedle).limit(1); - console.log("gdata:", gdata); - let fetchedType = null; - if (!gerr && gdata && gdata.length) fetchedType = gdata[0] && gdata[0].type; - console.log("fetchedType:", fetchedType); -} - -run(); diff --git a/test_db2.js b/test_db2.js deleted file mode 100644 index e27b482..0000000 --- a/test_db2.js +++ /dev/null @@ -1,10 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const { data } = await supabase.from('gares').select('id,nom,type').ilike('nom', '%Ouges%'); - console.log("Matched gares:", data); -} - -run(); diff --git a/test_db3.js b/test_db3.js deleted file mode 100644 index 7c89d5c..0000000 --- a/test_db3.js +++ /dev/null @@ -1,12 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const { data: horaires, error: hErr } = await supabase.from('horaires').select('*').limit(2); - console.log("Horaires sample:", horaires); - const { data: perturbs, error: pErr } = await supabase.from('perturbations').select('*').limit(2); - console.log("Perturbations sample:", perturbs); -} - -run(); diff --git a/test_horaire.js b/test_horaire.js deleted file mode 100644 index f21bbe0..0000000 --- a/test_horaire.js +++ /dev/null @@ -1,23 +0,0 @@ -const horaires = [{ heure_depart: '21:00', numero_train: '123' }]; -const isArrival = false; -const currentTime = new Date(); -currentTime.setHours(20, 55, 0, 0); - -for (const train of horaires) { - const numTrain = train.num_train || train.numero_train || train.numero; - const timeStr = isArrival - ? (train.heure_arrivee || train.arrival_time || train.time || train.heure) - : (train.heure_depart || train.departure_time || train.time || train.heure); - - let trainTime; - if (typeof timeStr === 'string' && timeStr.length <= 5 && timeStr.includes(':')) { - const [hh, mm] = timeStr.split(':'); - trainTime = new Date(currentTime); - trainTime.setHours(parseInt(hh, 10), parseInt(mm, 10), 0, 0); - } else { - trainTime = new Date(timeStr); - } - const diffMs = trainTime.getTime() - currentTime.getTime(); - const diffMins = Math.floor(diffMs / 60000); - console.log(`numTrain=${numTrain}, timeStr=${timeStr}, diffMins=${diffMins}`); -} diff --git a/test_logic.js b/test_logic.js deleted file mode 100644 index 340cde9..0000000 --- a/test_logic.js +++ /dev/null @@ -1,85 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -const normalizeName = (s) => { - if (!s) return ''; - try { - const t = String(s).normalize('NFD').replace(/\p{M}/gu, '').toLowerCase().trim(); - return t.replace(/[-_]+/g, ' ').replace(/\s+/g, ' '); - } catch (e) { - return String(s).toLowerCase().trim(); - } -}; - -const parseTimeHM = (v) => { - if (!v) return null; - const m = String(v).match(/(\d{1,2}):(\d{2})/); - if (m) return parseInt(m[1], 10)*60 + parseInt(m[2], 10); - return null; -}; - -async function run() { - const gareDisplay = "Ouges"; - const flexNeedle = `%${String(gareDisplay).replace(/[-_+\s]+/g, '%')}%`; - const { data: gdata } = await supabase.from('gares').select('id,type').ilike('nom', flexNeedle).limit(1); - const stationType = gdata && gdata.length ? gdata[0].type.toLowerCase() : null; - console.log("stationType:", stationType); - - const thresholdForStationType = (typeGare) => { - const tG = String(typeGare || '').trim().toLowerCase(); - if (tG === 'interurbaine' || tG.includes('interurb')) return 360; - if (tG === 'ville' || tG.includes('ville')) return 30; - return 30; - }; - const threshold = thresholdForStationType(stationType); - console.log("threshold:", threshold); - - const { data: allData } = await supabase.from('horaires').select('*').limit(200); - const horaires = allData.filter(h => JSON.stringify(h).includes("Ouges")); - const h = horaires[0]; - - const stops = h.gares_desservies; - const targetStationName = gareDisplay; - - const stopForStation = (stops || []).find(s => { - const nm = s.gare_nom || s.nom || s.name || s.stop_name || ''; - const ns = normalizeName(nm || ''); - const target = normalizeName(targetStationName || ''); - return (targetStationName && (ns === target || ns.includes(target) || target.includes(ns))); - }) || null; - - h._displayPlatform = (stopForStation && stopForStation.quai_reel) || h.quai_reel || ''; - console.log("_displayPlatform:", h._displayPlatform); - - const platform = (h._displayPlatform || '').toString().trim(); - let showPlatform = Boolean(platform); - console.log("Boolean(platform):", showPlatform); - - if (showPlatform) { - let diffMinutes = null; - h._depHM = parseTimeHM(h.heure_depart); - const sched = h._depHM; - console.log("sched:", sched); - - const currentTime = new Date(); - if (sched !== null) { - const baseDate = new Date(); - baseDate.setHours(Math.floor(sched / 60), sched % 60, 0, 0); - const nowHM = currentTime.getHours() * 60 + currentTime.getMinutes(); - if (sched < 240 && nowHM > 1200) baseDate.setDate(baseDate.getDate() + 1); - else if (sched > 1200 && nowHM < 240) baseDate.setDate(baseDate.getDate() - 1); - diffMinutes = (baseDate.getTime() - currentTime.getTime()) / 60000; - } - console.log("diffMinutes:", diffMinutes); - - if (diffMinutes !== null) { - showPlatform = (diffMinutes <= threshold); - } else { - showPlatform = false; - } - } - console.log("FINAL showPlatform:", showPlatform); -} - -run(); diff --git a/test_ouges.js b/test_ouges.js deleted file mode 100644 index d3f531c..0000000 --- a/test_ouges.js +++ /dev/null @@ -1,10 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -const supabaseUrl = 'https://supabase-ferrovia.mr-patator.fr'; -const supabaseAnonKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzc1OTUxMjAxLCJleHAiOjE5MzM2MzEyMDF9.ZFuzZSHVOMX-poU1fhWTjZ2sn7LFz9NYATTwJknlSr0'; -const supabase = createClient(supabaseUrl, supabaseAnonKey); - -async function test() { - const { data: horaires, error } = await supabase.from('horaires').select('*').eq('numero_train', '891807'); - console.log(horaires[0]); -} -test(); diff --git a/test_ouges2.js b/test_ouges2.js deleted file mode 100644 index 5c500c4..0000000 --- a/test_ouges2.js +++ /dev/null @@ -1,16 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function test() { - const { data: horaires } = await supabase.from('horaires').select('*').limit(100); - const matched = horaires.filter(h => JSON.stringify(h).includes("Ouges")); - const h = matched[0]; - console.log("Train:", h.numero_train); - console.log("Stops:", h.gares_desservies.map(s => ({ - gare_nom: s.gare_nom, - quai_reel: s.quai_reel, - gare_id: s.gare_id - }))); -} -test(); diff --git a/test_pert.js b/test_pert.js deleted file mode 100644 index bd9cb4e..0000000 --- a/test_pert.js +++ /dev/null @@ -1,10 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const { data } = await supabase.from('perturbations').select('*'); - console.log("Perturbations:", data); -} - -run(); diff --git a/test_quai.js b/test_quai.js deleted file mode 100644 index 18b8ac6..0000000 --- a/test_quai.js +++ /dev/null @@ -1,10 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -const supabaseUrl = 'https://supabase-ferrovia.mr-patator.fr'; -const supabaseAnonKey = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzc1OTUxMjAxLCJleHAiOjE5MzM2MzEyMDF9.ZFuzZSHVOMX-poU1fhWTjZ2sn7LFz9NYATTwJknlSr0'; -const supabase = createClient(supabaseUrl, supabaseAnonKey); - -async function test() { - const { data: horaires, error } = await supabase.from('horaires_departs').select('*'); - console.log('Error:', error); -} -test(); diff --git a/test_realtime.js b/test_realtime.js deleted file mode 100644 index 90f1ad8..0000000 --- a/test_realtime.js +++ /dev/null @@ -1,24 +0,0 @@ -require('dotenv').config(); -const { createClient } = require('@supabase/supabase-js'); - -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -const channel = supabase - .channel('test_channel') - .on( - 'postgres_changes', - { event: 'INSERT', schema: 'public', table: 'annonces_diffusions' }, - (payload) => { - console.log('Realtime Event Received!', payload); - } - ) - .subscribe((status) => { - console.log('Subscription status:', status); - }); - -console.log('Waiting for events...'); -// We will kill this process manually or let it timeout -setTimeout(() => { - console.log('Timeout. Exiting.'); - process.exit(0); -}, 10000); diff --git a/test_sun.js b/test_sun.js deleted file mode 100644 index 1802965..0000000 --- a/test_sun.js +++ /dev/null @@ -1,10 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const { data } = await supabase.from('horaires').select('numero_train, heure_depart, circule_dimanche').ilike('gares_desservies', '%Ouges%'); - console.log("Trains Ouges on Sunday:", data); -} - -run(); diff --git a/test_sun2.js b/test_sun2.js deleted file mode 100644 index dda6f4e..0000000 --- a/test_sun2.js +++ /dev/null @@ -1,10 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const { data } = await supabase.from('horaires').select('numero_train, circule_dimanche').in('numero_train', ['891801', '891813']); - console.log("Trains Ouges on Sunday:", data); -} - -run(); diff --git a/test_sun3.js b/test_sun3.js deleted file mode 100644 index 99fcb2b..0000000 --- a/test_sun3.js +++ /dev/null @@ -1,11 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const { data: allData } = await supabase.from('horaires').select('numero_train, gare_depart_nom, gare_arrivee_nom, gares_desservies, circule_dimanche').limit(200); - const horaires = allData.filter(h => JSON.stringify(h).includes("Ouges") && h.circule_dimanche === true); - console.log("Trains Ouges on Sunday:", horaires.length); -} - -run(); diff --git a/test_sun4.js b/test_sun4.js deleted file mode 100644 index 49c4f7e..0000000 --- a/test_sun4.js +++ /dev/null @@ -1,14 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const { data: allData } = await supabase.from('horaires').select('numero_train, gare_depart_nom, gare_arrivee_nom, heure_depart, gares_desservies, circule_dimanche').limit(200); - const horaires = allData.filter(h => JSON.stringify(h).includes("Ouges") && h.circule_dimanche === true); - horaires.forEach(h => { - const stop = h.gares_desservies.find(s => s.gare_nom === 'Ouges'); - console.log(`Train ${h.numero_train} departs Ouges at ${stop ? stop.heure_depart : h.heure_depart}`); - }); -} - -run(); diff --git a/test_sun5.js b/test_sun5.js deleted file mode 100644 index 78bcc83..0000000 --- a/test_sun5.js +++ /dev/null @@ -1,14 +0,0 @@ -const { createClient } = require('@supabase/supabase-js'); -require('dotenv').config({ path: '/Users/mathisgrillot/IdeaProjects/afficheurs-ferrovia/.env' }); -const supabase = createClient(process.env.REACT_APP_SUPABASE_URL, process.env.REACT_APP_SUPABASE_ANON_KEY); - -async function run() { - const { data: allData } = await supabase.from('horaires').select('numero_train, gare_depart_nom, gare_arrivee_nom, heure_depart, gares_desservies, circule_dimanche').limit(200); - const horaires = allData.filter(h => JSON.stringify(h).includes("Ouges") && h.circule_dimanche === true); - horaires.forEach(h => { - const stop = h.gares_desservies.find(s => s.gare_nom === 'Ouges'); - console.log(`Train ${h.numero_train} departs Ouges at ${stop ? stop.heure_depart : h.heure_depart} with quai_reel: ${stop ? stop.quai_reel : 'none'}`); - }); -} - -run();