示例#1
0
def _get_piv_higher_trip_disruption(piv_disruptions,
                                    piv_data_freshness_source):
    """
    Get (or build) the single reference tag from "evenement" list (the most important)
    :param piv_disruptions: list of json objects for tag "evenement"
    :param piv_data_freshness_source: value of json attribute "planTransportSource"
    :return: reference object "evenement" to be considered everywhere
    """
    if not piv_disruptions:
        if piv_data_freshness_source in ["PTP", "OPE"]:
            return ujson.loads('{"type": "CREATION", "texte": ""}')
        else:
            raise UnsupportedValue(
                "planTransportSource {} is not supported".format(
                    piv_data_freshness_source))

    higher_trip_disruption = ujson.loads('{"type": "UNDEFINED", "texte": ""}')
    for piv_disruption in piv_disruptions:
        piv_disruption_type = get_value(piv_disruption, "type", nullable=True)
        if piv_disruption_type and piv_disruption_type in trip_piv_status_to_effect:
            higher_trip_disruption = (
                piv_disruption
                if _get_trip_effect_order_from_piv_status(piv_disruption_type)
                < _get_trip_effect_order_from_piv_status(
                    get_value(higher_trip_disruption, "type")) else
                higher_trip_disruption)
    if trip_piv_status_to_effect.get(
            higher_trip_disruption.get("type")) is None:
        raise UnsupportedValue(
            "None of the disruption-types {} are supported".format(
                piv_disruptions))

    return higher_trip_disruption
示例#2
0
def _is_fully_added_stop(ad):
    """
    Check if an arretDesserte object has any event that was present in base-schedule
    (and return the opposite boolean)
    """
    for event_toggle in ["arrivee", "depart"]:
        event = get_value(ad, event_toggle, nullable=True)
        if event and get_value(event, "planTransportSource",
                               nullable=True) not in ["OPE", "PTP"]:
            return False
    return True
示例#3
0
def _get_first_stop_base_datetime(list_ads,
                                  hour_obj_name,
                                  skip_fully_added_stops=True):
    if skip_fully_added_stops:
        s = next((s for s in list_ads if not _is_fully_added_stop(s)), None)
    else:
        s = next((s for s in list_ads), None)

    str_time = get_value(get_value(s, hour_obj_name),
                         "dateHeure") if s else None
    return str_to_utc_naive_dt(str_time) if str_time else None
示例#4
0
def _get_first_stop_datetime(list_pdps,
                             hour_obj_name,
                             skip_fully_added_stops=True):
    if skip_fully_added_stops:
        p = next((p for p in list_pdps if not _is_fully_added_pdp(p)), None)
    else:
        p = next((p for p in list_pdps), None)

    str_time = get_value(get_value(p, hour_obj_name),
                         "dateHeure") if p else None
    return str_to_utc_naive_dt(str_time) if str_time else None
示例#5
0
def _get_message(arret):
    arrival_stop = get_value(arret, "arrivee", nullable=True)
    departure_stop = get_value(arret, "depart", nullable=True)
    motif = None
    if departure_stop:
        motif = get_value(departure_stop, "motifModification", nullable=True)
    if not motif and arrival_stop:
        motif = get_value(arrival_stop, "motifModification", nullable=True)
    if not motif and departure_stop:
        motif = departure_stop.get("evenement", {}).get("texte", None)
    if not motif and arrival_stop:
        motif = arrival_stop.get("evenement", {}).get("texte", None)
    return motif
