Lbasara commited on
Commit
223e856
·
1 Parent(s): 4bb6838

v0.2 Material template et généralisation usagers destination

Browse files
Files changed (3) hide show
  1. CHANGELOG.md +5 -2
  2. app.py +26 -22
  3. vrp.py +2 -3
CHANGELOG.md CHANGED
@@ -6,10 +6,13 @@ The format is based on [Keep a Changelog],
6
  and this project adheres to [Semantic Versioning].
7
 
8
 
9
- ## [0.1.1] - 2025-10-09
10
 
11
  ### Added
12
- Ce CHANGELOG.md...
 
 
 
13
 
14
  ### Changed
15
 
 
6
  and this project adheres to [Semantic Versioning].
7
 
8
 
9
+ ## [0.2.0] - 2025-10-10
10
 
11
  ### Added
12
+ * Ce CHANGELOG.md...
13
+ * Généralisation à "Destinations" et "Usagers"
14
+ * Ajout d'un thème "Material template"
15
+ * Versionnage
16
 
17
  ### Changed
18
 
app.py CHANGED
@@ -39,7 +39,7 @@ def cree_adresse(df, cols):
39
  return df[cols].astype(str).agg(' '.join, axis=1).str.strip()
40
 
41
 
42
- def geocode(adresse, return_dict=True):
43
  r=requests.get(url="https://data.geopf.fr/geocodage/search",
44
  params={"q": adresse})
45
  if not r.ok:
@@ -47,15 +47,17 @@ def geocode(adresse, return_dict=True):
47
  try:
48
  js=r.json()
49
  feature=js['features'][0]
50
- if return_dict:
 
 
51
  return {js["query"] : feature}
52
- return shapely.Point(feature['geometry']['coordinates'])
53
  except:
54
  return None
55
 
56
 
57
  def add_coo(adresse, details=False):
58
- feature=geocode(adresse)[adresse]
59
  coo=shapely.Point(feature['geometry']['coordinates'])
60
  score=feature['properties']['score']
61
  suggestion=feature['properties']['label']
@@ -73,13 +75,13 @@ class Tableur():
73
  self.dfeuilles={}
74
  self.df=pd.DataFrame()
75
  self.dmime={'csv': 'text/csv', 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}
76
- self.feuille=pn.widgets.Select(name='Feuille de calcul', disabled=True)
77
- self.ecole_col=pn.widgets.Select(name="Colonne identifiant l'école", disabled=True)
78
- self.adr_col=pn.widgets.MultiChoice(name="Colonnes composant l'adresse", disabled=True)
79
  self.tabd=pn.widgets.Tabulator(height=1000,
80
- formatters={"début_cours" : DateFormatter(format="%H:%M")},
81
- editors={'début_cours': 'datetime'},
82
- widths={'début_cours': 160},
83
  page_size=50,
84
  )
85
  self.file_dropper = pn.widgets.FileDropper(accepted_filetypes=list(self.dmime.values()))
@@ -121,14 +123,14 @@ class Tableur():
121
  def lancer_calculs(self, _):
122
  self.df["adresse"]=cree_adresse(self.df, self.adr_col.value)
123
  self.df["geometry"], self.df["suggestion"], self.df["score"] = zip(*self.df.adresse.apply(add_coo, details=True))
124
- display_cols= self.adr_col.value + ["suggestion", "score", self.ecole_col.value, "début_cours"]
125
  if self.delivery_tab is None:
126
  auj9=dateparser.parse("aujourd'hui 9:00")
127
- self.df["début_cours"] = (auj9+ (self.df[self.ecole_col.value].str.split().str[0].isin({'Collège', 'Lycée'}))*timedelta(hours=-0.5))
128
  else:
129
- self.delivery_tab.df["début_cours"]=self.delivery_tab.tabd.value["début_cours"]
130
  self.df=self.df.merge(
131
- self.delivery_tab.df[[self.delivery_tab.ecole_col.value, "geometry", "début_cours"]].reset_index(names="indice école"),
132
  left_on=self.ecole_col.value, right_on=self.delivery_tab.ecole_col.value,
133
  how="left", suffixes=["_p", "_d"]
134
  )
