Arpit-Bansal commited on
Commit
f6aa8e8
·
1 Parent(s): aece385

structrue change for schedule

Browse files
api/greedyoptim_api.py CHANGED
@@ -174,14 +174,40 @@ class ScheduleOptimizationResponse(BaseModel):
174
 
175
 
176
  # New models for full schedule response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  class ServiceBlockResponse(BaseModel):
178
- """Service block with timing details"""
179
  block_id: str
180
  departure_time: str
181
  origin: str
182
  destination: str
183
  trip_count: int
184
  estimated_km: float
 
 
 
 
185
 
186
 
187
  class TrainsetScheduleResponse(BaseModel):
@@ -330,17 +356,47 @@ def convert_schedule_result_to_response(schedule_result) -> FullScheduleResponse
330
  for ts in schedule_result.trainsets:
331
  service_blocks_resp = None
332
  if ts.service_blocks:
333
- service_blocks_resp = [
334
- ServiceBlockResponse(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  block_id=sb.block_id,
336
  departure_time=sb.departure_time,
337
  origin=sb.origin,
338
  destination=sb.destination,
339
  trip_count=sb.trip_count,
340
- estimated_km=sb.estimated_km
341
- )
342
- for sb in ts.service_blocks
343
- ]
 
 
344
 
345
  trainsets.append(TrainsetScheduleResponse(
346
  trainset_id=ts.trainset_id,
 
174
 
175
 
176
  # New models for full schedule response
177
+ class StationStopResponse(BaseModel):
178
+ """A single station stop within a trip"""
179
+ station_code: str
180
+ station_name: str
181
+ arrival_time: Optional[str] = None
182
+ departure_time: Optional[str] = None
183
+ distance_from_origin_km: float
184
+ platform: Optional[int] = None
185
+
186
+
187
+ class TripResponse(BaseModel):
188
+ """A single trip from origin to destination with all stops"""
189
+ trip_id: str
190
+ trip_number: int
191
+ direction: str # "UP" or "DOWN"
192
+ origin: str
193
+ destination: str
194
+ departure_time: str
195
+ arrival_time: str
196
+ stops: List[StationStopResponse] = []
197
+
198
+
199
  class ServiceBlockResponse(BaseModel):
200
+ """Service block with timing details and trips"""
201
  block_id: str
202
  departure_time: str
203
  origin: str
204
  destination: str
205
  trip_count: int
206
  estimated_km: float
207
+ journey_time_minutes: Optional[float] = None
208
+ period: Optional[str] = None
209
+ is_peak: bool = False
210
+ trips: Optional[List[TripResponse]] = None
211
 
212
 
213
  class TrainsetScheduleResponse(BaseModel):
 
356
  for ts in schedule_result.trainsets:
357
  service_blocks_resp = None
358
  if ts.service_blocks:
359
+ service_blocks_resp = []
360
+ for sb in ts.service_blocks:
361
+ # Convert trips with station stops
362
+ trips_resp = None
363
+ if sb.trips:
364
+ trips_resp = []
365
+ for trip in sb.trips:
366
+ stops_resp = [
367
+ StationStopResponse(
368
+ station_code=stop.station_code,
369
+ station_name=stop.station_name,
370
+ arrival_time=stop.arrival_time,
371
+ departure_time=stop.departure_time,
372
+ distance_from_origin_km=stop.distance_from_origin_km,
373
+ platform=stop.platform
374
+ )
375
+ for stop in trip.stops
376
+ ]
377
+ trips_resp.append(TripResponse(
378
+ trip_id=trip.trip_id,
379
+ trip_number=trip.trip_number,
380
+ direction=trip.direction,
381
+ origin=trip.origin,
382
+ destination=trip.destination,
383
+ departure_time=trip.departure_time,
384
+ arrival_time=trip.arrival_time,
385
+ stops=stops_resp
386
+ ))
387
+
388
+ service_blocks_resp.append(ServiceBlockResponse(
389
  block_id=sb.block_id,
390
  departure_time=sb.departure_time,
391
  origin=sb.origin,
392
  destination=sb.destination,
393
  trip_count=sb.trip_count,
394
+ estimated_km=sb.estimated_km,
395
+ journey_time_minutes=sb.journey_time_minutes,
396
+ period=sb.period,
397
+ is_peak=sb.is_peak,
398
+ trips=trips_resp
399
+ ))
400
 
401
  trainsets.append(TrainsetScheduleResponse(
402
  trainset_id=ts.trainset_id,
greedyOptim/__init__.py CHANGED
@@ -19,7 +19,8 @@ Usage:
19
  from .models import (
20
  OptimizationResult, OptimizationConfig, TrainsetConstraints,
21
  ScheduleResult, ScheduleTrainset, ServiceBlock, FleetSummary,
22
- OptimizationMetrics, ScheduleAlert, TrainStatus, MaintenanceType, AlertSeverity
 
23
  )
24
  from .evaluator import TrainsetSchedulingEvaluator
25
  from .genetic_algorithm import GeneticAlgorithmOptimizer
 
19
  from .models import (
20
  OptimizationResult, OptimizationConfig, TrainsetConstraints,
21
  ScheduleResult, ScheduleTrainset, ServiceBlock, FleetSummary,
22
+ OptimizationMetrics, ScheduleAlert, TrainStatus, MaintenanceType, AlertSeverity,
23
+ StationStop, Trip
24
  )
25
  from .evaluator import TrainsetSchedulingEvaluator
26
  from .genetic_algorithm import GeneticAlgorithmOptimizer
greedyOptim/data/kochi_metro_stations.json CHANGED
@@ -264,4 +264,4 @@
264
  {"start": "17:00", "end": "21:00", "type": "evening"}
265
  ]
266
  }
267
- }
 
264
  {"start": "17:00", "end": "21:00", "type": "evening"}
265
  ]