示例#6
0
def _retrieve_projected_time(source_ref, list_proj_time):
    """
    pick the good projected arrival/departure objects from the list provided,
    using the source-reference if existing
    """
    # if a source-reference is defined
    if source_ref is not None:
        # retrieve the one mentioned if it exists
        if list_proj_time:
            for p in list_proj_time:
                s = get_value(p, "source", nullable=True)
                if s is not None and s == source_ref:
                    return p

        # if a reference is provided but impossible to retrieve corresponding element, reject whole COTS feed
        raise InvalidArguments(
            'invalid json, impossible to find source "{s}" in any json dict '
            "of list: {list}".format(s=source_ref,
                                     list=ujson.dumps(list_proj_time)))

    elif list_proj_time:
        # if no source-reference exists, but only one element in the list, we take it
        if len(list_proj_time) == 1:
            return list_proj_time[0]
        # if list has multiple elements but no source-reference, reject whole COTS feed
        raise InvalidArguments(
            "invalid json, impossible no source but multiple json dicts "
            "in list: {list}".format(list=ujson.dumps(list_proj_time)))

    return None
示例#7
0
def _retrieve_stop_event_delay(pdp, arrival_departure_toggle):
    cots_ref_planned = get_value(
        pdp,
        "sourceHoraireProjete{}Reference".format(arrival_departure_toggle),
        nullable=True)
    cots_planned_stop_times = get_value(
        pdp,
        "listeHoraireProjete{}".format(arrival_departure_toggle),
        nullable=True)
    cots_planned_stop_time = _retrieve_projected_time(cots_ref_planned,
                                                      cots_planned_stop_times)

    if cots_planned_stop_time is not None:
        delay = get_value(cots_planned_stop_time, "pronosticIV", nullable=True)
        return as_duration(delay)

    return None
示例#8
0
def is_station(pdp):
    """
    determine if a Point de Parcours is a legit station
    :param pdp: stop_time to be checked
    :return: True if pdp is a station, False otherwise
    """
    t = get_value(pdp, "typeArret", nullable=True)
    return (t is None) or (t in ["", "CD", "CH", "FD", "FH"])
示例#9
0
def _retrieve_interesting_pdp(list_pdp):
    """
    Filter and sort "Points de Parcours" (corresponding to stop_times in Navitia) to get only the relevant
    ones from a Navitia's perspective (stations, where travelers can hop on or hop off)
    Context: COTS may contain operating informations, useless for traveler
    :param list_pdp: an array of "Point de Parcours" (typically the one from the feed)
    :return: Filtered array
    Notes:  - written in a yield-fashion to switch implementation if possible, but we need random access for now
            - see 'test_retrieve_interesting_pdp' for a functional example
    """
    res = []
    picked_one = False
    sorted_list_pdp = sorted(list_pdp, key=itemgetter("rang"))
    for idx, pdp in enumerate(sorted_list_pdp):
        # At start, do not consume until there's a departure time (horaireVoyageurDepart)
        if not picked_one and not get_value(
                pdp, "horaireVoyageurDepart", nullable=True):
            continue
        # exclude stop_times that are not legit stations
        if not is_station(pdp):
            continue
        # exclude stop_times that have no departure nor arrival time (empty stop_times)
        if not get_value(pdp, "horaireVoyageurDepart",
                         nullable=True) and not get_value(
                             pdp, "horaireVoyageurArrivee", nullable=True):
            continue
        # stop consuming once all following stop_times are missing arrival time
        # * if a stop_time only has departure time, travelers can only hop in, but if they are be able to
        #   hop off later because some stop_time has arrival time then the current stop_time is useful,
        #   so we keep current stop_time.
        # * if no stop_time has arrival time anymore, then stop_times are useless as traveler cannot
        #   hop off, so no point hopping in anymore, so we remove all the stop_times until the end
        #   (should not happen in practice).
        if not get_value(pdp, "horaireVoyageurArrivee", nullable=True):
            has_following_arrival = any(
                get_value(follow_pdp, "horaireVoyageurArrivee", nullable=True)
                and is_station(follow_pdp)
                for follow_pdp in sorted_list_pdp[idx:])
            if not has_following_arrival:
                break

        picked_one = True
        res.append(pdp)
    return res
