예제 #1
0
파일: model.py 프로젝트: benoit-bst/kirin
    def get_local_circulation_date(self):
        from kirin.utils import get_timezone

        if self.navitia_vj is None:
            return None

        first_stop_time = self.navitia_vj.get('stop_times', [{}])[0]
        tzinfo = get_timezone(first_stop_time)
        return self.get_start_timestamp().astimezone(tzinfo).date()
예제 #2
0
파일: model.py 프로젝트: benoit-bst/kirin
    def __init__(self, navitia_vj, local_circulation_date):
        from kirin.utils import get_timezone

        self.id = gen_uuid()
        if 'trip' in navitia_vj and 'id' in navitia_vj['trip']:
            self.navitia_trip_id = navitia_vj['trip']['id']

        first_stop_time = navitia_vj.get('stop_times', [{}])[0]
        start_time = first_stop_time['arrival_time']
        if start_time is None:
            start_time = first_stop_time['departure_time']
        tzinfo = get_timezone(first_stop_time)
        self.start_timestamp = tzinfo.localize(
            datetime.datetime.combine(local_circulation_date,
                                      start_time)).astimezone(utc)
        self.navitia_vj = navitia_vj  # Not persisted
예제 #3
0
파일: handler.py 프로젝트: benoit-bst/kirin
def merge(navitia_vj, db_trip_update, new_trip_update, is_new_complete=False):
    """
    We need to merge the info from 3 sources:
        * the navitia base schedule
        * the trip update already in the db (potentially nonexistent)
        * the incoming trip update

    The result is either the db_trip_update if it exists, or the new_trip_update (it is updated as a side
    effect)

    The mechanism is quite simple:
        * the result trip status is the new_trip_update's status
            (ie in the db the trip was cancelled, and a new update is only an update, the trip update is
            not cancelled anymore, only updated)

        * for each navitia's stop_time and for departure|arrival:
            - if there is an update on this stoptime (in new_trip_update):
                we compute the new datetime based on the new information and the navitia's base schedule
            - else if there is the stoptime in the db:
                we keep this db stoptime
            - else we keep the navitia's base schedule

    Note that the results is either 'db_trip_update' or 'new_trip_update'. Side effects on this object are
    thus wanted because of database persistency (update or creation of new objects)

    If is_new_complete==True, then new_trip_update is considered as a complete trip, so it will erase and
    replace the (old) db_trip_update.
    Detail: is_new_complete changes the way None is interpreted in the new_trip_update:
        - if is_new_complete==False, None means there is no new information, so we keep old ones
        - if is_new_complete==True, None means we are back to normal, so we keep the new None
          (for now it only impacts messages to allow removal)


    ** Important Note **:
    we DO NOT HANDLE changes in navitia's schedule for the moment
    it will need to be handled, but it will be done after
    """
    res = db_trip_update if db_trip_update else new_trip_update
    res_stoptime_updates = []

    res.status = new_trip_update.status
    if new_trip_update.message is not None or is_new_complete:
        res.message = new_trip_update.message
    res.contributor = new_trip_update.contributor

    if res.status == 'delete':
        # for trip cancellation, we delete all stoptimes update
        res.stop_time_updates = []
        return res

    last_nav_dep = None
    last_departure = None
    local_circulation_date = new_trip_update.vj.get_local_circulation_date()

    has_changes = False
    for nav_order, navitia_stop in enumerate(navitia_vj.get('stop_times', [])):
        # TODO handle forbidden pickup/dropoff (in those case set departure/arrival at None)
        nav_departure_time = navitia_stop.get('departure_time')
        nav_arrival_time = navitia_stop.get('arrival_time')
        timezone = get_timezone(navitia_stop)

        # we compute the arrival time and departure time on base schedule and take past mid-night into
        # consideration
        base_arrival = base_departure = None
        if nav_arrival_time is not None:
            if last_nav_dep is not None and last_nav_dep > nav_arrival_time:
                # last departure is after arrival, it's a past-midnight
                local_circulation_date += timedelta(days=1)
            base_arrival = _get_datetime(local_circulation_date, nav_arrival_time, timezone)

        if nav_departure_time is not None:
            if nav_arrival_time is not None and nav_arrival_time > nav_departure_time:
                # departure is before arrival, it's a past-midnight
                local_circulation_date += timedelta(days=1)
            base_departure = _get_datetime(local_circulation_date, nav_departure_time, timezone)

        stop_id = navitia_stop.get('stop_point', {}).get('id')
        new_st = new_trip_update.find_stop(stop_id, nav_order)

        if db_trip_update is not None and new_st is not None:
            """
            First case: we already have recorded the delay and we find update info in the new trip update
            Then      : we should probably update it or not if the input info is exactly the same as the one in db
            """
            db_st = db_trip_update.find_stop(stop_id, nav_order)
            new_st_update = _make_stop_time_update(base_arrival,
                                                   base_departure,
                                                   last_departure,
                                                   new_st,
                                                   navitia_stop['stop_point'],
                                                   order=nav_order)
            has_changes |= (db_st is None) or db_st.is_ne(new_st_update)
            res_st = new_st_update if has_changes else db_st

        elif db_trip_update is None and new_st is not None:
            """
            Second case: we have not yet recorded the delay
            Then       : it's time to create one in the db
            """
            has_changes = True
            res_st = _make_stop_time_update(base_arrival,
                                            base_departure,
                                            last_departure,
                                            new_st,
                                            navitia_stop['stop_point'],
                                            order=nav_order)
            res_st.message = new_st.message

        elif db_trip_update is not None and new_st is None:
            """
            Third case: we have already recorded a delay but nothing is mentioned in the new trip update
            Then      : For IRE, we do nothing but only update stop time's order
                        For gtfs-rt, according to the specification, we should use the delay from the previous
                        stop time, which will be handled sooner by the connector-specified model maker
                        
                        *** Here, we MUST NOT do anything, only update stop time's order ***
            """
            db_st = db_trip_update.find_stop(stop_id, nav_order)
            res_st = db_st if db_st is not None else StopTimeUpdate(navitia_stop['stop_point'],
                                                                    departure=base_departure,
                                                                    arrival=base_arrival,
                                                                    order=nav_order)
            has_changes |= (db_st is None)
        else:
            """
            Last case: nothing is recorded yet and there is no update info in the new trip update
            Then     : take the base schedule's arrival/departure time and let's create a whole new world!
            """
            has_changes = True
            res_st = StopTimeUpdate(navitia_stop['stop_point'],
                                    departure=base_departure,
                                    arrival=base_arrival,
                                    order=nav_order)

        last_departure = res_st.departure
        res_stoptime_updates.append(res_st)
        last_nav_dep = nav_departure_time

    if has_changes:
        res.stop_time_updates = res_stoptime_updates
        return res

    return None