266
  }
267
+ }
greedyOptim/models.py CHANGED
@@ -29,6 +29,56 @@ class AlertSeverity(str, Enum):
29
  CRITICAL = "CRITICAL"
30
 
31
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
  @dataclass
33
  class ServiceBlock:
34
  """A service block represents a continuous operating period for a train."""
@@ -38,9 +88,14 @@ class ServiceBlock:
38
  destination: str
39
  trip_count: int
40
  estimated_km: int
 
 
 
 
 
41
 
42
  def to_dict(self) -> Dict:
43
- return {
44
  'block_id': self.block_id,
45
  'departure_time': self.departure_time,
46
  'origin': self.origin,
@@ -48,6 +103,14 @@ class ServiceBlock:
48
  'trip_count': self.trip_count,
49
  'estimated_km': self.estimated_km
50
  }
 
 
 
 
 
 
 
 
51
 
52
 
53
  @dataclass
 
29
  CRITICAL = "CRITICAL"
30
 
31
 
32
+ @dataclass
33
+ class StationStop:
34
+ """A single station stop within a trip."""
35
+ station_code: str
36
+ station_name: str
37
+ arrival_time: Optional[str] # HH:MM format, None for origin
38
+ departure_time: Optional[str] # HH:MM format, None for destination
39
+ distance_from_origin_km: float
40
+ platform: Optional[int] = None
41
+
42
+ def to_dict(self) -> Dict:
43
+ result = {
44
+ 'station_code': self.station_code,
45
+ 'station_name': self.station_name,
46
+ 'distance_from_origin_km': self.distance_from_origin_km
47
+ }
48
+ if self.arrival_time:
49
+ result['arrival_time'] = self.arrival_time
50
+ if self.departure_time:
51
+ result['departure_time'] = self.departure_time
52
+ if self.platform:
53
+ result['platform'] = self.platform
54
+ return result
55
+
56
+
57
+ @dataclass
58
+ class Trip:
59
+ """A single trip from origin to destination with all stops."""
60
+ trip_id: str
61
+ trip_number: int # 1, 2, 3... within the block
62
+ direction: str # "UP" (towards Pettah) or "DOWN" (towards Aluva)
63
+ origin: str
64
+ destination: str
65
+ departure_time: str
66
+ arrival_time: str
67
+ stops: List[StationStop] = field(default_factory=list)
68
+
69
+ def to_dict(self) -> Dict:
70
+ return {
71
+ 'trip_id': self.trip_id,
72
+ 'trip_number': self.trip_number,
73
+ 'direction': self.direction,
74
+ 'origin': self.origin,
75
+ 'destination': self.destination,
76
+ 'departure_time': self.departure_time,
77
+ 'arrival_time': self.arrival_time,
78
+ 'stops': [s.to_dict() for s in self.stops]
79
+ }
80
+
81
+
82
  @dataclass