示例#10
0
def _is_fully_added_pdp(pdp):
    """
    Check if a projected arrival/departure object is fully created
    """
    # retrieve expressed statuses
    dep_arr_statuses = []
    for arrival_departure_toggle in ["Arrivee", "Depart"]:
        cots_traveler_time = get_value(
            pdp,
            "horaireVoyageur{}".format(arrival_departure_toggle),
            nullable=True)
        if cots_traveler_time:
            dep_arr_statuses.append(
                get_value(cots_traveler_time,
                          "statutCirculationOPE",
                          nullable=True))

    # if there are expressed cots_traveler_times and all are 'CREATION', pdp is fully added
    return dep_arr_statuses and all(s == "CREATION" for s in dep_arr_statuses)
示例#11
0
    def _get_navitia_stop_point(self, arret, nav_vj):
        """
        Get a navitia stop point from the stop_time in a 'Point de Parcours' dict.
        The dict MUST contain cr, ci, ch tags.
        It searches in the vj's stops for a stop_area with the external code cr-ci-ch

        If the stop_time isn't found in the vj, in case of an additional stop_time,
        a request is made to Navitia.

        Error messages are also returned as 'missing stop point', 'duplicate stops'
        """
        uic8 = get_value(get_value(arret, "emplacement"), "code")
        nav_st, log_dict = _extract_navitia_stop_time(uic8, nav_vj)
        if not nav_st:
            nav_stop, req_log_dict = self._request_navitia_stop_point(uic8)
            log_dict.update(req_log_dict)
        else:
            nav_stop = nav_st.get("stop_point", None)
        return nav_stop, log_dict
示例#12
0
def _get_action_on_trip(train_numbers, dict_version, pdps):
    """
    Verify if trip in cots feed is a newly added one and check possible actions
    :param dict_version: Value of attribut nouvelleVersion in cots feed
    :return: NOT_ADDED if the trip is not added by current feed,
        FIRST_TIME_ADDED if it's the first time this trip is added,
        PREVIOUSLY_ADDED if this train is already added (present in db)
    """
    cots_trip_status = get_value(dict_version, "statutOperationnel",
                                 TripStatus.PERTURBEE.name)

    # We have to verify if the trip exists in database
    vj_start = _get_first_stop_datetime(pdps,
                                        "horaireVoyageurDepart",
                                        skip_fully_added_stops=False)
    vj_end = _get_first_stop_datetime(reversed(pdps),
                                      "horaireVoyageurArrivee",
                                      skip_fully_added_stops=False)
    train_id = TRAIN_ID_FORMAT.format(train_numbers)
    trip_added_in_db = model.TripUpdate.find_vj_by_period(
        train_id,
        start_date=vj_start - SNCF_SEARCH_MARGIN,
        end_date=vj_end + SNCF_SEARCH_MARGIN)

    action_on_trip = ActionOnTrip.NOT_ADDED.name
    if trip_added_in_db:
        # Raise exception on forbidden / inconsistent actions
        # No addition multiple times
        # No update or delete on trip already deleted.
        if cots_trip_status == TripStatus.AJOUTEE.name and trip_added_in_db.status == ModificationType.add.name:
            raise InvalidArguments(
                "Invalid action, trip {} can not be added multiple times".
                format(train_numbers))
        elif (cots_trip_status != TripStatus.AJOUTEE.name
              and trip_added_in_db.status == ModificationType.delete.name):
            raise InvalidArguments(
                "Invalid action, trip {} already deleted in database".format(
                    train_numbers))

        # Trip added then deleted followed by add should be handled as FIRST_TIME_ADDED
        if (cots_trip_status == TripStatus.AJOUTEE.name
                and trip_added_in_db.status == ModificationType.delete.name):
            action_on_trip = ActionOnTrip.FIRST_TIME_ADDED.name
        # Trip already added should be handled as PREVIOUSLY_ADDED
        elif trip_added_in_db.status == ModificationType.add.name:
            action_on_trip = ActionOnTrip.PREVIOUSLY_ADDED.name

    else:
        if cots_trip_status == TripStatus.AJOUTEE.name:
            action_on_trip = ActionOnTrip.FIRST_TIME_ADDED.name
    return action_on_trip
