Ejemplo n.º 1
0
        def decorated_service(*args, **kwargs):
            form = get_form_from_request(request)
            if form is None:
                current_app.logger.warning(
                    "Unsupported request method for unpacking 'prior' from request."
                )
                return invalid_method(request.method)

            if "prior" in form:
                prior = parse_isodate_str(form["prior"])
                if ex_post is True:
                    start = parse_isodate_str(form["start"])
                    duration = parse_duration(form["duration"], start)
                    # todo: validate start and duration (refactor already duplicate code from period_required and optional_horizon_accepted)
                    knowledge_time = (
                        start + duration
                    )  # todo: take into account knowledge horizon function
                    if prior < knowledge_time:
                        extra_info = "Meter data can only be observed after the fact."
                        return invalid_horizon(extra_info)
            else:
                prior = None

            kwargs["prior"] = prior
            return fn(*args, **kwargs)
Ejemplo n.º 2
0
        def decorated_service(*args, **kwargs):
            kwargs[
                "resolution"
            ] = None  # using this decorator means you can expect this attribute, None means default
            form = get_form_from_request(request)
            if form is None:
                current_app.logger.warning(
                    "Unsupported request method for unpacking 'resolution' from request."
                )
                return invalid_method(request.method)

            if "resolution" in form and form["resolution"]:
                ds_resolution = parse_duration(form["resolution"])
                if ds_resolution is None:
                    return invalid_resolution_str(form["resolution"])
                # Check if the resolution can be applied to all assets (if it is a multiple
                # of the event_resolution(s) and thus downsampling is possible)
                for asset_group in kwargs["generic_asset_name_groups"]:
                    for asset_descriptor in asset_group:
                        generic_asset = get_generic_asset(asset_descriptor, entity_type)
                        if generic_asset is None:
                            return unrecognized_asset()
                        asset_resolution = generic_asset.event_resolution
                        if ds_resolution % asset_resolution != timedelta(minutes=0):
                            return unapplicable_resolution(
                                f"{isodate.duration_isoformat(asset_resolution)} or a multiple hereof."
                            )
                kwargs["resolution"] = to_offset(
                    isodate.parse_duration(form["resolution"])
                ).freqstr  # Convert ISO period string to pandas frequency string

            return fn(*args, **kwargs)
Ejemplo n.º 3
0
    def wrapper(*args, **kwargs):
        form = get_form_from_request(request)
        if form is None:
            current_app.logger.warning(
                "Unsupported request method for unpacking 'values' from request."
            )
            return invalid_method(request.method)

        if "value" in form:
            value_groups = [parse_as_list(form["value"], of_type=float)]
        elif "values" in form:
            value_groups = [parse_as_list(form["values"], of_type=float)]
        elif "groups" in form:
            value_groups = []
            for group in form["groups"]:
                if "value" in group:
                    value_groups.append(parse_as_list(group["value"], of_type=float))
                elif "values" in group:
                    value_groups.append(parse_as_list(group["values"], of_type=float))
                else:
                    current_app.logger.warning("Group %s missing value(s)" % group)
                    return ptus_incomplete()
        else:
            current_app.logger.warning("Request missing value(s) or group.")
            return ptus_incomplete()

        if not contains_empty_items(value_groups):
            kwargs["value_groups"] = value_groups
            return fn(*args, **kwargs)
        else:
            extra_info = "Request includes empty or ill-formatted value(s)."
            current_app.logger.warning(extra_info)
            return ptus_incomplete(extra_info)