83
  class ServiceBlock:
84
  """A service block represents a continuous operating period for a train."""
 
88
  destination: str
89
  trip_count: int
90
  estimated_km: int
91
+ # Enhanced fields
92
+ journey_time_minutes: Optional[float] = None
93
+ trips: List[Trip] = field(default_factory=list) # Detailed trip breakdown
94
+ period: Optional[str] = None # morning_peak, midday, evening_peak, late_evening
95
+ is_peak: bool = False
96
 
97
  def to_dict(self) -> Dict:
98
+ result = {
99
  'block_id': self.block_id,
100
  'departure_time': self.departure_time,
101
  'origin': self.origin,
 
103
  'trip_count': self.trip_count,
104
  'estimated_km': self.estimated_km
105
  }
106
+ if self.journey_time_minutes:
107
+ result['journey_time_minutes'] = self.journey_time_minutes
108
+ if self.period:
109
+ result['period'] = self.period
110
+ result['is_peak'] = self.is_peak
111
+ if self.trips:
112
+ result['trips'] = [t.to_dict() for t in self.trips]
113
+ return result
114
 
115
 
116
  @dataclass
greedyOptim/schedule_generator.py CHANGED
@@ -9,9 +9,11 @@ from typing import Dict, List, Optional
9
  from .models import (
10
  OptimizationResult, OptimizationConfig,
11
  ScheduleResult, ScheduleTrainset, ServiceBlock, FleetSummary,
12
- OptimizationMetrics, ScheduleAlert, TrainStatus, MaintenanceType, AlertSeverity
 
13
  )
14
  from .service_blocks import ServiceBlockGenerator
 
15
  from .evaluator import normalize_certificate_status, normalize_component_status
16
 
17
 
@@ -37,6 +39,12 @@ class ScheduleGenerator:
37
  self.config = config or OptimizationConfig()
38
  self.service_block_generator = ServiceBlockGenerator()
39
 
 
 
 
 
 
 
40
  # Build lookups
41
  self._build_lookups()
42
 
@@ -178,6 +186,97 @@ class ScheduleGenerator:
178
  alerts=alerts
179
  )
180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  def _generate_service_trainset(
182
  self,
183
  trainset_id: str,
@@ -214,17 +313,26 @@ class ScheduleGenerator:
214
  # Fall back to legacy index-based block generation
215
  blocks_data = self.service_block_generator.generate_service_blocks(index, num_service)
216
 
217
- service_blocks = [
218
- ServiceBlock(
219
- block_id=b['block_id'],
 
 
 
 
 
 
220
  departure_time=b['departure_time'],
221
  origin=b['origin'],
222
  destination=b['destination'],
223
  trip_count=b['trip_count'],
224
- estimated_km=b['estimated_km']
 
 
 
 
225
  )
226
- for b in blocks_data
227
- ]
228
 
229
  daily_km = sum(b.estimated_km for b in service_blocks)
230
 
 
9
  from .models import (
10
  OptimizationResult, OptimizationConfig,
11
  ScheduleResult, ScheduleTrainset, ServiceBlock, FleetSummary,
12
+ OptimizationMetrics, ScheduleAlert, TrainStatus, MaintenanceType, AlertSeverity,
13
+ StationStop, Trip
14
  )
15
  from .service_blocks import ServiceBlockGenerator
16
+ from .station_loader import get_station_loader, StationDataLoader
17
  from .evaluator import normalize_certificate_status, normalize_component_status
18
 
19
 
 
39
  self.config = config or OptimizationConfig()
40
  self.service_block_generator = ServiceBlockGenerator()
41
 
42
+ # Initialize station loader for detailed trip generation
43
+ try:
44
+ self.station_loader = get_station_loader()
45
+ except Exception:
46
+ self.station_loader = None
47
+
48
  # Build lookups
49
  self._build_lookups()
50
 
 
186
  alerts=alerts
187
  )
188
 