示例#13
0
    def build_trip_updates(self, rt_update):
        """
        parse the COTS raw json stored in the rt_update object (in Kirin db)
        and return a list of trip updates

        The TripUpdates are not yet associated with the RealTimeUpdate

        Most of the realtime information parsed is contained in the 'nouvelleVersion' sub-object
        (see fixtures and documentation)
        """
        try:
            json = ujson.loads(rt_update.raw_data)
        except ValueError as e:
            raise InvalidArguments("invalid json: {}".format(str(e)))

        if "nouvelleVersion" not in json:
            raise InvalidArguments(
                'No object "nouvelleVersion" available in feed provided')

        dict_version = get_value(json, "nouvelleVersion")
        train_numbers = get_value(dict_version, "numeroCourse")
        pdps = _retrieve_interesting_pdp(
            get_value(dict_version, "listePointDeParcours"))
        if len(pdps) < 2:
            raise InvalidArguments(
                'invalid json, "listePointDeParcours" has less than 2 valid stop_times in '
                "json elt {elt}".format(elt=ujson.dumps(dict_version)))

        action_on_trip = _get_action_on_trip(train_numbers, dict_version, pdps)
        vjs = self._get_vjs(train_numbers, pdps, action_on_trip=action_on_trip)
        trip_updates = [
            self._make_trip_update(dict_version,
                                   vj,
                                   action_on_trip=action_on_trip) for vj in vjs
        ]

        log_dict = {}
        return trip_updates, log_dict
示例#14
0
    def _get_navitia_stop_point(self, pdp, nav_vj):
        """
        Get a navitia stop point from the stop_time in a 'Point de Parcours' dict.
        The dict MUST contain cr, ci, ch tags.
        It searches in the vj's stops for a stop_area with the external code cr-ci-ch

        If the stop_time isn't found in the vj, in case of an additional stop_time,
        a request is made to Navitia.

        Error messages are also returned as 'missing stop point', 'duplicate stops'
        """
        nav_st, log_dict = get_navitia_stop_time_sncf(cr=get_value(pdp, "cr"),
                                                      ci=get_value(pdp, "ci"),
                                                      ch=get_value(pdp, "ch"),
                                                      nav_vj=nav_vj)
        if not nav_st:
            nav_stop, log_dict = self._request_navitia_stop_point(
                cr=get_value(pdp, "cr"),
                ci=get_value(pdp, "ci"),
                ch=get_value(pdp, "ch"))
        else:
            nav_stop = nav_st.get("stop_point", None)
        return nav_stop, log_dict
