Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(22)

Delta Between Two Patch Sets: python/transitfeed.py

Issue 75063: Adding validation for uniqueness of service_id X date in calendar_dates SVN Base: http://googletransitdatafeed.googlecode.com/svn/trunk/
Left Patch Set: Created 5 months, 3 weeks ago
Right Patch Set: Removing unnecessary linebreak Created 1 month, 1 week ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
Left: Side by side diff | Download
Right: Side by side diff | Download
LEFTRIGHT
1 #!/usr/bin/python2.5 1 #!/usr/bin/python2.5
2 2
3 # Copyright (C) 2007 Google Inc. 3 # Copyright (C) 2007 Google Inc.
4 # 4 #
5 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License. 6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at 7 # You may obtain a copy of the License at
8 # 8 #
9 # http://www.apache.org/licenses/LICENSE-2.0 9 # http://www.apache.org/licenses/LICENSE-2.0
10 # 10 #
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after
62 import warnings 62 import warnings
63 # Objects in a schedule (Route, Trip, etc) should not keep a strong reference 63 # Objects in a schedule (Route, Trip, etc) should not keep a strong reference
64 # to the Schedule object to avoid a reference cycle. Schedule needs to use 64 # to the Schedule object to avoid a reference cycle. Schedule needs to use
65 # __del__ to cleanup its temporary file. The garbage collector can't handle 65 # __del__ to cleanup its temporary file. The garbage collector can't handle
66 # reference cycles containing objects with custom cleanup code. 66 # reference cycles containing objects with custom cleanup code.
67 import weakref 67 import weakref
68 import zipfile 68 import zipfile
69 69
70 OUTPUT_ENCODING = 'utf-8' 70 OUTPUT_ENCODING = 'utf-8'
71 MAX_DISTANCE_FROM_STOP_TO_SHAPE = 1000 71 MAX_DISTANCE_FROM_STOP_TO_SHAPE = 1000
72 72 MAX_DISTANCE_BETWEEN_STOP_AND_PARENT_STATION_WARNING = 100.0
73 73 MAX_DISTANCE_BETWEEN_STOP_AND_PARENT_STATION_ERROR = 1000.0
74 __version__ = '1.2.1' 74
75 __version__ = '1.2.2'
75 76
76 77
77 def EncodeUnicode(text): 78 def EncodeUnicode(text):
78 """ 79 """
79 Optionally encode text and return it. The result should be safe to print. 80 Optionally encode text and return it. The result should be safe to print.
80 """ 81 """
81 if type(text) == type(u''): 82 if type(text) == type(u''):
82 return text.encode(OUTPUT_ENCODING) 83 return text.encode(OUTPUT_ENCODING)
83 else: 84 else:
84 return text 85 return text
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after
163 e = MissingValue(column_name=column_name, reason=reason, context=context, 164 e = MissingValue(column_name=column_name, reason=reason, context=context,
164 context2=self._context) 165 context2=self._context)
165 self._Report(e) 166 self._Report(e)
166 167
167 def InvalidValue(self, column_name, value, reason=None, context=None, 168 def InvalidValue(self, column_name, value, reason=None, context=None,
168 type=TYPE_ERROR): 169 type=TYPE_ERROR):
169 e = InvalidValue(column_name=column_name, value=value, reason=reason, 170 e = InvalidValue(column_name=column_name, value=value, reason=reason,
170 context=context, context2=self._context, type=type) 171 context=context, context2=self._context, type=type)
171 self._Report(e) 172 self._Report(e)
172 173
173 def DuplicateID(self, column_name, value, context=None, type=TYPE_ERROR): 174 def DuplicateID(self, column_names, values, context=None, type=TYPE_ERROR):
174 e = DuplicateID(column_name=column_name, value=value, 175 if isinstance(column_names, tuple):
176 column_names = '(' + ', '.join(column_names) + ')'
177 if isinstance(values, tuple):
178 values = '(' + ', '.join(values) + ')'
179 e = DuplicateID(column_name=column_names, value=values,
175 context=context, context2=self._context, type=type) 180 context=context, context2=self._context, type=type)
176 self._Report(e) 181 self._Report(e)
177 182
178 def UnusedStop(self, stop_id, stop_name, context=None): 183 def UnusedStop(self, stop_id, stop_name, context=None):
179 e = UnusedStop(stop_id=stop_id, stop_name=stop_name, 184 e = UnusedStop(stop_id=stop_id, stop_name=stop_name,
180 context=context, context2=self._context, type=TYPE_WARNING) 185 context=context, context2=self._context, type=TYPE_WARNING)
181 self._Report(e) 186 self._Report(e)
182 187
183 def UsedStation(self, stop_id, stop_name, context=None): 188 def UsedStation(self, stop_id, stop_name, context=None):
184 e = UsedStation(stop_id=stop_id, stop_name=stop_name, 189 e = UsedStation(stop_id=stop_id, stop_name=stop_name,
185 context=context, context2=self._context, type=TYPE_ERROR) 190 context=context, context2=self._context, type=TYPE_ERROR)
186 self._Report(e) 191 self._Report(e)
187 192
193 def StopTooFarFromParentStation(self, stop_id, stop_name, parent_stop_id,
194 parent_stop_name, distance,
195 type=TYPE_WARNING, context=None):
196 e = StopTooFarFromParentStation(
197 stop_id=stop_id, stop_name=stop_name,
198 parent_stop_id=parent_stop_id,
199 parent_stop_name=parent_stop_name, distance=distance,
200 context=context, context2=self._context, type=type)
201 self._Report(e)
202
188 def ExpirationDate(self, expiration, context=None): 203 def ExpirationDate(self, expiration, context=None):
189 e = ExpirationDate(expiration=expiration, context=context, 204 e = ExpirationDate(expiration=expiration, context=context,
190 context2=self._context, type=TYPE_WARNING) 205 context2=self._context, type=TYPE_WARNING)
206 self._Report(e)
207
208 def FutureService(self, start_date, context=None):
209 e = FutureService(start_date=start_date, context=context,
210 context2=self._context, type=TYPE_WARNING)
191 self._Report(e) 211 self._Report(e)
192 212
193 def InvalidLineEnd(self, bad_line_end, context=None): 213 def InvalidLineEnd(self, bad_line_end, context=None):
194 """bad_line_end is a human readable string.""" 214 """bad_line_end is a human readable string."""
195 e = InvalidLineEnd(bad_line_end=bad_line_end, context=context, 215 e = InvalidLineEnd(bad_line_end=bad_line_end, context=context,
196 context2=self._context, type=TYPE_WARNING) 216 context2=self._context, type=TYPE_WARNING)
197 self._Report(e) 217 self._Report(e)
198 218
219 def TooFastTravel(self, trip_id, prev_stop, next_stop, dist, time, speed,
220 type=TYPE_ERROR):
221 e = TooFastTravel(trip_id=trip_id, prev_stop=prev_stop,
222 next_stop=next_stop, time=time, dist=dist, speed=speed,
223 context=None, context2=self._context, type=type)
224 self._Report(e)
225
226 def StopWithMultipleRouteTypes(self, stop_name, stop_id, route_id1, route_id2,
227 context=None):
228 e = StopWithMultipleRouteTypes(stop_name=stop_name, stop_id=stop_id,
229 route_id1=route_id1, route_id2=route_id2,
230 context=context, context2=self._context,
231 type=TYPE_WARNING)
232 self._Report(e)
233
234 def DuplicateTrip(self, trip_id1, route_id1, trip_id2, route_id2,
235 context=None):
236 e = DuplicateTrip(trip_id1=trip_id1, route_id1=route_id1, trip_id2=trip_id2,
237 route_id2=route_id2, context=context,
238 context2=self._context, type=TYPE_WARNING)
239 self._Report(e)
240
199 def OtherProblem(self, description, context=None, type=TYPE_ERROR): 241 def OtherProblem(self, description, context=None, type=TYPE_ERROR):
200 e = OtherProblem(description=description, 242 e = OtherProblem(description=description,
201 context=context, context2=self._context, type=type) 243 context=context, context2=self._context, type=type)
202 self._Report(e) 244 self._Report(e)
203 245
204 class ProblemReporter(ProblemReporterBase): 246 class ProblemReporter(ProblemReporterBase):
205 """This is a basic problem reporter that just prints to console.""" 247 """This is a basic problem reporter that just prints to console."""
206 def _Report(self, e): 248 def _Report(self, e):
207 print EncodeUnicode(self._LineWrap(e.FormatProblem(), 78))
208 context = e.FormatContext() 249 context = e.FormatContext()
209 if context: 250 if context:
210 print context 251 print context
252 print EncodeUnicode(self._LineWrap(e.FormatProblem(), 78))
211 253
212 @staticmethod 254 @staticmethod
213 def _LineWrap(text, width): 255 def _LineWrap(text, width):
214 """ 256 """
215 A word-wrap function that preserves existing line breaks 257 A word-wrap function that preserves existing line breaks
216 and most spaces in the text. Expects that existing line 258 and most spaces in the text. Expects that existing line
217 breaks are posix newlines (\n). 259 breaks are posix newlines (\n).
218 260
219 Taken from: 261 Taken from:
220 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061 262 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after
361 class DuplicateID(ExceptionWithContext): 403 class DuplicateID(ExceptionWithContext):
362 ERROR_TEXT = 'Duplicate ID %(value)s in column %(column_name)s' 404 ERROR_TEXT = 'Duplicate ID %(value)s in column %(column_name)s'
363 405
364 class UnusedStop(ExceptionWithContext): 406 class UnusedStop(ExceptionWithContext):
365 ERROR_TEXT = "%(stop_name)s (ID %(stop_id)s) isn't used in any trips" 407 ERROR_TEXT = "%(stop_name)s (ID %(stop_id)s) isn't used in any trips"
366 408
367 class UsedStation(ExceptionWithContext): 409 class UsedStation(ExceptionWithContext):
368 ERROR_TEXT = "%(stop_name)s (ID %(stop_id)s) has location_type=1 " \ 410 ERROR_TEXT = "%(stop_name)s (ID %(stop_id)s) has location_type=1 " \
369 "(station) so it should not appear in stop_times" 411 "(station) so it should not appear in stop_times"
370 412
413 class StopTooFarFromParentStation(ExceptionWithContext):
414 ERROR_TEXT = "%(stop_name)s (ID %(stop_id)s) is too far from its" \
415 " parent station %(parent_stop_name)s (ID %(parent_stop_id)s)" \
416 " : %(distance)s meters."
417
371 class ExpirationDate(ExceptionWithContext): 418 class ExpirationDate(ExceptionWithContext):
372 def FormatProblem(self, d=None): 419 def FormatProblem(self, d=None):
373 if not d: 420 if not d:
374 d = self.GetDictToFormat() 421 d = self.GetDictToFormat()
375 expiration = d['expiration'] 422 expiration = d['expiration']
376 formatted_date = time.strftime("%B %d, %Y", 423 formatted_date = time.strftime("%B %d, %Y",
377 time.localtime(expiration)) 424 time.localtime(expiration))
378 if (expiration < time.mktime(time.localtime())): 425 if (expiration < time.mktime(time.localtime())):
379 return "This feed expired on %s" % formatted_date 426 return "This feed expired on %s" % formatted_date
380 else: 427 else:
381 return "This feed will soon expire, on %s" % formatted_date 428 return "This feed will soon expire, on %s" % formatted_date
382 429
430 class FutureService(ExceptionWithContext):
431 def FormatProblem(self, d=None):
432 if not d:
433 d = self.GetDictToFormat()
434 formatted_date = time.strftime("%B %d, %Y", time.localtime(d['start_date']))
435 return ("The earliest service date in this feed is in the future, on %s. "
436 "Published feeds must always include the current date." %
437 formatted_date)
438
439
383 class InvalidLineEnd(ExceptionWithContext): 440 class InvalidLineEnd(ExceptionWithContext):
384 ERROR_TEXT = "Each line must end with CR LF or LF except for the last line " \ 441 ERROR_TEXT = "Each line must end with CR LF or LF except for the last line " \
385 "of the file. This line ends with \"%(bad_line_end)s\"." 442 "of the file. This line ends with \"%(bad_line_end)s\"."
443
444 class StopWithMultipleRouteTypes(ExceptionWithContext):
445 ERROR_TEXT = "Stop %(stop_name)s (ID=%(stop_id)s) belongs to both " \
446 "subway (ID=%(route_id1)s) and bus line (ID=%(route_id2)s)."
447
448 class TooFastTravel(ExceptionWithContext):
449 def FormatProblem(self, d=None):
450 if not d:
451 d = self.GetDictToFormat()
452 if not d['speed']:
453 return "High speed travel detected in trip %(trip_id)s: %(prev_stop)s" \
454 " to %(next_stop)s. %(dist)d meters in %(time)d seconds." % d
455 else:
456 return "High speed travel detected in trip %(trip_id)s: %(prev_stop)s" \
457 " to %(next_stop)s. %(dist)d meters in %(time)d seconds." \
458 " (%(speed)f km/h)." % d
459
460 class DuplicateTrip(ExceptionWithContext):
461 ERROR_TEXT = "Trip %(trip_id1)s of route %(route_id1)s might be duplicated " \
462 "with trip %(trip_id2)s of route %(route_id2)s. They go " \
463 "through the same stops with same service."
386 464
387 class OtherProblem(ExceptionWithContext): 465 class OtherProblem(ExceptionWithContext):
388 ERROR_TEXT = '%(description)s' 466 ERROR_TEXT = '%(description)s'
389 467
390 468
391 class ExceptionProblemReporter(ProblemReporter): 469 class ExceptionProblemReporter(ProblemReporter):
392 def __init__(self, raise_warnings=False): 470 def __init__(self, raise_warnings=False):
393 ProblemReporterBase.__init__(self) 471 ProblemReporterBase.__init__(self)
394 self.raise_warnings = raise_warnings 472 self.raise_warnings = raise_warnings
395 473
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
442 520
443 def FindUniqueId(dic): 521 def FindUniqueId(dic):
444 """Return a string not used as a key in the dictionary dic""" 522 """Return a string not used as a key in the dictionary dic"""
445 name = str(len(dic)) 523 name = str(len(dic))
446 while name in dic: 524 while name in dic:
447 name = str(random.randint(1, 999999999)) 525 name = str(random.randint(1, 999999999))
448 return name 526 return name
449 527
450 528
451 def TimeToSecondsSinceMidnight(time_string): 529 def TimeToSecondsSinceMidnight(time_string):
452 """Convert HH:MM:SS into seconds since midnight. 530 """Convert HHH:MM:SS into seconds since midnight.
453 531
454 For example "01:02:03" returns 3723. The leading zero of the hours may be 532 For example "01:02:03" returns 3723. The leading zero of the hours may be
455 omitted. HH may be more than 23 if the time is on the following day.""" 533 omitted. HH may be more than 23 if the time is on the following day."""
456 m = re.match(r'(\d{1,2}):([0-5]\d):([0-5]\d)$', time_string) 534 m = re.match(r'(\d{1,3}):([0-5]\d):([0-5]\d)$', time_string)
457 # ignored: matching for leap seconds 535 # ignored: matching for leap seconds
458 if not m: 536 if not m:
459 raise Error, 'Bad HH:MM:SS "%s"' % time_string 537 raise Error, 'Bad HH:MM:SS "%s"' % time_string
460 return int(m.group(1)) * 3600 + int(m.group(2)) * 60 + int(m.group(3)) 538 return int(m.group(1)) * 3600 + int(m.group(2)) * 60 + int(m.group(3))
461 539
462 540
463 def FormatSecondsSinceMidnight(s): 541 def FormatSecondsSinceMidnight(s):
464 """Formats an int number of seconds past midnight into a string 542 """Formats an int number of seconds past midnight into a string
465 as "HH:MM:SS".""" 543 as "HH:MM:SS"."""
466 return "%02d:%02d:%02d" % (s / 3600, (s / 60) % 60, s % 60) 544 return "%02d:%02d:%02d" % (s / 3600, (s / 60) % 60, s % 60)
(...skipping 335 matching lines...) Expand 10 before | Expand all | Expand 10 after
802 880
803 class Route(GenericGTFSObject): 881 class Route(GenericGTFSObject):
804 """Represents a single route.""" 882 """Represents a single route."""
805 883
806 _REQUIRED_FIELD_NAMES = [ 884 _REQUIRED_FIELD_NAMES = [
807 'route_id', 'route_short_name', 'route_long_name', 'route_type' 885 'route_id', 'route_short_name', 'route_long_name', 'route_type'
808 ] 886 ]
809 _FIELD_NAMES = _REQUIRED_FIELD_NAMES + [ 887 _FIELD_NAMES = _REQUIRED_FIELD_NAMES + [
810 'agency_id', 'route_desc', 'route_url', 'route_color', 'route_text_color' 888 'agency_id', 'route_desc', 'route_url', 'route_color', 'route_text_color'
811 ] 889 ]
812 _ROUTE_TYPE_NAMES = { 890 _ROUTE_TYPES = {
813 'Tram': 0, 891 0: {'name':'Tram', 'max_speed':50},
814 'Subway': 1, 892 1: {'name':'Subway', 'max_speed':150},
815 'Rail': 2, 893 2: {'name':'Rail', 'max_speed':300},
816 'Bus': 3, 894 3: {'name':'Bus', 'max_speed':100},
817 'Ferry': 4, 895 4: {'name':'Ferry', 'max_speed':80},
818 'Cable Car': 5, 896 5: {'name':'Cable Car', 'max_speed':50},
819 'Gondola': 6, 897 6: {'name':'Gondola', 'max_speed':50},
820 'Funicular': 7 898 7: {'name':'Funicular', 'max_speed':50},
821 } 899 }
822 _ROUTE_TYPE_IDS = set(_ROUTE_TYPE_NAMES.values()) 900 # Create a reverse lookup dict of route type names to route types.
901 _ROUTE_TYPE_IDS = set(_ROUTE_TYPES.keys())
902 _ROUTE_TYPE_NAMES = dict((v['name'], k) for k, v in _ROUTE_TYPES.items())
823 _TABLE_NAME = 'routes' 903 _TABLE_NAME = 'routes'
824 904
825 def __init__(self, short_name=None, long_name=None, route_type=None, 905 def __init__(self, short_name=None, long_name=None, route_type=None,
826 route_id=None, agency_id=None, field_dict=None): 906 route_id=None, agency_id=None, field_dict=None):
827 self._schedule = None 907 self._schedule = None
828 self._trips = [] 908 self._trips = []
829 909
830 if not field_dict: 910 if not field_dict:
831 field_dict = {} 911 field_dict = {}
832 if short_name is not None: 912 if short_name is not None:
(...skipping 818 matching lines...) Expand 10 before | Expand all | Expand 10 after
1651 problems.OtherProblem( 1731 problems.OtherProblem(
1652 'No time for start of trip_id "%s""' % (self.trip_id)) 1732 'No time for start of trip_id "%s""' % (self.trip_id))
1653 if stoptimes[-1].arrival_time is None and stoptimes[-1].departure_time is None: 1733 if stoptimes[-1].arrival_time is None and stoptimes[-1].departure_time is None:
1654 problems.OtherProblem( 1734 problems.OtherProblem(
1655 'No time for end of trip_id "%s""' % (self.trip_id)) 1735 'No time for end of trip_id "%s""' % (self.trip_id))
1656 1736
1657 # Sorts the stoptimes by sequence and then checks that the arrival time 1737 # Sorts the stoptimes by sequence and then checks that the arrival time
1658 # for each time point is after the departure time of the previous. 1738 # for each time point is after the departure time of the previous.
1659 stoptimes.sort(key=lambda x: x.stop_sequence) 1739 stoptimes.sort(key=lambda x: x.stop_sequence)
1660 prev_departure = 0 1740 prev_departure = 0
1741 prev_stop = None
1742 try:
1743 route_type = self._schedule.GetRoute(self.route_id).route_type
1744 max_speed = Route._ROUTE_TYPES[route_type]['max_speed']
1745 except KeyError, e:
1746 # If route_type cannot be found, assume it is 0 (Tram) for checking
1747 # speeds between stops.
1748 route_type = 0
1749 max_speed = 30
1661 for timepoint in stoptimes: 1750 for timepoint in stoptimes:
1662 if timepoint.arrival_secs is not None: 1751 if timepoint.arrival_secs is not None:
1752 self._CheckSpeed(prev_stop, timepoint.stop, prev_departure,
1753 timepoint.arrival_secs, max_speed, problems)
1754
1663 if timepoint.arrival_secs >= prev_departure: 1755 if timepoint.arrival_secs >= prev_departure:
1664 prev_departure = timepoint.departure_secs 1756 prev_departure = timepoint.departure_secs
1757 prev_stop = timepoint.stop
1665 else: 1758 else:
1666 problems.OtherProblem('Timetravel detected! Arrival time ' 1759 problems.OtherProblem('Timetravel detected! Arrival time '
1667 'is before previous departure ' 1760 'is before previous departure '
1668 'at sequence number %s in trip %s' % 1761 'at sequence number %s in trip %s' %
1669 (timepoint.stop_sequence, self.trip_id)) 1762 (timepoint.stop_sequence, self.trip_id))
1670 1763
1671 if self.shape_id and self.shape_id in self._schedule._shapes: 1764 if self.shape_id and self.shape_id in self._schedule._shapes:
1672 shape = self._schedule.GetShape(self.shape_id) 1765 shape = self._schedule.GetShape(self.shape_id)
1673 max_shape_dist = shape.max_distance 1766 max_shape_dist = shape.max_distance
1674 st = stoptimes[-1] 1767 st = stoptimes[-1]
(...skipping 28 matching lines...) Expand all
1703 type=TYPE_WARNING) 1796 type=TYPE_WARNING)
1704 1797
1705 # O(n^2), but we don't anticipate many headway periods per trip 1798 # O(n^2), but we don't anticipate many headway periods per trip
1706 for headway_index, headway in enumerate(self._headways[0:-1]): 1799 for headway_index, headway in enumerate(self._headways[0:-1]):
1707 for other in self._headways[headway_index + 1:]: 1800 for other in self._headways[headway_index + 1:]:
1708 if (other[0] < headway[1]) and (other[1] > headway[0]): 1801 if (other[0] < headway[1]) and (other[1] > headway[0]):
1709 problems.OtherProblem('Trip contains overlapping headway periods ' 1802 problems.OtherProblem('Trip contains overlapping headway periods '
1710 '%s and %s' % 1803 '%s and %s' %
1711 (self._HeadwayOutputTuple(headway), 1804 (self._HeadwayOutputTuple(headway),
1712 self._HeadwayOutputTuple(other))) 1805 self._HeadwayOutputTuple(other)))
1806
1807 def _CheckSpeed(self, prev_stop, next_stop, depart_time,
1808 arrive_time, max_speed, problems):
1809 # Checks that the speed between two stops is not faster than max_speed
1810 if prev_stop != None:
1811 try:
1812 time_between_stops = arrive_time - depart_time
1813 except TypeError:
1814 return
1815
1816 try:
1817 dist_between_stops = \
1818 ApproximateDistanceBetweenStops(next_stop, prev_stop)
1819 except TypeError, e:
1820 return
1821
1822 if time_between_stops == 0:
1823 problems.TooFastTravel(self.trip_id,
1824 prev_stop.stop_name,
1825 next_stop.stop_name,
1826 dist_between_stops,
1827 time_between_stops,
1828 speed=None,
1829 type=TYPE_WARNING)
1830 return
1831 # This needs floating point division for precision.
1832 speed_between_stops = ((float(dist_between_stops) / 1000) /
1833 (float(time_between_stops) / 3600))
1834 if speed_between_stops > max_speed:
1835 problems.TooFastTravel(self.trip_id,
1836 prev_stop.stop_name,
1837 next_stop.stop_name,
1838 dist_between_stops,
1839 time_between_stops,
1840 speed_between_stops,
1841 type=TYPE_WARNING)
1713 1842
1714 # TODO: move these into a separate file 1843 # TODO: move these into a separate file
1715 class ISO4217(object): 1844 class ISO4217(object):
1716 """Represents the set of currencies recognized by the ISO-4217 spec.""" 1845 """Represents the set of currencies recognized by the ISO-4217 spec."""
1717 codes = { # map of alpha code to numerical code 1846 codes = { # map of alpha code to numerical code
1718 'AED': 784, 'AFN': 971, 'ALL': 8, 'AMD': 51, 'ANG': 532, 'AOA': 973, 1847 'AED': 784, 'AFN': 971, 'ALL': 8, 'AMD': 51, 'ANG': 532, 'AOA': 973,
1719 'ARS': 32, 'AUD': 36, 'AWG': 533, 'AZN': 944, 'BAM': 977, 'BBD': 52, 1848 'ARS': 32, 'AUD': 36, 'AWG': 533, 'AZN': 944, 'BAM': 977, 'BBD': 52,
1720 'BDT': 50, 'BGN': 975, 'BHD': 48, 'BIF': 108, 'BMD': 60, 'BND': 96, 1849 'BDT': 50, 'BGN': 975, 'BHD': 48, 'BIF': 108, 'BMD': 60, 'BND': 96,
1721 'BOB': 68, 'BOV': 984, 'BRL': 986, 'BSD': 44, 'BTN': 64, 'BWP': 72, 1850 'BOB': 68, 'BOV': 984, 'BRL': 986, 'BSD': 44, 'BTN': 64, 'BWP': 72,
1722 'BYR': 974, 'BZD': 84, 'CAD': 124, 'CDF': 976, 'CHE': 947, 'CHF': 756, 1851 'BYR': 974, 'BZD': 84, 'CAD': 124, 'CDF': 976, 'CHE': 947, 'CHF': 756,
(...skipping 593 matching lines...) Expand 10 before | Expand all | Expand 10 after
2316 yield (self.service_id, date, unicode(exception_type)) 2445 yield (self.service_id, date, unicode(exception_type))
2317 2446
2318 def GetCalendarDatesFieldValuesTuples(self): 2447 def GetCalendarDatesFieldValuesTuples(self):
2319 """Return a list of date execeptions""" 2448 """Return a list of date execeptions"""
2320 result = [] 2449 result = []
2321 for date_tuple in self.GenerateCalendarDatesFieldValuesTuples(): 2450 for date_tuple in self.GenerateCalendarDatesFieldValuesTuples():
2322 result.append(date_tuple) 2451 result.append(date_tuple)
2323 result.sort() # helps with __eq__ 2452 result.sort() # helps with __eq__
2324 return result 2453 return result
2325 2454
2326 def SetDateHasService(self, date, has_service=True): 2455 def SetDateHasService(self, date, has_service=True, problems=None):
2456 if date in self.date_exceptions and problems:
2457 problems.DuplicateID(('service_id', 'date'),
2458 (self.service_id, date),
2459 type=TYPE_WARNING)
2327 self.date_exceptions[date] = has_service and 1 or 2 2460 self.date_exceptions[date] = has_service and 1 or 2
2328 2461
2329 def ResetDateToNormalService(self, date): 2462 def ResetDateToNormalService(self, date):
2330 if date in self.date_exceptions: 2463 if date in self.date_exceptions:
2331 del self.date_exceptions[date] 2464 del self.date_exceptions[date]
2332 2465
2333 def SetStartDate(self, start_date): 2466 def SetStartDate(self, start_date):
2334 """Set the first day of service as a string in YYYYMMDD format""" 2467 """Set the first day of service as a string in YYYYMMDD format"""
2335 self.start_date = start_date 2468 self.start_date = start_date
2336 2469
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
2369 2502
2370 def IsActiveOn(self, date): 2503 def IsActiveOn(self, date):
2371 """Test if this service period is active on a date. 2504 """Test if this service period is active on a date.
2372 2505
2373 Args: 2506 Args:
2374 date: a string of form "YYYYMMDD" 2507 date: a string of form "YYYYMMDD"
2375 2508
2376 Returns: 2509 Returns:
2377 True iff this service is active on date. 2510 True iff this service is active on date.
2378 """ 2511 """
2379 date_obj = DateStringToDateObject(date)
2380 if date in self.date_exceptions: 2512 if date in self.date_exceptions:
2381 if self.date_exceptions[date] == 1: 2513 if self.date_exceptions[date] == 1:
2382 return True 2514 return True
2383 else: 2515 else:
2384 return False 2516 return False
2385 if (self.start_date and self.end_date and self.start_date <= date and 2517 if (self.start_date and self.end_date and self.start_date <= date and
2386 date <= self.end_date): 2518 date <= self.end_date):
2519 date_obj = DateStringToDateObject(date)
2387 return self.day_of_week[date_obj.weekday()] 2520 return self.day_of_week[date_obj.weekday()]
2388 return False 2521 return False
2389 2522
2390 def ActiveDates(self): 2523 def ActiveDates(self):
2391 """Return dates this service period is active as a list of "YYYYMMDD".""" 2524 """Return dates this service period is active as a list of "YYYYMMDD"."""
2392 (earliest, latest) = self.GetDateRange() 2525 (earliest, latest) = self.GetDateRange()
2393 if earliest is None: 2526 if earliest is None:
2394 return [] 2527 return []
2395 dates = [] 2528 dates = []
2396 date_it = DateStringToDateObject(earliest) 2529 date_it = DateStringToDateObject(earliest)
(...skipping 316 matching lines...) Expand 10 before | Expand all | Expand 10 after
2713 2846
2714 ranges = [period.GetDateRange() for period in self.GetServicePeriodList()] 2847 ranges = [period.GetDateRange() for period in self.GetServicePeriodList()]
2715 starts = filter(lambda x: x, [item[0] for item in ranges]) 2848 starts = filter(lambda x: x, [item[0] for item in ranges])
2716 ends = filter(lambda x: x, [item[1] for item in ranges]) 2849 ends = filter(lambda x: x, [item[1] for item in ranges])
2717 2850
2718 if not starts or not ends: 2851 if not starts or not ends:
2719 return (None, None) 2852 return (None, None)
2720 2853
2721 return (min(starts), max(ends)) 2854 return (min(starts), max(ends))
2722 2855
2856 def GetServicePeriodsActiveEachDate(self, date_start, date_end):
2857 """Return a list of tuples (date, [period1, period2, ...]).
2858
2859 For each date in the range [date_start, date_end) make list of each
2860 ServicePeriod object which is active.
2861
2862 Args:
2863 date_start: The first date in the list, a date object
2864 date_end: The first date after the list, a date object
2865
2866 Returns:
2867 A list of tuples. Each tuple contains a date object and a list of zero or
2868 more ServicePeriod objects.
2869 """
2870 date_it = date_start
2871 one_day = datetime.timedelta(days=1)
2872 date_service_period_list = []
2873 while date_it < date_end:
2874 periods_today = []
2875 for service in self.GetServicePeriodList():
2876 if service.IsActiveOn(date_it.strftime("%Y%m%d")):
2877 periods_today.append(service)
2878 date_service_period_list.append((date_it, periods_today))
2879 date_it += one_day
2880 return date_service_period_list
2881
2882
2723 def AddStop(self, lat, lng, name): 2883 def AddStop(self, lat, lng, name):
2724 """Add a stop to this schedule. 2884 """Add a stop to this schedule.
2725 2885
2726 A new stop_id is created for this stop. Do not use this method unless all 2886 A new stop_id is created for this stop. Do not use this method unless all
2727 stops in this Schedule are created with it. See source for details. 2887 stops in this Schedule are created with it. See source for details.
2728 2888
2729 Args: 2889 Args:
2730 lat: Latitude of the stop as a float or string 2890 lat: Latitude of the stop as a float or string
2731 lng: Longitude of the stop as a float or string 2891 lng: Longitude of the stop as a float or string
2732 name: Name of the stop, which will appear in the feed 2892 name: Name of the stop, which will appear in the feed
(...skipping 357 matching lines...) Expand 10 before | Expand all | Expand 10 after
3090 3250
3091 archive.close() 3251 archive.close()
3092 3252
3093 def Validate(self, problems=None, validate_children=True): 3253 def Validate(self, problems=None, validate_children=True):
3094 """Validates various holistic aspects of the schedule 3254 """Validates various holistic aspects of the schedule
3095 (mostly interrelationships between the various data sets).""" 3255 (mostly interrelationships between the various data sets)."""
3096 if not problems: 3256 if not problems:
3097 problems = self.problem_reporter 3257 problems = self.problem_reporter
3098 3258
3099 (start_date, end_date) = self.GetDateRange() 3259 (start_date, end_date) = self.GetDateRange()
3100 if not end_date: 3260 if not end_date or not start_date:
3101 problems.OtherProblem('This feed has no effective service dates!', 3261 problems.OtherProblem('This feed has no effective service dates!',
3102 type=TYPE_WARNING) 3262 type=TYPE_WARNING)
3103 else: 3263 else:
3104 try: 3264 try:
3105 expiration = time.mktime(time.strptime(end_date, "%Y%m%d")) 3265 expiration = time.mktime(time.strptime(end_date, "%Y%m%d"))
3266 start_date_time = time.mktime(time.strptime(start_date, "%Y%m%d"))
3106 now = time.mktime(time.localtime()) 3267 now = time.mktime(time.localtime())
3107 warning_cutoff = now + 60 * 60 * 24 * 30 # one month from expiration 3268 warning_cutoff = now + 60 * 60 * 24 * 30 # one month from expiration
3108 if expiration < warning_cutoff: 3269 if expiration < warning_cutoff:
3109 problems.ExpirationDate(expiration) 3270 problems.ExpirationDate(expiration)
3271 if start_date_time > now:
3272 problems.FutureService(start_date_time)
3110 except ValueError: 3273 except ValueError:
3111 problems.InvalidValue('end_date', end_date) 3274 # Format of start_date and end_date checked in class ServicePeriod
3275 pass
3112 3276
3113 # TODO: Check Trip fields against valid values 3277 # TODO: Check Trip fields against valid values
3114 3278
3115 # Check for stops that aren't referenced by any trips and broken 3279 # Check for stops that aren't referenced by any trips and broken
3116 # parent_station references. 3280 # parent_station references. Also check that the parent station isn't too
3281 # far from its child stops.
3117 for stop in self.stops.values(): 3282 for stop in self.stops.values():
3118 if validate_children: 3283 if validate_children:
3119 stop.Validate(problems) 3284 stop.Validate(problems)
3120 cursor = self._connection.cursor() 3285 cursor = self._connection.cursor()
3121 cursor.execute("SELECT count(*) FROM stop_times WHERE stop_id=? LIMIT 1", 3286 cursor.execute("SELECT count(*) FROM stop_times WHERE stop_id=? LIMIT 1",
3122 (stop.stop_id,)) 3287 (stop.stop_id,))
3123 count = cursor.fetchone()[0] 3288 count = cursor.fetchone()[0]
3124 if stop.location_type == 0 and count == 0: 3289 if stop.location_type == 0 and count == 0:
3125 problems.UnusedStop(stop.stop_id, stop.stop_name) 3290 problems.UnusedStop(stop.stop_id, stop.stop_name)
3126 elif stop.location_type == 1 and count != 0: 3291 elif stop.location_type == 1 and count != 0:
3127 problems.UsedStation(stop.stop_id, stop.stop_name) 3292 problems.UsedStation(stop.stop_id, stop.stop_name)
3128 3293
3129 if stop.location_type != 1 and stop.parent_station: 3294 if stop.location_type != 1 and stop.parent_station:
3130 if stop.parent_station not in self.stops: 3295 if stop.parent_station not in self.stops:
3131 problems.InvalidValue("parent_station", 3296 problems.InvalidValue("parent_station",
3132 EncodeUnicode(stop.parent_station), 3297 EncodeUnicode(stop.parent_station),
3133 "parent_station '%s' not found for stop_id " 3298 "parent_station '%s' not found for stop_id "
3134 "'%s' in stops.txt" % 3299 "'%s' in stops.txt" %
3135 (EncodeUnicode(stop.parent_station), 3300 (EncodeUnicode(stop.parent_station),
3136 EncodeUnicode(stop.stop_id))) 3301 EncodeUnicode(stop.stop_id)))
3137 elif self.stops[stop.parent_station].location_type != 1: 3302 elif self.stops[stop.parent_station].location_type != 1:
3138 problems.InvalidValue("parent_station", 3303 problems.InvalidValue("parent_station",
3139 EncodeUnicode(stop.parent_station), 3304 EncodeUnicode(stop.parent_station),
3140 "parent_station '%s' of stop_id '%s' must " 3305 "parent_station '%s' of stop_id '%s' must "
3141 "have location_type=1 in stops.txt" % 3306 "have location_type=1 in stops.txt" %
3142 (EncodeUnicode(stop.parent_station), 3307 (EncodeUnicode(stop.parent_station),
3143 EncodeUnicode(stop.stop_id))) 3308 EncodeUnicode(stop.stop_id)))
3144 3309 else:
3145 #TODO: check that every station is used and within 1km of stops that are 3310 parent_station = self.stops[stop.parent_station]
3146 # part of it. Then uncomment testStationWithoutReference. 3311 distance = ApproximateDistanceBetweenStops(stop, parent_station)
3312 if distance > MAX_DISTANCE_BETWEEN_STOP_AND_PARENT_STATION_ERROR:
3313 problems.StopTooFarFromParentStation(
3314 stop.stop_id, stop.stop_name, parent_station.stop_id,
3315 parent_station.stop_name, distance, TYPE_ERROR)
3316 elif distance > MAX_DISTANCE_BETWEEN_STOP_AND_PARENT_STATION_WARNING:
3317 problems.StopTooFarFromParentStation(
3318 stop.stop_id, stop.stop_name, parent_station.stop_id,
3319 parent_station.stop_name, distance, TYPE_WARNING)
3320
3321 #TODO: check that every station is used.
3322 # Then uncomment testStationWithoutReference.
3147 3323
3148 # Check for stops that might represent the same location (specifically, 3324 # Check for stops that might represent the same location (specifically,
3149 # stops that are less that 2 meters apart) First filter out stops without a 3325 # stops that are less that 2 meters apart) First filter out stops without a
3150 # valid lat and lon. Then sort by latitude, then find the distance between 3326 # valid lat and lon. Then sort by latitude, then find the distance between
3151 # each pair of stations within 2 meters latitude of each other. This avoids 3327 # each pair of stations within 2 meters latitude of each other. This avoids
3152 # doing n^2 comparisons in the average case and doesn't need a spatial 3328 # doing n^2 comparisons in the average case and doesn't need a spatial
3153 # index. 3329 # index.
3154 sorted_stops = filter(lambda s: s.stop_lat and s.stop_lon, 3330 sorted_stops = filter(lambda s: s.stop_lat and s.stop_lon,
3155 self.GetStopList()) 3331 self.GetStopList())
3156 sorted_stops.sort(key=(lambda x: x.stop_lat)) 3332 sorted_stops.sort(key=(lambda x: x.stop_lat))
(...skipping 29 matching lines...) Expand all
3186 elif stop.location_type == 1 and other_stop.location_type == 0: 3362 elif stop.location_type == 1 and other_stop.location_type == 0:
3187 this_stop = other_stop 3363 this_stop = other_stop
3188 this_station = stop 3364 this_station = stop
3189 if this_stop.parent_station != this_station.stop_id: 3365 if this_stop.parent_station != this_station.stop_id:
3190 problems.OtherProblem( 3366 problems.OtherProblem(
3191 'The parent_station of stop "%s" (ID "%s") is not ' 3367 'The parent_station of stop "%s" (ID "%s") is not '
3192 'station "%s" (ID "%s") but they are only %0.2fm apart.' % 3368 'station "%s" (ID "%s") but they are only %0.2fm apart.' %
3193 (EncodeUnicode(this_stop.stop_name), 3369 (EncodeUnicode(this_stop.stop_name),
3194 EncodeUnicode(this_stop.stop_id), 3370 EncodeUnicode(this_stop.stop_id),
3195 EncodeUnicode(this_station.stop_name), 3371 EncodeUnicode(this_station.stop_name),
3196 EncodeUnicode(this_station.stop_id), distance)) 3372 EncodeUnicode(this_station.stop_id), distance),
3373 type=TYPE_WARNING)
3197 index += 1 3374 index += 1
3198 3375
3199 # Check for multiple routes using same short + long name 3376 # Check for multiple routes using same short + long name
3200 route_names = {} 3377 route_names = {}
3201 for route in self.routes.values(): 3378 for route in self.routes.values():
3202 if validate_children: 3379 if validate_children:
3203 route.Validate(problems) 3380 route.Validate(problems)
3204 short_name = '' 3381 short_name = ''
3205 if not IsEmpty(route.route_short_name): 3382 if not IsEmpty(route.route_short_name):
3206 short_name = route.route_short_name.lower().strip() 3383 short_name = route.route_short_name.lower().strip()
3207 long_name = '' 3384 long_name = ''
3208 if not IsEmpty(route.route_long_name): 3385 if not IsEmpty(route.route_long_name):
3209 long_name = route.route_long_name.lower().strip() 3386 long_name = route.route_long_name.lower().strip()
3210 name = (short_name, long_name) 3387 name = (short_name, long_name)
3211 if name in route_names: 3388 if name in route_names:
3212 problems.InvalidValue('route_long_name', 3389 problems.InvalidValue('route_long_name',
3213 long_name, 3390 long_name,
3214 'The same combination of ' 3391 'The same combination of '
3215 'route_short_name and route_long_name ' 3392 'route_short_name and route_long_name '
3216 'shouldn\'t be used for more than one ' 3393 'shouldn\'t be used for more than one '
3217 'route, as it is for the for the two routes ' 3394 'route, as it is for the for the two routes '
3218 'with IDs "%s" and "%s".' % 3395 'with IDs "%s" and "%s".' %
3219 (route.route_id, route_names[name].route_id), 3396 (route.route_id, route_names[name].route_id),
3220 type=TYPE_WARNING) 3397 type=TYPE_WARNING)
3221 else: 3398 else:
3222 route_names[name] = route 3399 route_names[name] = route
3223 3400
3224 # Check duplicate trips which go through the same stops with same 3401 stop_types = {} # a dict mapping stop_id to [route_id, route_type, is_match]
3225 # service and start times. 3402 trips = {} # a dict mapping tuple to (route_id, trip_id)
3226 3403 for trip in sorted(self.trips.values()):
3227 if self._check_duplicate_trips: 3404 if trip.route_id not in self.routes:
3228 trips = {} 3405 continue
3229 for trip in self.trips.values(): 3406 route_type = self.GetRoute(trip.route_id).route_type
3230 stop_ids = [] 3407 arrival_times = []
3231 stop_times = [] 3408 stop_ids = []
3232 for index, st in enumerate(trip.GetStopTimes(problems)): 3409 for index, st in enumerate(trip.GetStopTimes(problems)):
3233 stop_times.append(st.arrival_time) 3410 stop_id = st.stop.stop_id
3234 stop_ids.append(st.stop.stop_id) 3411 arrival_times.append(st.arrival_time)
3235 if not stop_ids or not stop_times: 3412 stop_ids.append(stop_id)
3413 # Check a stop if which belongs to both subway and bus.
3414 if (route_type == Route._ROUTE_TYPE_NAMES['Subway'] or
3415 route_type == Route._ROUTE_TYPE_NAMES['Bus']):
3416 if stop_id not in stop_types:
3417 stop_types[stop_id] = [trip.route_id, route_type, 0]
3418 elif (stop_types[stop_id][1] != route_type and
3419 stop_types[stop_id][2] == 0):
3420 stop_types[stop_id][2] = 1
3421 if stop_types[stop_id][1] == Route._ROUTE_TYPE_NAMES['Subway']:
3422 subway_route_id = stop_types[stop_id][0]
3423 bus_route_id = trip.route_id
3424 else:
3425 subway_route_id = trip.route_id
3426 bus_route_id = stop_types[stop_id][0]
3427 problems.StopWithMultipleRouteTypes(st.stop.stop_name, stop_id,
3428 subway_route_id, bus_route_id)
3429
3430 # Check duplicate trips which go through the same stops with same
3431 # service and start times.
3432 if self._check_duplicate_trips:
3433 if not stop_ids or not arrival_times:
3236 continue 3434 continue
3237 key = (trip.service_id, min(stop_times), str(stop_ids)) 3435 key = (trip.service_id, min(arrival_times), str(stop_ids))
3238 if key not in trips: 3436 if key not in trips:
3239 trips[key] = [trip.route_id, trip.trip_id] 3437 trips[key] = (trip.route_id, trip.trip_id)
3240 else: 3438 else:
3241 problems.OtherProblem('Trip %s of route %s might be duplicated ' 3439 problems.DuplicateTrip(trips[key][1], trips[key][0], trip.trip_id,
3242 'with trip %s of route %s. They go through ' 3440 trip.route_id)
3243 'the same stops with same service. '
3244 % (trips[key][1], trips[key][0],
3245 trip.trip_id, trip.route_id),
3246 type=TYPE_WARNING)
3247 3441
3248 # Check that routes' agency IDs are valid, if set 3442 # Check that routes' agency IDs are valid, if set
3249 for route in self.routes.values(): 3443 for route in self.routes.values():
3250 if (not IsEmpty(route.agency_id) and 3444 if (not IsEmpty(route.agency_id) and
3251 not route.agency_id in self._agencies): 3445 not route.agency_id in self._agencies):
3252 problems.InvalidValue('agency_id', 3446 problems.InvalidValue('agency_id',
3253 route.agency_id, 3447 route.agency_id,
3254 'The route with ID "%s" specifies agency_id ' 3448 'The route with ID "%s" specifies agency_id '
3255 '"%s", which doesn\'t exist.' % 3449 '"%s", which doesn\'t exist.' %
3256 (route.route_id, route.agency_id)) 3450 (route.route_id, route.agency_id))
(...skipping 463 matching lines...) Expand 10 before | Expand all | Expand 10 after
3720 def _FileContents(self, file_name): 3914 def _FileContents(self, file_name):
3721 results = None 3915 results = None
3722 if self._zip: 3916 if self._zip:
3723 try: 3917 try:
3724 results = self._zip.read(file_name) 3918 results = self._zip.read(file_name)
3725 except KeyError: # file not found in archve 3919 except KeyError: # file not found in archve
3726 self._problems.MissingFile(file_name) 3920 self._problems.MissingFile(file_name)
3727 return None 3921 return None
3728 else: 3922 else:
3729 try: 3923 try:
3730 data_file = open(os.path.join(self._path, file_name), 'r') 3924 data_file = open(os.path.join(self._path, file_name), 'rb')
3731 results = data_file.read() 3925 results = data_file.read()
3732 except IOError: # file not found 3926 except IOError: # file not found
3733 self._problems.MissingFile(file_name) 3927 self._problems.MissingFile(file_name)
3734 return None 3928 return None
3735 3929
3736 if not results: 3930 if not results:
3737 self._problems.EmptyFile(file_name) 3931 self._problems.EmptyFile(file_name)
3738 return results 3932 return results
3739 3933
3740 def _LoadAgencies(self): 3934 def _LoadAgencies(self):
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
3782 periods = {} 3976 periods = {}
3783 3977
3784 # process calendar.txt 3978 # process calendar.txt
3785 if self._HasFile(file_name): 3979 if self._HasFile(file_name):
3786 has_useful_contents = False 3980 has_useful_contents = False
3787 for (row, row_num, cols) in \ 3981 for (row, row_num, cols) in \
3788 self._ReadCSV(file_name, 3982 self._ReadCSV(file_name,
3789 ServicePeriod._FIELD_NAMES, 3983 ServicePeriod._FIELD_NAMES,
3790 ServicePeriod._FIELD_NAMES_REQUIRED): 3984 ServicePeriod._FIELD_NAMES_REQUIRED):
3791 context = (file_name, row_num, row, cols) 3985 context = (file_name, row_num, row, cols)
3986 self._problems.SetFileContext(*context)
3792 3987
3793 period = ServicePeriod(field_list=row) 3988 period = ServicePeriod(field_list=row)
3794 3989
3795 if period.service_id in periods: 3990 if period.service_id in periods:
3796 self._problems.DuplicateID('service_id', period.service_id) 3991 self._problems.DuplicateID('service_id', period.service_id)
3797 continue 3992 else:
3798 3993 periods[period.service_id] = (period, context)
3799 periods[period.service_id] = (period, context) 3994 self._problems.ClearContext()
3800 3995
3801 # process calendar_dates.txt 3996 # process calendar_dates.txt
3802 if self._HasFile(file_name_dates): 3997 if self._HasFile(file_name_dates):
3803 service_id_date_entries = {}
3804 # ['service_id', 'date', 'exception_type'] 3998 # ['service_id', 'date', 'exception_type']
3805 fields = ServicePeriod._FIELD_NAMES_CALENDAR_DATES 3999 fields = ServicePeriod._FIELD_NAMES_CALENDAR_DATES
3806 for (row, row_num, cols) in self._ReadCSV(file_name_dates, 4000 for (row, row_num, cols) in self._ReadCSV(file_name_dates,
3807 fields, fields): 4001 fields, fields):
3808 context = (file_name_dates, row_num, row, cols) 4002 context = (file_name_dates, row_num, row, cols)
4003 self._problems.SetFileContext(*context)
3809 4004
3810 service_id = row[0] 4005 service_id = row[0]
3811 4006
3812 period = None 4007 period = None
3813 if service_id in periods: 4008 if service_id in periods:
3814 period = periods[service_id][0] 4009 period = periods[service_id][0]
3815 else: 4010 else:
3816 period = ServicePeriod(service_id) 4011 period = ServicePeriod(service_id)
3817 periods[period.service_id] = (period, context) 4012 periods[period.service_id] = (period, context)
3818
3819 # Check uniqueness of service_id x date
3820 if (service_id, row[1]) in service_id_date_entries:
3821 self._problems.DuplicateID('service_id x date',
3822 service_id+' X '+row[1],
Tom 2009/06/18 22:17:33 I don't think this should use DuplicateID as is be
jirka 2009/10/22 22:25:58 On 2009/06/18 22:17:33, Tom wrote: > I don't think
3823 type=TYPE_WARNING)
3824 service_id_date_entries[service_id, row[1]] = 1
3825 4013
3826 exception_type = row[2] 4014 exception_type = row[2]
3827 if exception_type == u'1': 4015 if exception_type == u'1':
3828 period.SetDateHasService(row[1], True) 4016 period.SetDateHasService(row[1], True, self._problems)
3829 elif exception_type == u'2': 4017 elif exception_type == u'2':
3830 period.SetDateHasService(row[1], False) 4018 period.SetDateHasService(row[1], False, self._problems)
3831 else: 4019 else:
3832 self._problems.InvalidValue('exception_type', exception_type) 4020 self._problems.InvalidValue('exception_type', exception_type)
4021 self._problems.ClearContext()
3833 4022
3834 # Now insert the periods into the schedule object, so that they're 4023 # Now insert the periods into the schedule object, so that they're
3835 # validated with both calendar and calendar_dates info present 4024 # validated with both calendar and calendar_dates info present
3836 for period, context in periods.values(): 4025 for period, context in periods.values():
3837 self._problems.SetFileContext(*context) 4026 self._problems.SetFileContext(*context)
3838 self._schedule.AddServicePeriodObject(period, self._problems) 4027 self._schedule.AddServicePeriodObject(period, self._problems)
3839 self._problems.ClearContext() 4028 self._problems.ClearContext()
3840 4029
3841 def _LoadShapes(self): 4030 def _LoadShapes(self):
3842 if not self._HasFile('shapes.txt'): 4031 if not self._HasFile('shapes.txt'):
(...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after
4044 def __init__(self, *args, **kwargs): 4233 def __init__(self, *args, **kwargs):
4045 """Initialize a new ShapeLoader object. 4234 """Initialize a new ShapeLoader object.
4046 4235
4047 See Loader.__init__ for argument documentation. 4236 See Loader.__init__ for argument documentation.
4048 """ 4237 """
4049 Loader.__init__(self, *args, **kwargs) 4238 Loader.__init__(self, *args, **kwargs)
4050 4239
4051 def Load(self): 4240 def Load(self):
4052 self._LoadShapes() 4241 self._LoadShapes()
4053 return self._schedule 4242 return self._schedule
LEFTRIGHT

Powered by Google App Engine
RSS Feeds Recent Issues | This issue
This is Rietveld r497