def ValidateRouteShortAndLongNamesAreNotBlank(self, problems): if util.IsEmpty(self.route_short_name) and \ util.IsEmpty(self.route_long_name): problems.InvalidValue( 'route_short_name', self.route_short_name, 'Both route_short_name and ' 'route_long name are blank.')
def ValidateRouteNames(self, problems, validate_children): # Check for multiple routes using same short + long name route_names = {} for route in self.routes.values(): if validate_children: route.Validate(problems) short_name = '' if not util.IsEmpty(route.route_short_name): short_name = route.route_short_name.lower().strip() long_name = '' if not util.IsEmpty(route.route_long_name): long_name = route.route_long_name.lower().strip() name = (short_name, long_name) if name in route_names: problems.InvalidValue('route_long_name', long_name, 'The same combination of ' 'route_short_name and route_long_name ' 'shouldn\'t be used for more than one ' 'route, as it is for the for the two routes ' 'with IDs "%s" and "%s".' % (route.route_id, route_names[name].route_id), type=problems_module.TYPE_WARNING) else: route_names[name] = route
def ValidateStopTimezone(self, problems): # Entrances or other child stops (having a parent station) must not have a # stop_timezone. util.ValidateTimezone(self.stop_timezone, 'stop_timezone', problems) if (not util.IsEmpty(self.parent_station) and not util.IsEmpty(self.stop_timezone)): problems.InvalidValue('stop_timezone', self.stop_timezone, reason='a stop having a parent stop must not have a stop_timezone', type=problems_module.TYPE_WARNING)
def ValidateStopDescriptionAndNameAreDifferent(self, problems): if (self.stop_desc is not None and self.stop_name is not None and self.stop_desc and self.stop_name and not util.IsEmpty(self.stop_desc) and self.stop_name.strip().lower() == self.stop_desc.strip().lower()): problems.InvalidValue('stop_desc', self.stop_desc, 'stop_desc should not be the same as stop_name')
def ValidateTransferType(self, problems): if not util.IsEmpty(self.transfer_type): if (not isinstance(self.transfer_type, int)) or \ (self.transfer_type not in range(0, 4)): problems.InvalidValue('transfer_type', self.transfer_type) return False return True
def ValidateMinimumTransferTime(self, problems): if not util.IsEmpty(self.min_transfer_time): if self.transfer_type != 2: problems.MinimumTransferTimeSetWithInvalidTransferType( self.transfer_type) # If min_transfer_time is negative, equal to or bigger than 24h, issue # an error. If smaller than 24h but bigger than 3h issue a warning. # These errors are not blocking, and should not prevent the transfer # from being added to the schedule. if (isinstance(self.min_transfer_time, int)): if self.min_transfer_time < 0: problems.InvalidValue('min_transfer_time', self.min_transfer_time, reason="This field cannot contain a negative " \ "value.") elif self.min_transfer_time >= 24 * 3600: problems.InvalidValue('min_transfer_time', self.min_transfer_time, reason="The value is very large for a " \ "transfer time and most likely " \ "indicates an error.") elif self.min_transfer_time >= 3 * 3600: problems.InvalidValue('min_transfer_time', self.min_transfer_time, type=problems_module.TYPE_WARNING, reason="The value is large for a transfer " \ "time and most likely indicates " \ "an error.") else: # It has a value, but it is not an integer problems.InvalidValue('min_transfer_time', self.min_transfer_time, reason="If present, this field should contain " \ "an integer value.") return False return True
def ValidateDate(self, date, field_name, problems, context=None): if date is None: # No exception is issued because ServicePeriods can be created using only # calendar_dates.txt. In that case we have a ServicePeriod consisting # entirely of service exceptions, and with no start_date or end_date. return False if util.IsEmpty(date): problems.MissingValue(field_name, date, context) return False elif not util.ValidateDate(date, field_name, problems): return False else: try: date_value = time.strptime(date, "%Y%m%d") if not (self._VALID_DATE_RANGE_FROM <= date_value.tm_year <= self._VALID_DATE_RANGE_TO): problems.DateOutsideValidRange(field_name, date, self._VALID_DATE_RANGE_FROM, self._VALID_DATE_RANGE_TO, context=context) return False return True except ValueError: problems.InvalidValue(field_name, 'Could not parse date value.', date, context, problems_module.TYPE_ERROR) return False
def ValidateServicePeriod(self, problems): if 'service_period' in self.__dict__: # Some tests assign to the service_period attribute. Patch up self before # proceeding with validation. See also comment in Trip.__init__. self.service_id = self.__dict__['service_period'].service_id del self.service_period if util.IsEmpty(self.service_id): problems.MissingValue('service_id')
def ValidateEndDate(self, problems): if self.end_date is not None: if util.IsEmpty(self.end_date): problems.MissingValue('end_date') self.end_date = None elif not self._IsValidDate(self.end_date): problems.InvalidValue('end_date', self.end_date) self.end_date = None
def DeprecatedColumn(self, file_name, column_name, new_name, context=None, type=TYPE_WARNING): reason = None if not util.IsEmpty(new_name): reason = 'Please use the new column "%s" instead.' % (new_name) e = DeprecatedColumn(file_name=file_name, column_name=column_name, reason=reason, context=context, context2=self._context, type=type) self.AddToAccumulator(e)
def ValidateDaysOfWeek(self, problems): if self.original_day_values: index = 0 for value in self.original_day_values: column_name = self._DAYS_OF_WEEK[index] if util.IsEmpty(value): problems.MissingValue(column_name) elif (value != u'0') and (value != '1'): problems.InvalidValue(column_name, value) index += 1
def ValidateRouteAgencyId(self, problems): # Check that routes' agency IDs are valid, if set for route in self.routes.values(): if (not util.IsEmpty(route.agency_id) and not route.agency_id in self._agencies): problems.InvalidValue('agency_id', route.agency_id, 'The route with ID "%s" specifies agency_id ' '"%s", which doesn\'t exist.' % (route.route_id, route.agency_id))
def ValidateDaysOfWeek(self, problems): if self.original_day_values: for column_name, value in self.original_day_values.items(): # check that the day of the week value exists if util.IsEmpty(value): problems.MissingValue(column_name) # and check that it is an expected value elif (value != u'0') and (value != '1'): problems.InvalidValue(column_name, value)
def ValidateExactTimes(self, problems): if util.IsEmpty(self.exact_times): self.exact_times = 0 return try: self.exact_times = int(self.exact_times) except (ValueError, TypeError): problems.InvalidValue('exact_times', self.exact_times, 'Should be 0 (no fixed schedule) or 1 (fixed and ' \ 'regular schedule, shortcut for a repetitive ' \ 'stop_times file).') del self.exact_times return if self.exact_times not in (0, 1): problems.InvalidValue('exact_times', self.exact_times, 'Should be 0 (no fixed schedule) or 1 (fixed and ' \ 'regular schedule, shortcut for a repetitive ' \ 'stop_times file).')
def ValidateTransferWalkingTime(self, problems): if util.IsEmpty(self.min_transfer_time): return if self.min_transfer_time < 0: # Error has already been reported, and it does not make sense # to calculate walking speed with negative times. return distance = self.GetTransferDistance() # If min_transfer_time + 120s isn't enough for someone walking very fast # (2m/s) then issue a warning. # # Stops that are close together (less than 240m appart) never trigger this # warning, regardless of min_transfer_time. FAST_WALKING_SPEED = 2 # 2m/s if self.min_transfer_time + 120 < distance / FAST_WALKING_SPEED: problems.TransferWalkingSpeedTooFast( from_stop_id=self.from_stop_id, to_stop_id=self.to_stop_id, transfer_time=self.min_transfer_time, distance=distance)
def AddFareRuleObject(self, rule, problem_reporter=None): if not problem_reporter: problem_reporter = self.problem_reporter if util.IsEmpty(rule.fare_id): problem_reporter.MissingValue('fare_id') return if rule.route_id and rule.route_id not in self.routes: problem_reporter.InvalidValue('route_id', rule.route_id) if rule.origin_id and rule.origin_id not in self.fare_zones: problem_reporter.InvalidValue('origin_id', rule.origin_id) if rule.destination_id and rule.destination_id not in self.fare_zones: problem_reporter.InvalidValue('destination_id', rule.destination_id) if rule.contains_id and rule.contains_id not in self.fare_zones: problem_reporter.InvalidValue('contains_id', rule.contains_id) if rule.fare_id in self.fares: self.GetFareAttribute(rule.fare_id).rules.append(rule) else: problem_reporter.InvalidValue('fare_id', rule.fare_id, '(This fare_id doesn\'t correspond to any ' 'of the IDs defined in the ' 'fare attributes.)')
def ValidateRouteId(self, problems): if util.IsEmpty(self.route_id): problems.MissingValue('route_id')
def ValidateRequiredFieldNames(self, problems): for required in self._REQUIRED_FIELD_NAMES: if util.IsEmpty(getattr(self, required, None)): problems.MissingValue(required) return True return False
def ValidateRouteTypeIsPresent(self, problems): if util.IsEmpty(self.route_type): problems.MissingValue('route_type')
def ValidateAgencyLang(self, problems): if (not util.IsEmpty(self.agency_lang) and self.agency_lang.lower() not in ISO639.codes_2letter): problems.InvalidValue('agency_lang', self.agency_lang) return True return False
def ValidateToStopIdIsPresent(self, problems): if util.IsEmpty(self.to_stop_id): problems.MissingValue('to_stop_id') return False return True
def ValidateStopRequiredFields(self, problems): for required in self._REQUIRED_FIELD_NAMES: if util.IsEmpty(getattr(self, required, None)): # TODO: For now I'm keeping the API stable but it would be cleaner to # treat whitespace stop_id as invalid, instead of missing problems.MissingValue(required)
def ValidateCurrencyType(self, problems): if util.IsEmpty(self.currency_type): problems.MissingValue("currency_type") elif self.currency_type not in util.ISO4217.codes: problems.InvalidValue("currency_type", self.currency_type)
def ValidateDirectionId(self, problems): if hasattr(self, 'direction_id') and (not util.IsEmpty(self.direction_id)) \ and (self.direction_id != '0') and (self.direction_id != '1'): problems.InvalidValue('direction_id', self.direction_id, 'direction_id must be "0" or "1"')
def ParseAttributes(self, problems): """Parse all attributes, calling problems as needed. Return True if all of the values are valid. """ if util.IsEmpty(self.shape_id): problems.MissingValue('shape_id') return try: if not isinstance(self.shape_pt_sequence, int): self.shape_pt_sequence = \ util.NonNegIntStringToInt(self.shape_pt_sequence, problems) elif self.shape_pt_sequence < 0: problems.InvalidValue('shape_pt_sequence', self.shape_pt_sequence, 'Value should be a number (0 or higher)') except (TypeError, ValueError): problems.InvalidValue('shape_pt_sequence', self.shape_pt_sequence, 'Value should be a number (0 or higher)') return try: if not isinstance(self.shape_pt_lat, (int, float)): self.shape_pt_lat = util.FloatStringToFloat(self.shape_pt_lat, problems) if abs(self.shape_pt_lat) > 90.0: problems.InvalidValue('shape_pt_lat', self.shape_pt_lat) return except (TypeError, ValueError): problems.InvalidValue('shape_pt_lat', self.shape_pt_lat) return try: if not isinstance(self.shape_pt_lon, (int, float)): self.shape_pt_lon = util.FloatStringToFloat(self.shape_pt_lon, problems) if abs(self.shape_pt_lon) > 180.0: problems.InvalidValue('shape_pt_lon', self.shape_pt_lon) return except (TypeError, ValueError): problems.InvalidValue('shape_pt_lon', self.shape_pt_lon) return if abs(self.shape_pt_lat) < 1.0 and abs(self.shape_pt_lon) < 1.0: problems.InvalidValue('shape_pt_lat', self.shape_pt_lat, 'Point location too close to 0, 0, which means ' 'that it\'s probably an incorrect location.', type=problems_module.TYPE_WARNING) return if self.shape_dist_traveled == '': self.shape_dist_traveled = None if (self.shape_dist_traveled is not None and not isinstance(self.shape_dist_traveled, (int, float))): try: self.shape_dist_traveled = \ util.FloatStringToFloat(self.shape_dist_traveled, problems) except (TypeError, ValueError): problems.InvalidValue('shape_dist_traveled', self.shape_dist_traveled, 'This value should be a positive number.') return if self.shape_dist_traveled is not None and self.shape_dist_traveled < 0: problems.InvalidValue('shape_dist_traveled', self.shape_dist_traveled, 'This value should be a positive number.') return return True
def ValidateServiceId(self, problems): if util.IsEmpty(self.service_id): problems.MissingValue('service_id')
def ValidateTripId(self, problems): if util.IsEmpty(self.trip_id): problems.MissingValue('trip_id')
def ValidateStopRequiredFields(self, problems): for required in self._REQUIRED_FIELD_NAMES: if util.IsEmpty(getattr(self, required, None)): self._ReportMissingRequiredField(problems, required)
def ValidateShapeId(self, problems): if util.IsEmpty(self.shape_id): problems.MissingValue('shape_id')
def ValidateTrips(self, problems): stop_types = {} # a dict mapping stop_id to [route_id, route_type, is_match] trips = {} # a dict mapping tuple to (route_id, trip_id) # a dict mapping block_id to a list of tuple of # (trip_id, first_arrival_secs, last_arrival_secs) trip_intervals_by_block_id = defaultdict(lambda: []) for trip in sorted(self.trips.values()): if trip.route_id not in self.routes: continue route_type = self.GetRoute(trip.route_id).route_type stop_ids = [] stop_times = trip.GetStopTimes(problems) for index, st in enumerate(stop_times): stop_id = st.stop.stop_id stop_ids.append(stop_id) # Check a stop if which belongs to both subway and bus. if (route_type == self._gtfs_factory.Route._ROUTE_TYPE_NAMES['Subway'] or route_type == self._gtfs_factory.Route._ROUTE_TYPE_NAMES['Bus']): if stop_id not in stop_types: stop_types[stop_id] = [trip.route_id, route_type, 0] elif (stop_types[stop_id][1] != route_type and stop_types[stop_id][2] == 0): stop_types[stop_id][2] = 1 if stop_types[stop_id][1] == \ self._gtfs_factory.Route._ROUTE_TYPE_NAMES['Subway']: subway_route_id = stop_types[stop_id][0] bus_route_id = trip.route_id else: subway_route_id = trip.route_id bus_route_id = stop_types[stop_id][0] problems.StopWithMultipleRouteTypes(st.stop.stop_name, stop_id, subway_route_id, bus_route_id) # We only care about trips with a block id if not util.IsEmpty(trip.block_id) and stop_times: first_arrival_secs = stop_times[0].arrival_secs last_departure_secs = stop_times[-1].departure_secs # The arrival and departure time of the first and last stop_time # SHOULD be set, but we need to handle the case where we're given # an invalid feed anyway if first_arrival_secs is not None and last_departure_secs is not None: # Create a trip interval tuple of the trip id and arrival time # intervals key = trip.block_id trip_intervals = trip_intervals_by_block_id[key] trip_interval = (trip, first_arrival_secs, last_departure_secs) trip_intervals.append(trip_interval) # Check duplicate trips which go through the same stops with same # service and start times. if self._check_duplicate_trips: if not stop_ids or not stop_times: continue key = (trip.service_id, stop_times[0].arrival_time, str(stop_ids)) if key not in trips: trips[key] = (trip.route_id, trip.trip_id) else: problems.DuplicateTrip(trips[key][1], trips[key][0], trip.trip_id, trip.route_id) # Now that we've generated our block trip intervls, we can check for # overlaps in the intervals self.ValidateBlocks(problems, trip_intervals_by_block_id)