示例#15
0
    def _make_trip_update(self, json_train, vj):
        trip_update = model.TripUpdate(vj=vj,
                                       contributor_id=self.contributor.id)
        trip_update.headsign = json_train.get("numero")
        company_id = json_train.get("operateur").get("codeOperateur")
        trip_update.company_id = self._get_navitia_company_id(company_id)
        physical_mode = json_train.get("modeTransport").get("typeMode")
        trip_update.physical_mode_id = self._get_navitia_physical_mode_id(
            physical_mode)
        trip_status_type = trip_piv_status_to_effect.get(
            json_train.get("evenement").get("type"), TripEffect.UNDEFINED)
        trip_update.message = json_train.get("evenement").get("texte")
        trip_update.effect = trip_status_type.name
        if trip_status_type == TripEffect.NO_SERVICE:
            # the whole trip is deleted
            trip_update.status = ModificationType.delete.name
            trip_update.stop_time_updates = []
            return trip_update
        elif trip_status_type == TripEffect.ADDITIONAL_SERVICE:
            trip_update.status = ModificationType.add.name
        else:
            trip_update.status = ModificationType.update.name

        highest_st_status = ModificationType.none.name
        ads = get_value(get_value(json_train, "listeArretsDesserte"), "arret")

        # this variable is used to store the last stop_time's departure in order to check the stop_time consistency
        # ex. stop_time[i].arrival/departure must be greater than stop_time[i-1].departure
        previous_rt_stop_event_time = datetime.datetime.utcfromtimestamp(0)
        for arret in ads:
            # retrieve navitia's stop_point corresponding to the current PIV ad
            nav_stop, log_dict = self._get_navitia_stop_point(
                arret, vj.navitia_vj)

            if log_dict:
                record_internal_failure(log_dict["log"],
                                        contributor=self.contributor.id)
                log_dict.update({"contributor": self.contributor.id})
                logging.getLogger(__name__).info("metrology", extra=log_dict)

            if nav_stop is None:
                continue  # simply skip stop_times at unknown stop areas

            st_update = model.StopTimeUpdate(nav_stop)
            trip_update.stop_time_updates.append(st_update)

            st_update.message = _get_message(arret)
            for event_toggle in ["arrivee", "depart"]:
                event = get_value(arret, event_toggle, nullable=True)
                if event is None:
                    continue

                piv_disruption = event.get("evenement", {})
                piv_event_status = event.get("statutModification",
                                             piv_disruption.get("type", None))

                if not piv_event_status or piv_event_status in MANAGED_STOP_EVENTS:
                    piv_event_datetime = get_value(event,
                                                   "dateHeureReelle",
                                                   nullable=True)
                    event_datetime = str_to_utc_naive_dt(
                        piv_event_datetime) if piv_event_datetime else None
                    if event_datetime:
                        setattr(st_update,
                                STOP_EVENT_DATETIME_MAP[event_toggle],
                                event_datetime)

                    if piv_event_status in ["SUPPRESSION_PARTIELLE"]:
                        setattr(st_update, STATUS_MAP[event_toggle],
                                ModificationType.delete.name)
                    elif piv_event_status in ["SUPPRESSION_DETOURNEMENT"]:
                        setattr(st_update, STATUS_MAP[event_toggle],
                                ModificationType.deleted_for_detour.name)
                    elif piv_event_status in ["CREATION"]:
                        setattr(st_update, STATUS_MAP[event_toggle],
                                ModificationType.add.name)
                    elif piv_event_status in ["CREATION_DETOURNEMENT"]:
                        setattr(st_update, STATUS_MAP[event_toggle],
                                ModificationType.added_for_detour.name)
                    elif trip_status_type == TripEffect.ADDITIONAL_SERVICE:
                        # In this case we want to create schedules taking into account the delay
                        # without adding the delay a second time
                        setattr(st_update, STATUS_MAP[event_toggle],
                                ModificationType.add.name)
                    elif piv_event_status in [
                            "RETARD_OBSERVE", "RETARD_PROJETE"
                    ]:
                        setattr(st_update, STATUS_MAP[event_toggle],
                                ModificationType.update.name)
                        if piv_disruption:
                            piv_event_delay = piv_disruption.get(
                                "retard", {}).get("duree", 0)
                            setattr(st_update, DELAY_MAP[event_toggle],
                                    as_duration(piv_event_delay *
                                                60))  # minutes
                    else:
                        setattr(st_update, STATUS_MAP[event_toggle],
                                ModificationType.none.name)
                    # otherwise let those be none

                else:
                    raise InvalidArguments(
                        "invalid value {s} for field {t}/statutModification or {t}/evenement/type"
                        .format(s=piv_event_status, t=event_toggle))

                event_status = getattr(st_update, STATUS_MAP[event_toggle],
                                       ModificationType.none.name)
                highest_st_status = get_higher_status(highest_st_status,
                                                      event_status)

                mdi = (arret[STOP_EVENT_MDI_MAP[event_toggle]] if arret.get(
                    STOP_EVENT_MDI_MAP[event_toggle]) else False)
                if _is_stop_event_served(event_datetime, event_status, mdi):
                    if previous_rt_stop_event_time > event_datetime:
                        raise InvalidArguments(
                            "invalid feed: stop_point's({}) time is not consistent"
                            .format(
                                get_value(get_value(arret, "emplacement"),
                                          "code")))
                    previous_rt_stop_event_time = event_datetime

        # Calculates effect from stop_time status list (this work is also done in kraken and has to be deleted there)
        if trip_update.effect == TripEffect.MODIFIED_SERVICE.name:
            trip_update.effect = get_effect_from_modification_type(
                highest_st_status)
        return trip_update