Ejemplo n.º 4
0
    def wrapper(*args, **kwargs):
        form = get_form_from_request(request)
        if form is None:
            current_app.logger.warning(
                "Unsupported request method for unpacking 'start' and 'duration' from request."
            )
            return invalid_method(request.method)

        if "start" in form:
            start = parse_isodate_str(form["start"])
            if not start:
                current_app.logger.warning("Cannot parse 'start' value")
                return invalid_period()
            if start.tzinfo is None:
                current_app.logger.warning("Cannot parse timezone of 'start' value")
                return invalid_timezone(
                    "Start time should explicitly state a timezone."
                )
        else:
            current_app.logger.warning("Request missing 'start'.")
            return invalid_period()
        kwargs["start"] = start
        if "duration" in form:
            duration = parse_duration(form["duration"], start)
            if not duration:
                current_app.logger.warning("Cannot parse 'duration' value")
                return invalid_period()
        else:
            current_app.logger.warning("Request missing 'duration'.")
            return invalid_period()
        kwargs["duration"] = duration
        return fn(*args, **kwargs)
Ejemplo n.º 5
0
        def decorated_service(*args, **kwargs):
            form = get_form_from_request(request)
            if form is None:
                current_app.logger.warning(
                    "Unsupported request method for unpacking 'prior' from request."
                )
                return invalid_method(request.method)

            if "prior" in form:
                prior = parse_isodate_str(form["prior"])
                if ex_post is True:
                    start = parse_isodate_str(form["start"])
                    duration = parse_duration(form["duration"], start)
                    # todo: validate start and duration (refactor already duplicate code from period_required and optional_horizon_accepted)
                    knowledge_time = (
                        start + duration
                    )  # todo: take into account knowledge horizon function
                    if prior < knowledge_time:
                        extra_info = "Meter data can only be observed after the fact."
                        return invalid_horizon(extra_info)
            elif infer_missing is True or (
                    infer_missing_play is True and current_app.config.get(
                        "FLEXMEASURES_MODE", "") == "play"):
                # A missing prior is inferred by the server
                prior = server_now()
            else:
                # Otherwise, a missing prior is fine (a horizon may still be inferred by the server)
                prior = None

            kwargs["prior"] = prior
            return fn(*args, **kwargs)
Ejemplo n.º 6
0
        def decorated_service(*args, **kwargs):
            form = get_form_from_request(request)
            if form is None:
                current_app.logger.warning(
                    "Unsupported request method for unpacking 'horizon' from request."
                )
                return invalid_method(request.method)

            rolling = True
            if "horizon" in form:
                horizon, rolling = parse_horizon(form["horizon"])
                if horizon is None:
                    current_app.logger.warning("Cannot parse 'horizon' value")
                    return invalid_horizon()
                elif ex_post is True:
                    if horizon > timedelta(hours=0):
                        extra_info = "Meter data must have a zero or negative horizon to indicate observations after the fact."
                        return invalid_horizon(extra_info)
            elif infer_missing is True:
                # A missing horizon is only accepted if the server can infer it
                if "start" in form and "duration" in form:
                    start = parse_isodate_str(form["start"])
                    duration = parse_duration(form["duration"], start)
                    if not start:
                        extra_info = "Cannot parse 'start' value."
                        current_app.logger.warning(extra_info)
                        return invalid_period(extra_info)
                    if start.tzinfo is None:
                        current_app.logger.warning(
                            "Cannot parse timezone of 'start' value"
                        )
                        return invalid_timezone(
                            "Start time should explicitly state a timezone."
                        )
                    if not duration:
                        extra_info = "Cannot parse 'duration' value."
                        current_app.logger.warning(extra_info)
                        return invalid_period(extra_info)
                    if current_app.config.get("FLEXMEASURES_MODE", "") == "play":
                        horizon = timedelta(hours=0)
                    else:
                        horizon = start + duration - server_now()
                    rolling = False
                else:
                    current_app.logger.warning(
                        "Request missing both 'horizon', 'start' and 'duration'."
                    )
                    extra_info = "Specify a 'horizon' value, or 'start' and 'duration' values so that the horizon can be inferred."
                    return invalid_horizon(extra_info)
            else:
                # Otherwise, a missing horizon is fine
                horizon = None

            kwargs["horizon"] = horizon
            if infer_missing is True:
                kwargs["rolling"] = rolling
            return fn(*args, **kwargs)