예제 #4
0
파일: model_maker.py 프로젝트: prhod/kirin
    def _make_db_vj(self, vj_source_code, since, until):
        navitia_vjs = self.navitia.vehicle_journeys(
            q={
                'filter':
                'vehicle_journey.has_code({}, {})'.format(
                    self.stop_code_key, vj_source_code),
                'since':
                to_str(since),
                'until':
                to_str(until),
                'depth':
                '2',  # we need this depth to get the stoptime's stop_area
            })

        if not navitia_vjs:
            self.log.info('impossible to find vj {t} on [{s}, {u}]'.format(
                t=vj_source_code, s=since, u=until))
            record_internal_failure('missing vj', contributor=self.contributor)
            return []

        if len(navitia_vjs) > 1:
            vj_ids = [vj.get('id') for vj in navitia_vjs]
            self.log.info(
                'too many vjs found for {t} on [{s}, {u}]: {ids}'.format(
                    t=vj_source_code, s=since, u=until, ids=vj_ids))
            record_internal_failure('duplicate vjs',
                                    contributor=self.contributor)
            return []

        nav_vj = navitia_vjs[0]

        # Now we compute the real circulate_date of VJ from since, until and vj's first stop_time
        # We do this to prevent cases like pass midnight when [since, until] is too large
        # we need local timezone circulate_date (and it's sometimes different from UTC date)
        first_stop_time = nav_vj.get('stop_times', [{}])[0]
        tzinfo = get_timezone(first_stop_time)

        # 'since' and 'until' must have a timezone before being converted to local timezone
        local_since = pytz.utc.localize(since).astimezone(tzinfo)
        local_until = pytz.utc.localize(until).astimezone(tzinfo)

        circulate_date = None

        if local_since.date() == local_until.date():
            circulate_date = local_since.date()
        else:
            arrival_time = first_stop_time['arrival_time']
            # At first, we suppose that the circulate_date is local_since's date
            if local_since <= tzinfo.localize(
                    datetime.datetime.combine(local_since.date(),
                                              arrival_time)) <= local_until:
                circulate_date = local_since.date()
            elif local_since <= tzinfo.localize(
                    datetime.datetime.combine(local_until.date(),
                                              arrival_time)) <= local_until:
                circulate_date = local_until.date()

        if circulate_date is None:
            self.log.error(
                'impossible to calculate the circulate date (local) of vj: {}'.
                format(nav_vj.get('id')))
            record_internal_failure(
                'impossible to calculate the circulate date of vj',
                contributor=self.contributor)
            return []

        try:
            vj = model.VehicleJourney(nav_vj, circulate_date)
            return [vj]
        except Exception as e:
            self.log.exception(
                'Error while creating kirin VJ of {}: {}'.format(
                    nav_vj.get('id'), e))
            record_internal_failure('Error while creating kirin VJ',
                                    contributor=self.contributor)
            return []