示例#16
0
    def build_trip_updates(self, rt_update):
        """
        parse the PIV raw json stored in the rt_update object (in Kirin db)
        and return a list of trip updates

        The TripUpdates are not associated with the RealTimeUpdate at this point
        """
        log_dict = {}
        try:
            json = ujson.loads(rt_update.raw_data)
        except ValueError as e:
            raise InvalidArguments("invalid json: {}".format(str(e)))

        dict_objects = get_value(json, "objects")
        json_train = get_value(
            dict_objects[0],
            "object")  # TODO: can we get more than 1 relevant in objects[]?
        brand_code = json_train.get("marque", {}).get("code")
        if brand_code in ["TN", "TNRER"]:
            raise InvalidArguments(
                '"{}" marque code is not supported'.format(brand_code))
        piv_disruptions = get_value(json_train, "evenement", nullable=True)
        plan_transport_source = get_value(json_train,
                                          "planTransportSource",
                                          nullable=True)

        if not piv_disruptions and not plan_transport_source:
            raise InvalidArguments(
                'No object "evenement" or "planTransportSource" available in feed provided'
            )

        higher_trip_disruption = _get_piv_higher_trip_disruption(
            piv_disruptions, plan_transport_source)

        json_train["evenement"] = higher_trip_disruption
        train_date_str = get_value(json_train, "dateCirculation")
        train_numbers = get_value(json_train, "numero")
        train_company = get_value(get_value(json_train, "operateur"),
                                  "codeOperateur")
        mode_dict = get_value(json_train, "modeTransport")
        train_mode = get_value(mode_dict, "codeMode")
        train_submode = get_value(mode_dict, "codeSousMode")
        train_typemode = get_value(mode_dict, "typeMode")
        piv_key = "{d}:{n}:{c}:{m}:{s}:{t}".format(d=train_date_str,
                                                   n=train_numbers,
                                                   c=train_company,
                                                   m=train_mode,
                                                   s=train_submode,
                                                   t=train_typemode)

        list_ads = get_value(json_train, "listeArretsDesserte")
        ads = _retrieve_interesting_stops(get_value(list_ads, "arret"))
        if len(ads) < 2:
            raise InvalidArguments(
                'invalid json, "listeArretsDesserte/arret" has less than 2 valid stop_times in '
                "json elt {elt}".format(elt=ujson.dumps(json_train)))
        # replace by cleaned and sorted version of stoptimes list.
        json_train["listeArretsDesserte"]["arret"] = ads

        is_trip_addition = higher_trip_disruption.get("type") == "CREATION"
        train_date = str_to_date(train_date_str)
        vj = self._get_navitia_vj(piv_key, train_date, ads, is_trip_addition)

        trip_updates = [self._make_trip_update(json_train, vj)]

        return trip_updates, log_dict
示例#17
0
def _retrieve_stop_event_datetime(cots_traveler_time):
    base_schedule_datetime = get_value(cots_traveler_time,
                                       "dateHeure",
                                       nullable=True)
    return str_to_utc_naive_dt(
        base_schedule_datetime) if base_schedule_datetime else None