Ejemplo n.º 7
0
 def decorated_service(*args, **kwargs):
     form = get_form_from_request(request)
     if form is None:
         current_app.logger.warning(
             "Unsupported request method for unpacking 'type' from request."
         )
         return invalid_method(request.method)
     elif "type" not in form:
         current_app.logger.warning("Request is missing message type.")
         return no_message_type()
     elif form["type"] != message_type:
         current_app.logger.warning("Type is not accepted for this endpoint.")
         return invalid_message_type(message_type)
     else:
         return fn(*args, **kwargs)
Ejemplo n.º 8
0
    def wrapper(*args, **kwargs):
        form = get_form_from_request(request)
        if form is None:
            current_app.logger.warning(
                "Unsupported request method for unpacking 'unit' from request."
            )
            return invalid_method(request.method)

        if "unit" in form:
            unit = form["unit"]
        else:
            current_app.logger.warning("Request missing 'unit'.")
            return invalid_unit(quantity=None, units=None)

        kwargs["unit"] = unit
        return fn(*args, **kwargs)
Ejemplo n.º 9
0
 def decorated_service(*args, **kwargs):
     form = get_form_from_request(request)
     if form is None:
         current_app.logger.warning(
             "Unsupported request method for unpacking 'unit' from request."
         )
         return invalid_method(request.method)
     elif "unit" not in form:
         current_app.logger.warning("Request is missing unit.")
         return invalid_unit(quantity, units)
     elif form["unit"] not in units:
         current_app.logger.warning(
             "Unit %s is not accepted as one of %s." % (form["unit"], units)
         )
         return invalid_unit(quantity, units)
     else:
         kwargs["unit"] = form["unit"]
         return fn(*args, **kwargs)
Ejemplo n.º 10
0
        def decorated_service(*args, **kwargs):
            form = get_form_from_request(request)
            if form is None:
                current_app.logger.warning(
                    "Unsupported request method for unpacking '%s' from request."
                    % plural_name
                )
                return invalid_method(request.method)

            if generic_asset_type_name in form:
                generic_asset_name_groups = [
                    parse_as_list(form[generic_asset_type_name])
                ]
            elif plural_name in form:
                generic_asset_name_groups = [parse_as_list(form[plural_name])]
            elif groups_name in form:
                generic_asset_name_groups = []
                for group in form["groups"]:
                    if generic_asset_type_name in group:
                        generic_asset_name_groups.append(
                            parse_as_list(group[generic_asset_type_name])
                        )
                    elif plural_name in group:
                        generic_asset_name_groups.append(
                            parse_as_list(group[plural_name])
                        )
                    else:
                        current_app.logger.warning(
                            "Group %s missing %s" % (group, plural_name)
                        )
                        return unrecognized_connection_group()
            else:
                current_app.logger.warning("Request missing %s or group." % plural_name)
                return unrecognized_connection_group()

            if not contains_empty_items(generic_asset_name_groups):
                kwargs["generic_asset_name_groups"] = generic_asset_name_groups
                return fn(*args, **kwargs)
            else:
                current_app.logger.warning("Request includes empty %s." % plural_name)
                return unrecognized_connection_group()
Ejemplo n.º 11
0
        def decorated_service(*args, **kwargs):
            form = get_form_from_request(request)
            if form is None:
                current_app.logger.warning(
                    "Unsupported request method for unpacking 'source' from request."
                )
                return invalid_method(request.method)

            if "source" in form:
                validated_user_source_ids = validate_user_sources(
                    form["source"])
                if None in validated_user_source_ids:
                    return invalid_source(form["source"])
                kwargs["user_source_ids"] = include_current_user_source_id(
                    validated_user_source_ids)
            elif default_source is not None:
                kwargs["user_source_ids"] = include_current_user_source_id(
                    validate_user_sources(default_source))
            else:
                kwargs["user_source_ids"] = None

            return fn(*args, **kwargs)