@@ -136,7 +138,7 @@ class Tableur():
136
  self.df.index = self.idx_offset+np.arange(len(self.df))
137
  self.tabd.value=self.df[display_cols]
138
  self.tabd.style.background_gradient(subset=["score"], cmap="RdYlGn", vmin=0.5, vmax=1.0)\
139
- .map(lambda v: '' if v == v else 'background-color:grey;', subset=["indice école", "début_cours"])
140
  self.tabd.param.trigger("value")
141
  return None
142
 
@@ -275,7 +277,7 @@ def tous_calculs(_):
275
  sol=calcul_routes(dfp, ladr, mat_dist, mat_dur, duree_taxi.value, duree_ecole.value, capacité.value)
276
  #print(sol.to_dict()["summary"])
277
 
278
- gpu=dfp.loc[list({u["id"] for u in sol.to_dict()["unassigned"]})][['suggestion', 'indice école', 'ECOLE', 'début_cours']].copy()
279
  gpu["Distance (km)"]=gpu.apply(lambda row: mat_dist[ladr.index(row["suggestion"])][ladr.index( row["ECOLE"])], axis=1)
280
  gpu["Durée (min)"]=gpu.apply(lambda row: mat_dur[ladr.index( row["suggestion"])][ladr.index( row["ECOLE"])]//60, axis=1)
281
  tabs.append(('Non assignés', pn.widgets.Tabulator(gpu, disabled=True, height=1000)))
@@ -296,9 +298,11 @@ def tous_calculs(_):
296
  bouton=pn.widgets.Button(name="Lancer les calculs", button_type="primary", on_click=tous_calculs)
297
 
298
  tabs=pn.Tabs(
299
- ('Établissements scolaires', tabd.get_panel()),
300
- ('Élèves', tabp.get_panel()),
301
- ('Calculs des routes', pn.Column(duree_taxi, duree_ecole, capacité, bouton) ),
302
- )
303
-
304
- tabs.servable()
 
 
 
39
  return df[cols].astype(str).agg(' '.join, axis=1).str.strip()
40
 
41
 
42
+ def geocode(adresse, as_point=False, as_dict=False):
43
  r=requests.get(url="https://data.geopf.fr/geocodage/search",
44
  params={"q": adresse})
45
  if not r.ok:
 
47
  try:
48
  js=r.json()
49
  feature=js['features'][0]
50
+ if as_point:
51
+ return shapely.Point(feature['geometry']['coordinates'])
52
+ if as_dict:
53
  return {js["query"] : feature}
54
+ return feature
55
  except:
56
  return None
57
 
58
 
59
  def add_coo(adresse, details=False):
60
+ feature=geocode(adresse)
61
  coo=shapely.Point(feature['geometry']['coordinates'])
62
  score=feature['properties']['score']
63
  suggestion=feature['properties']['label']
 
75
  self.dfeuilles={}
76
  self.df=pd.DataFrame()
77
  self.dmime={'csv': 'text/csv', 'xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'}
78
+ self.feuille=pn.widgets.Select(name='Feuille de calcul', disabled=True, width=200)
79
+ self.ecole_col=pn.widgets.Select(name="Colonne identifiant la destination", disabled=True, width=200)
80
+ self.adr_col=pn.widgets.MultiChoice(name="Colonnes composant l'adresse", disabled=True, width=200)
81
  self.tabd=pn.widgets.Tabulator(height=1000,
82
+ formatters={"arrivée_max" : DateFormatter(format="%H:%M")},
83
+ editors={'arrivée_max': 'datetime'},
84
+ widths={'arrivée_max': 160},
85
  page_size=50,
86
  )
87
  self.file_dropper = pn.widgets.FileDropper(accepted_filetypes=list(self.dmime.values()))
 
123
  def lancer_calculs(self, _):
124
  self.df["adresse"]=cree_adresse(self.df, self.adr_col.value)
125
  self.df["geometry"], self.df["suggestion"], self.df["score"] = zip(*self.df.adresse.apply(add_coo, details=True))
126
+ display_cols= self.adr_col.value + ["suggestion", "score", self.ecole_col.value, "arrivée_max"]
127
  if self.delivery_tab is None:
128
  auj9=dateparser.parse("aujourd'hui 9:00")
129
+ self.df["arrivée_max"] = (auj9+ (self.df[self.ecole_col.value].str.split().str[0].isin({'Collège', 'Lycée'}))*timedelta(hours=-0.5))
130
  else:
131
+ self.delivery_tab.df["arrivée_max"]=self.delivery_tab.tabd.value["arrivée_max"]
132
  self.df=self.df.merge(
133
+ self.delivery_tab.df[[self.delivery_tab.ecole_col.value, "geometry", "arrivée_max"]].reset_index(names="indice école"),
134
  left_on=self.ecole_col.value, right_on=self.delivery_tab.ecole_col.value,
135
  how="left", suffixes=["_p", "_d"]
136
  )
 
138
  self.df.index = self.idx_offset+np.arange(len(self.df))
139
  self.tabd.value=self.df[display_cols]
140
  self.tabd.style.background_gradient(subset=["score"], cmap="RdYlGn", vmin=0.5, vmax=1.0)\
141
+ .map(lambda v: '' if v == v else 'background-color:grey;', subset=["indice école", "arrivée_max"])
142
  self.tabd.param.trigger("value")
143
  return None
144
 
 
277
  sol=calcul_routes(dfp, ladr, mat_dist, mat_dur, duree_taxi.value, duree_ecole.value, capacité.value)
278
  #print(sol.to_dict()["summary"])
279
 
280
+ gpu=dfp.loc[list({u["id"] for u in sol.to_dict()["unassigned"]})][['suggestion', 'indice école', 'ECOLE', 'arrivée_max']].copy()
281
  gpu["Distance (km)"]=gpu.apply(lambda row: mat_dist[ladr.index(row["suggestion"])][ladr.index( row["ECOLE"])], axis=1)
282
  gpu["Durée (min)"]=gpu.apply(lambda row: mat_dur[ladr.index( row["suggestion"])][ladr.index( row["ECOLE"])]//60, axis=1)
283
  tabs.append(('Non assignés', pn.widgets.Tabulator(gpu, disabled=True, height=1000)))
 
298
  bouton=pn.widgets.Button(name="Lancer les calculs", button_type="primary", on_click=tous_calculs)
299
 
300
  tabs=pn.Tabs(
301
+ ('Destinations', tabd.get_panel()),
302
+ ('Usagers', tabp.get_panel()),
303
+ ('Calculs des routes', pn.Column(duree_taxi, duree_ecole, capacité, bouton) ), )
304
+
305
+ template= pn.template.MaterialTemplate(title="Problème de tournée des véhicules avec capacités et fenêtre temporelle")
306
+ template.main.append(tabs)
307
+ template.header.append("v0.2.0")
308
+ template.servable()
vrp.py CHANGED
@@ -13,14 +13,13 @@ def calcul_routes(dfp, ladr, mat_dist, mat_dur, durée_max_taxi=45, durée_max_e
13
  def tw(fin, delta):
14
  return [TimeWindow(deltaepoch(fin, delta), deltaepoch(fin))]
15
 
16
-
17
  for (irow, row) in dfp.iterrows():
18
  idxp=ladr.index(row["suggestion"])
19
  idxd=ladr.index(row["ECOLE"])
20
  vrm.add_job(
21
  Shipment(
22
- pickup=ShipmentStep(id=irow, location=idxp, time_windows=tw(row["début_cours"], durée_max_taxi)),
23
- delivery=ShipmentStep(id=irow, location=idxd, time_windows=tw(row["début_cours"], durée_max_ecole)),
24
  amount=[1],
25
  # skills
26
  )
 
13
  def tw(fin, delta):
14
  return [TimeWindow(deltaepoch(fin, delta), deltaepoch(fin))]
15
 
 
16
  for (irow, row) in dfp.iterrows():
17
  idxp=ladr.index(row["suggestion"])
18
  idxd=ladr.index(row["ECOLE"])
19
  vrm.add_job(
20
  Shipment(
21
+ pickup=ShipmentStep(id=irow, location=idxp, time_windows=tw(row["arrivée_max"], durée_max_taxi)),
22
+ delivery=ShipmentStep(id=irow, location=idxd, time_windows=tw(row["arrivée_max"], durée_max_ecole)),
23
  amount=[1],
24
  # skills
25
  )