示例#18
0
    def _make_trip_update(self,
                          json_train,
                          vj,
                          action_on_trip=ActionOnTrip.NOT_ADDED.name):
        """
        create the new TripUpdate object
        Following the COTS spec: https://github.com/hove-io/kirin/blob/master/documentation/cots_connector.md
        """
        trip_update = model.TripUpdate(vj=vj,
                                       contributor_id=self.contributor.id)
        trip_message_id = get_value(json_train,
                                    "idMotifInterneReference",
                                    nullable=True)
        if trip_message_id:
            trip_update.message = self.message_handler.get_message(
                index=trip_message_id)
        cots_company_id = get_value(json_train,
                                    "codeCompagnieTransporteur",
                                    nullable=True) or DEFAULT_COMPANY_ID
        trip_update.company_id = self._get_navitia_company(cots_company_id)

        trip_status = get_value(json_train, "statutOperationnel")

        if trip_status == TripStatus.SUPPRIMEE.name:
            # the whole trip is deleted
            trip_update.status = ModificationType.delete.name
            trip_update.stop_time_updates = []
            trip_update.effect = TripEffect.NO_SERVICE.name
            return trip_update

        elif action_on_trip != ActionOnTrip.NOT_ADDED.name:
            # the trip is created from scratch
            trip_update.effect = TripEffect.ADDITIONAL_SERVICE.name
            trip_update.status = ModificationType.add.name
            cots_physical_mode = get_value(json_train,
                                           "indicateurFer",
                                           nullable=True)
            trip_update.physical_mode_id = self._get_navitia_physical_mode(
                cots_physical_mode)
            trip_update.headsign = get_value(json_train,
                                             "numeroCourse",
                                             nullable=True)

        # all other status is considered an 'update' of the trip_update and effect is calculated
        # from stop_time status list. This part is also done in kraken and is to be deleted later
        # Ordered stop_time status= 'nochange', 'add', 'delete', 'update'
        # 'nochange' or 'update' -> SIGNIFICANT_DELAYS, add -> MODIFIED_SERVICE, delete = DETOUR
        else:
            trip_update.status = ModificationType.update.name
            trip_update.effect = TripEffect.MODIFIED_SERVICE.name

        # Initialize stop_time status to nochange
        highest_st_status = ModificationType.none.name
        pdps = _retrieve_interesting_pdp(
            get_value(json_train, "listePointDeParcours"))

        # this variable is used to memoize the last stop_time's departure in order to check the stop_time consistency
        # ex. stop_time[i].arrival/departure must be greater than stop_time[i-1].departure
        last_stop_time_depart = None

        # manage realtime information stop_time by stop_time
        for pdp in pdps:
            # retrieve navitia's stop_point corresponding to the current COTS pdp
            nav_stop, log_dict = self._get_navitia_stop_point(
                pdp, vj.navitia_vj)
            projected_stop_time = {
                "Arrivee": None,
                "Depart": None
            }  # used to check consistency

            if log_dict:
                record_internal_failure(log_dict["log"],
                                        contributor=self.contributor.id)
                log_dict.update({"contributor": self.contributor.id})
                logging.getLogger(__name__).info("metrology", extra=log_dict)

            if nav_stop is None:
                continue

            st_update = model.StopTimeUpdate(nav_stop)
            trip_update.stop_time_updates.append(st_update)
            # using the message from departure-time in priority, if absent fallback on arrival-time's message
            st_message_id = get_value(pdp,
                                      "idMotifInterneDepartReference",
                                      nullable=True)
            if not st_message_id:
                st_message_id = get_value(pdp,
                                          "idMotifInterneArriveeReference",
                                          nullable=True)
            if st_message_id:
                st_update.message = self.message_handler.get_message(
                    index=st_message_id)

            _status_map = {
                "Arrivee": "arrival_status",
                "Depart": "departure_status"
            }
            _delay_map = {
                "Arrivee": "arrival_delay",
                "Depart": "departure_delay"
            }
            _stop_event_datetime_map = {
                "Arrivee": "arrival",
                "Depart": "departure"
            }

            # compute realtime information and fill st_update for arrival and departure
            for arrival_departure_toggle in ["Arrivee", "Depart"]:
                cots_traveler_time = get_value(
                    pdp,
                    "horaireVoyageur{}".format(arrival_departure_toggle),
                    nullable=True)

                if cots_traveler_time is None:
                    continue

                cots_stop_time_status = get_value(cots_traveler_time,
                                                  "statutCirculationOPE",
                                                  nullable=True)

                if cots_stop_time_status is None or cots_stop_time_status == "REACTIVATION":
                    # if no cots_stop_time_status, it is considered an 'update' of the stop_time
                    # (can be a delay, back to normal, normal, reactivation...)
                    cots_base_datetime = _retrieve_stop_event_datetime(
                        cots_traveler_time)
                    if cots_base_datetime:
                        projected_stop_time[
                            arrival_departure_toggle] = cots_base_datetime
                    cots_delay = _retrieve_stop_event_delay(
                        pdp, arrival_departure_toggle)

                    if cots_delay is not None:
                        # It's an update only if there is delay
                        projected_stop_time[
                            arrival_departure_toggle] += cots_delay
                        setattr(st_update,
                                _status_map[arrival_departure_toggle],
                                ModificationType.update.name)
                        setattr(st_update,
                                _delay_map[arrival_departure_toggle],
                                cots_delay)
                    # otherwise nothing to do (status none, delay none, time none)

                elif cots_stop_time_status == "SUPPRESSION":
                    # partial delete
                    setattr(st_update, _status_map[arrival_departure_toggle],
                            ModificationType.delete.name)

                elif cots_stop_time_status == "SUPPRESSION_DETOURNEMENT":
                    # stop_time is replaced by another one
                    setattr(
                        st_update,
                        _status_map[arrival_departure_toggle],
                        ModificationType.deleted_for_detour.name,
                    )

                elif cots_stop_time_status in ["CREATION", "DETOURNEMENT"]:
                    # new stop_time added
                    cots_base_datetime = _retrieve_stop_event_datetime(
                        cots_traveler_time)
                    if cots_base_datetime:
                        projected_stop_time[
                            arrival_departure_toggle] = cots_base_datetime
                    cots_delay = _retrieve_stop_event_delay(
                        pdp, arrival_departure_toggle)
                    if cots_delay is not None:
                        projected_stop_time[
                            arrival_departure_toggle] += cots_delay

                    setattr(
                        st_update,
                        _stop_event_datetime_map[arrival_departure_toggle],
                        projected_stop_time[arrival_departure_toggle],
                    )
                    # delay already added to stop_event datetime
                    setattr(st_update, _delay_map[arrival_departure_toggle],
                            None)

                    if cots_stop_time_status == "CREATION":
                        # pure add
                        setattr(st_update,
                                _status_map[arrival_departure_toggle],
                                ModificationType.add.name)
                    elif cots_stop_time_status == "DETOURNEMENT":
                        # add to replace another stop_time
                        setattr(
                            st_update,
                            _status_map[arrival_departure_toggle],
                            ModificationType.added_for_detour.name,
                        )

                else:
                    raise InvalidArguments(
                        "invalid value {} for field horaireVoyageur{}/statutCirculationOPE"
                        .format(cots_stop_time_status,
                                arrival_departure_toggle))

                arr_dep_status = getattr(st_update,
                                         _status_map[arrival_departure_toggle],
                                         ModificationType.none.name)
                highest_st_status = get_higher_status(highest_st_status,
                                                      arr_dep_status)

            self._check_stop_time_consistency(
                last_stop_time_depart,
                projected_stop_time,
                pdp_code="-".join(pdp[key] for key in ["cr", "ci", "ch"]),
            )
            last_stop_time_depart = projected_stop_time["Depart"]

        # Calculates effect from stop_time status list(this work is also done in kraken and has to be deleted)
        if trip_update.effect == TripEffect.MODIFIED_SERVICE.name:
            trip_update.effect = get_effect_from_modification_type(
                highest_st_status)
        return trip_update
示例#19
0
def _has_arrival(stop):
    return get_value(stop, "arrivee", nullable=True) and not get_value(
        stop, "indicateurDescenteInterdite", nullable=True)
示例#20
0
def _has_departure(stop):
    return get_value(stop, "depart", nullable=True) and not get_value(
        stop, "indicateurMonteeInterdite", nullable=True)