189
+ def _generate_trips_for_block(
190
+ self,
191
+ block_data: Dict,
192
+ block_id: str
193
+ ) -> List[Trip]:
194
+ """Generate detailed trips with station stops for a service block.
195
+
196
+ Args:
197
+ block_data: Block data dictionary
198
+ block_id: Block ID
199
+
200
+ Returns:
201
+ List of Trip objects with station stops
202
+ """
203
+ if not self.station_loader:
204
+ return []
205
+
206
+ trips = []
207
+ origin = block_data['origin']
208
+ destination = block_data['destination']
209
+ trip_count = block_data.get('trip_count', 1)
210
+
211
+ # Parse initial departure time
212
+ dep_time_str = block_data['departure_time']
213
+ current_time = datetime.strptime(dep_time_str, '%H:%M')
214
+
215
+ # Get terminals for direction determination
216
+ terminals = self.station_loader.terminals
217
+
218
+ for trip_num in range(1, trip_count + 1):
219
+ # Determine direction
220
+ if origin == terminals[0]: # Aluva
221
+ direction = "DOWN" # Towards Pettah
222
+ else:
223
+ direction = "UP" # Towards Aluva
224
+
225
+ # Get station sequence with times
226
+ try:
227
+ station_sequence = self.station_loader.get_station_sequence_for_trip(
228
+ origin, destination,
229
+ include_times=True,
230
+ departure_time=current_time.strftime('%H:%M')
231
+ )
232
+ except Exception:
233
+ station_sequence = []
234
+
235
+ # Convert to StationStop objects
236
+ stops = []
237
+ for station in station_sequence:
238
+ stop = StationStop(
239
+ station_code=station.get('code', ''),
240
+ station_name=station['name'],
241
+ arrival_time=station.get('arrival_time'),
242
+ departure_time=station.get('departure_time'),
243
+ distance_from_origin_km=station.get('distance_from_origin_km', 0),
244
+ platform=random.choice([1, 2]) # Random platform assignment
245
+ )
246
+ stops.append(stop)
247
+
248
+ # Calculate arrival time at destination
249
+ if stops:
250
+ arrival_time = stops[-1].arrival_time or current_time.strftime('%H:%M')
251
+ else:
252
+ # Fallback calculation
253
+ journey_minutes = block_data.get('journey_time_minutes', 53)
254
+ arrival = current_time + timedelta(minutes=journey_minutes)
255
+ arrival_time = arrival.strftime('%H:%M')
256
+
257
+ trip = Trip(
258
+ trip_id=f"{block_id}-T{trip_num:02d}",
259
+ trip_number=trip_num,
260
+ direction=direction,
261
+ origin=origin,
262
+ destination=destination,
263
+ departure_time=current_time.strftime('%H:%M'),
264
+ arrival_time=arrival_time,
265
+ stops=stops
266
+ )
267
+ trips.append(trip)
268
+
269
+ # Calculate next trip start time (arrival + turnaround)
270
+ journey_minutes = block_data.get('journey_time_minutes', 53)
271
+ turnaround_minutes = 3 # Turnaround at terminal
272
+ total_minutes = journey_minutes + turnaround_minutes
273
+ current_time = current_time + timedelta(minutes=total_minutes)
274
+
275
+ # Swap origin and destination for return trip
276
+ origin, destination = destination, origin
277
+
278
+ return trips
279
+
280
  def _generate_service_trainset(
281
  self,
282
  trainset_id: str,
 
313
  # Fall back to legacy index-based block generation
314
  blocks_data = self.service_block_generator.generate_service_blocks(index, num_service)
315
 
316
+ service_blocks = []
317
+ for b in blocks_data:
318
+ block_id = b['block_id']
319
+
320
+ # Generate detailed trips with station stops
321
+ trips = self._generate_trips_for_block(b, block_id)
322
+
323
+ service_block = ServiceBlock(
324
+ block_id=block_id,
325
  departure_time=b['departure_time'],
326
  origin=b['origin'],
327
  destination=b['destination'],
328
  trip_count=b['trip_count'],
329
+ estimated_km=b['estimated_km'],
330
+ journey_time_minutes=b.get('journey_time_minutes'),
331
+ period=b.get('period'),
332
+ is_peak=b.get('is_peak', False),
333
+ trips=trips
334
  )
335
+ service_blocks.append(service_block)
 
336
 
337
  daily_km = sum(b.estimated_km for b in service_blocks)
338