Ejemplo n.º 12
0
        def decorated_service(*args, **kwargs):
            form = get_form_from_request(request)
            if form is None:
                current_app.logger.warning(
                    "Unsupported request method for unpacking 'horizon' from request."
                )
                return invalid_method(request.method)

            rolling = True
            if "horizon" in form:
                horizon, rolling = parse_horizon(form["horizon"])
                if horizon is None:
                    current_app.logger.warning("Cannot parse 'horizon' value")
                    return invalid_horizon()
                elif ex_post is True:
                    if horizon > timedelta(hours=0):
                        extra_info = "Meter data must have a zero or negative horizon to indicate observations after the fact."
                        return invalid_horizon(extra_info)
                elif rolling is True and accept_repeating_interval is False:
                    extra_info = (
                        "API versions 2.0 and higher use regular ISO 8601 durations instead of repeating time intervals. "
                        "For example: R/P1D should be replaced by P1D.")
                    return invalid_horizon(extra_info)
            elif infer_missing is True or (
                    infer_missing_play is True and current_app.config.get(
                        "FLEXMEASURES_MODE", "") == "play"):
                # A missing horizon is set to zero
                horizon = timedelta(hours=0)
            else:
                # Otherwise, a missing horizon is fine (a prior may still be inferred by the server)
                horizon = None

            kwargs["horizon"] = horizon
            if infer_missing is True and accept_repeating_interval is True:
                kwargs["rolling"] = rolling
            return fn(*args, **kwargs)
Ejemplo n.º 13
0
        def decorated_service(*args, **kwargs):
            form = get_form_from_request(request)
            if form is None:
                current_app.logger.warning(
                    "Unsupported request method for inferring resolution from request."
                )
                return invalid_method(request.method)

            if not all(
                key in kwargs
                for key in [
                    "value_groups",
                    "start",
                    "duration",
                ]
            ):
                current_app.logger.warning("Could not infer resolution.")
                fields = ("values", "start", "duration")
                return required_info_missing(fields, "Resolution cannot be inferred.")
            if "generic_asset_name_groups" not in kwargs:
                return required_info_missing(
                    (entity_type),
                    "Required resolution cannot be found without asset info.",
                )

            # Calculating (inferring) the resolution in the POSTed data
            inferred_resolution = (
                (kwargs["start"] + kwargs["duration"]) - kwargs["start"]
            ) / len(kwargs["value_groups"][0])

            # Finding the required resolution for assets affected in this request
            required_resolution = None
            last_asset = None
            for asset_group in kwargs["generic_asset_name_groups"]:
                for asset_descriptor in asset_group:
                    # Getting the asset
                    generic_asset = get_generic_asset(asset_descriptor, entity_type)
                    if generic_asset is None:
                        return unrecognized_asset(
                            f"Failed to look up asset by {asset_descriptor}"
                        )
                    # Complain if assets don't all require the same resolution
                    if (
                        required_resolution is not None
                        and generic_asset.event_resolution != required_resolution
                    ):
                        return conflicting_resolutions(
                            f"Cannot send data for both {generic_asset} and {last_asset}."
                        )
                    # Setting the resolution & remembering last looked-at asset
                    required_resolution = generic_asset.event_resolution
                    last_asset = generic_asset

            # if inferred resolution is a multiple from required_solution, we can upsample_values
            if inferred_resolution % required_resolution == timedelta(hours=0):
                for i in range(len(kwargs["value_groups"])):
                    kwargs["value_groups"][i] = upsample_values(
                        kwargs["value_groups"][i],
                        from_resolution=inferred_resolution,
                        to_resolution=required_resolution,
                    )
                inferred_resolution = required_resolution

            if inferred_resolution != required_resolution:
                current_app.logger.warning(
                    f"Resolution {inferred_resolution} is not accepted. We require {required_resolution}."
                )
                return unapplicable_resolution(
                    isodate.duration_isoformat(required_resolution)
                )
            else:
                kwargs["resolution"] = inferred_resolution
                return fn(*args, **kwargs)