diff --git a/README.md b/README.md index 2394b98..f6abdff 100644 --- a/README.md +++ b/README.md @@ -217,6 +217,36 @@ suggested_radius = phq.radius.search(location__origin="45.5051,-122.6750") print(suggested_radius.radius, suggested_radius.radius_unit, suggested_radius.location.model_dump(exclude_none=True)) ``` +### Beam endpoints + +Get Analysis. + +Additional examples are available in [usecases/beam/analysis](https://github.com/predicthq/sdk-py/tree/master/usecases/beam/analysis) folder. + +```Python +from predicthq import Client + +phq = Client(access_token="abc123") + + +analysis = phq.beam.analysis.get(analysis_id="abc123") +print(analysis.model_dump(exclude_none=True)) +``` + +Get Analysis Group. + +Additional examples are available in [usecases/beam/analysis_group](https://github.com/predicthq/sdk-py/tree/master/usecases/beam/analysis_group) folder. + +```Python +from predicthq import Client + +phq = Client(access_token="abc123") + + +analysis_group = phq.beam.analysis_group.get(group_id="abc123") +print(analysis_group.model_dump(exlcude_none=True)) +``` + ### Serializing search results into a dictionary All search results can be serialized into a dictionary using the `.model_dump()` method call. diff --git a/predicthq/client.py b/predicthq/client.py index dbae207..f5876d0 100644 --- a/predicthq/client.py +++ b/predicthq/client.py @@ -16,7 +16,7 @@ class Client(object): @classmethod def build_url(cls, path): result = list(urlparse(path)) - result[2] = f"/{result[2].strip('/')}/" + result[2] = f"/{result[2].strip('/')}" return urljoin(config.ENDPOINT_URL, urlunparse(result)) def __init__(self, access_token=None): @@ -35,6 +35,7 @@ def initialize_endpoints(self): self.accounts = endpoints.AccountsEndpoint(proxy(self)) self.places = endpoints.PlacesEndpoint(proxy(self)) self.radius = endpoints.SuggestedRadiusEndpoint(proxy(self)) + self.beam = endpoints.BeamEndpoint(proxy(self)) def get_headers(self, headers): _headers = { diff --git a/predicthq/endpoints/__init__.py b/predicthq/endpoints/__init__.py index 913f460..78c2a71 100644 --- a/predicthq/endpoints/__init__.py +++ b/predicthq/endpoints/__init__.py @@ -5,6 +5,7 @@ from .v1.features import FeaturesEndpoint from .v1.places import PlacesEndpoint from .v1.radius import SuggestedRadiusEndpoint +from .v1.beam import BeamEndpoint __all__ = [ @@ -15,4 +16,5 @@ "FeaturesEndpoint", "PlacesEndpoint", "SuggestedRadiusEndpoint", + "BeamEndpoint", ] diff --git a/predicthq/endpoints/decorators.py b/predicthq/endpoints/decorators.py index 711b195..0500eab 100644 --- a/predicthq/endpoints/decorators.py +++ b/predicthq/endpoints/decorators.py @@ -4,6 +4,8 @@ from predicthq.exceptions import ValidationError +from predicthq.endpoints.schemas import ArgKwargResultSet + def _kwargs_to_key_list_mapping(kwargs, separator="__"): """ @@ -41,16 +43,17 @@ def _to_url_params(key_list_mapping, glue=".", separator=",", parent_key=""): return params -def _to_json(key_list_mapping): +def _to_json(key_list_mapping, json=None): """ Converts key_list_mapping to json """ - json = {} + if json is None: + json = {} for key, value in key_list_mapping.items(): for v in value: json[key] = dict() if not json.get(key) else json[key] if isinstance(v, dict): - json[key].update(_to_json(v)) + _to_json(v, json[key]) else: json[key] = v return json @@ -80,7 +83,6 @@ def returns(model_class): def decorator(f): @functools.wraps(f) def wrapper(endpoint, *args, **kwargs): - model = getattr(endpoint.Meta, f.__name__, {}).get("returns", model_class) data = f(endpoint, *args, **kwargs) @@ -88,6 +90,9 @@ def wrapper(endpoint, *args, **kwargs): loaded_model = model(**data) loaded_model._more = functools.partial(wrapper, endpoint) loaded_model._endpoint = endpoint + if isinstance(loaded_model, ArgKwargResultSet): + loaded_model._args = args + loaded_model._kwargs = kwargs return loaded_model except PydanticValidationError as e: raise ValidationError(e) diff --git a/predicthq/endpoints/schemas.py b/predicthq/endpoints/schemas.py index a08232d..a3066eb 100644 --- a/predicthq/endpoints/schemas.py +++ b/predicthq/endpoints/schemas.py @@ -1,4 +1,3 @@ -from urllib.parse import parse_qsl, urlparse from typing import Callable, Optional from pydantic import BaseModel, HttpUrl @@ -59,3 +58,8 @@ def iter_all(self): def __iter__(self): return self.iter_items() + + +class ArgKwargResultSet(ResultSet): + _args: Optional[dict] = None + _kwargs: Optional[dict] = None diff --git a/predicthq/endpoints/v1/beam/__init__.py b/predicthq/endpoints/v1/beam/__init__.py new file mode 100644 index 0000000..3ece7ca --- /dev/null +++ b/predicthq/endpoints/v1/beam/__init__.py @@ -0,0 +1,5 @@ +from .endpoint import BeamEndpoint +from .schemas import Analysis, AnalysisGroup + + +__all__ = ["BeamEndpoint", "Analysis", "AnalysisGroup"] diff --git a/predicthq/endpoints/v1/beam/endpoint.py b/predicthq/endpoints/v1/beam/endpoint.py new file mode 100644 index 0000000..4d4a505 --- /dev/null +++ b/predicthq/endpoints/v1/beam/endpoint.py @@ -0,0 +1,316 @@ +from predicthq.endpoints.base import BaseEndpoint +from .schemas import ( + CreateAnalysisResponse, + AnalysisResultSet, + Analysis, + FeatureImportance, + CorrelationResultSet, + CreateAnalysisGroupResponse, + AnalysisGroup, + AnalysisGroupResultSet, +) +from predicthq.endpoints.decorators import accepts, returns +from typing import overload, List, Optional, TextIO, Union +from datetime import date + + +class BeamEndpoint: + def __init__(self, client): + self.analysis = self.AnalysisEndpoint(client) + self.analysis_group = self.AnalysisGroupEndpoint(client) + + class AnalysisEndpoint(BaseEndpoint): + @overload + def create( + self, + name: str, + location__geopoint: dict, + location__radius: Optional[float] = None, + location__unit: Optional[str] = None, + location__google_place_id: Optional[str] = None, + location__geoscope_paths: Optional[List[str]] = None, + rank__type: Optional[str] = None, + rank__levels__phq: Optional[dict] = None, + rank__levels__local: Optional[dict] = None, + demand_type__industry: Optional[str] = None, + demand_type__unit_descriptor: Optional[str] = None, + demand_type__unit_currency_multiplier: Optional[float] = None, + demand_type__currency_code: Optional[str] = None, + tz: Optional[str] = None, + external_id: Optional[str] = None, + label: Optional[List[str]] = None, + **params, + ): ... + @accepts(query_string=False) + @returns(CreateAnalysisResponse) + def create(self, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.post( + f"{self.build_url('v1', 'beam')}analyses/", + json=params, + verify=verify_ssl, + ) + + @overload + def search( + self, + updated__gt: Optional[str] = None, + updated__gte: Optional[str] = None, + updated__lt: Optional[str] = None, + updated__lte: Optional[str] = None, + q: Optional[str] = None, + status: Optional[List[str]] = None, + group_id: Optional[List[str]] = None, + demand_type__interval: Optional[List[str]] = None, + demand_type__industry: Optional[List[str]] = None, + readiness_status: Optional[List[str]] = None, + include_deleted: Optional[bool] = None, + sort: Optional[List[str]] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + external_id: Optional[List[str]] = None, + label: Optional[List[str]] = None, + **params, + ): ... + @accepts() + @returns(AnalysisResultSet) + def search(self, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.get( + f"{self.build_url('v1', 'beam')}analyses/", + params=params, + verify=verify_ssl, + ) + + @accepts() + @returns(Analysis) + def get(self, analysis_id, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.get( + f"{self.build_url('v1', 'beam')}analyses/{analysis_id}/", + params=params, + verify=verify_ssl, + ) + + @overload + def update( + self, + analysis_id: str, + name: Optional[str] = None, + location__geopoint: Optional[dict] = None, + location__radius: Optional[float] = None, + location__unit: Optional[str] = None, + location__google_place_id: Optional[str] = None, + location__geoscope_paths: Optional[List[str]] = None, + rank__type: Optional[str] = None, + rank__levels__phq: Optional[dict] = None, + rank__levels__local: Optional[dict] = None, + demand_type__industry: Optional[str] = None, + demand_type__unit_descriptor: Optional[str] = None, + demand_type__unit_currency_multiplier: Optional[float] = None, + demand_type__currency_code: Optional[str] = None, + tz: Optional[str] = None, + external_id: Optional[str] = None, + label: Optional[List[str]] = None, + **params, + ): ... + @accepts(query_string=False) + def update(self, analysis_id: str, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.patch( + f"{self.build_url('v1', 'beam')}analyses/{analysis_id}/", + json=params, + verify=verify_ssl, + ) + + @accepts() + def delete(self, analysis_id: str, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.delete( + f"{self.build_url('v1', 'beam')}analyses/{analysis_id}/", + params=params, + verify=verify_ssl, + ) + + @accepts() + def refresh(self, analysis_id: str, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.post( + f"{self.build_url('v1', 'beam')}analyses/{analysis_id}/refresh/", + params=params, + verify=verify_ssl, + ) + + @overload + def get_correlation_results( + self, + analysis_id: str, + date__gt: Optional[date] = None, + date__gte: Optional[date] = None, + date__lt: Optional[date] = None, + date__lte: Optional[date] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + include_features: Optional[List[str]] = None, + **params, + ): ... + @accepts() + @returns(CorrelationResultSet) + def get_correlation_results(self, analysis_id: str, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.get( + f"{self.build_url('v1', 'beam')}analyses/{analysis_id}/correlate/", + params=params, + verify=verify_ssl, + ) + + @accepts() + @returns(FeatureImportance) + def get_feature_importance(self, analysis_id: str, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.get( + f"{self.build_url('v1', 'beam')}analyses/{analysis_id}/feature-importance/", + params=params, + verify=verify_ssl, + ) + + @accepts(query_string=False) + @overload + def upload_demand( + self, + analysis_id: str, + json: Optional[Union[str, TextIO]] = None, + ndjson: Optional[Union[str, TextIO]] = None, + csv: Optional[Union[str, TextIO]] = None, + **params, + ): ... + def upload_demand(self, analysis_id: str, **params): + verify_ssl = params.pop("config.verify_ssl", True) + if json := params.pop("json", None): + headers = {"Content-Type": "application/json"} + file = json + if ndjson := params.pop("ndjson", None): + headers = {"Content-Type": "application/x-ndjson"} + file = ndjson + if csv := params.pop("csv", None): + headers = {"Content-Type": "text/csv"} + file = csv + + if isinstance(file, str): + with open(file) as f: + return self.client.post( + f"{self.build_url('v1', 'beam')}analyses/{analysis_id}/sink/", + data=f.read(), + headers=headers, + verify=verify_ssl, + ) + + return self.client.post( + f"{self.build_url('v1', 'beam')}analyses/{analysis_id}/sink/", + data=file.read(), + headers=headers, + verify=verify_ssl, + ) + + class AnalysisGroupEndpoint(BaseEndpoint): + @overload + def create( + self, + name: str, + analysis_ids: List[str], + demand_type__unit_descriptor: Optional[str] = None, + **params, + ): ... + @accepts(query_string=False) + @returns(CreateAnalysisGroupResponse) + def create(self, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.post( + f"{self.build_url('v1', 'beam')}analysis-groups/", + json=params, + verify=verify_ssl, + ) + + @overload + def search( + self, + updated__gt: Optional[str] = None, + updated__gte: Optional[str] = None, + updated__lt: Optional[str] = None, + updated__lte: Optional[str] = None, + q: Optional[str] = None, + status: Optional[List[str]] = None, + demand_type__interval: Optional[List[str]] = None, + demand_type__industry: Optional[List[str]] = None, + readiness_status: Optional[List[str]] = None, + include_deleted: Optional[bool] = None, + sort: Optional[List[str]] = None, + offset: Optional[int] = None, + limit: Optional[int] = None, + **params, + ): ... + @accepts() + @returns(AnalysisGroupResultSet) + def search(self, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.get( + f"{self.build_url('v1', 'beam')}analysis-groups/", + params=params, + verify=verify_ssl, + ) + + @accepts() + @returns(AnalysisGroup) + def get(self, group_id: str, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.get( + f"{self.build_url('v1', 'beam')}analysis-groups/{group_id}/", + params=params, + verify=verify_ssl, + ) + + @overload + def update( + self, + group_id: str, + name: Optional[str] = None, + analysis_ids: Optional[List[str]] = None, + demand_type__unit_descriptor: Optional[str] = None, + **params, + ): ... + @accepts(query_string=False) + def update(self, group_id: str, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.patch( + f"{self.build_url('v1', 'beam')}analysis-groups/{group_id}/", + json=params, + verify=verify_ssl, + ) + + @accepts() + def delete(self, group_id: str, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.delete( + f"{self.build_url('v1', 'beam')}analysis-groups/{group_id}/", + params=params, + verify=verify_ssl, + ) + + @accepts() + def refresh(self, group_id: str, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.post( + f"{self.build_url('v1', 'beam')}analysis-groups/{group_id}/refresh/", + params=params, + verify=verify_ssl, + ) + + @accepts() + @returns(FeatureImportance) + def get_feature_importance(self, group_id: str, **params): + verify_ssl = params.pop("config.verify_ssl", True) + return self.client.get( + f"{self.build_url('v1', 'beam')}analysis-groups/{group_id}/feature-importance/", + params=params, + verify=verify_ssl, + ) diff --git a/predicthq/endpoints/v1/beam/schemas.py b/predicthq/endpoints/v1/beam/schemas.py new file mode 100644 index 0000000..ac07e78 --- /dev/null +++ b/predicthq/endpoints/v1/beam/schemas.py @@ -0,0 +1,203 @@ +from pydantic import BaseModel, Field, ConfigDict +from datetime import datetime +from predicthq.endpoints.schemas import ArgKwargResultSet +from typing import Optional, List + + +class BeamPaginationResultSet(ArgKwargResultSet): + def has_next(self): + return self._kwargs.get("offset", 0) + len(self.results) < self.count + + def get_next(self): + if "offset" in self._kwargs: + self._kwargs["offset"] = self._kwargs.get("offset") + len(self.results) + else: + self._kwargs["offset"] = len(self.results) + return self._more(**self._kwargs) + + + +class CreateAnalysisResponse(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + analysis_id: str + + +class GeoPoint(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + lat: str + lon: str + + +class Location(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + geopoint: GeoPoint + radius: float + unit: str + google_place_id: Optional[str] = None + + +class RankLevel(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + min: int + max: Optional[int] = None + + +class RankLevels(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + phq: Optional[RankLevel] = None + local: Optional[RankLevel] = None + + +class Rank(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + type: str + levels: Optional[RankLevels] = None + + +class AnalysisDateRange(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + start: datetime + end: datetime + + +class BestPracticeChecks(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + industry: bool = False + rank: bool = False + radius: bool = False + + +class AnalysisReadinessChecks(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + date_range: Optional[AnalysisDateRange] = None + error_code: Optional[str] = None + missing_dates: Optional[List[str]] = None + validation_response: Optional[dict] = None + best_practice: bool + best_practice_checks: BestPracticeChecks + + +class ProcessingCompleted(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + correlation: bool + feature_importance: bool + value_quant: bool + + +class DemandTypeGroup(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + interval: str + week_start_day: Optional[str] = None + industry: Optional[str] = None + unit_descriptor: str + currency_code: str + + +class DemandType(DemandTypeGroup): + interval: str + week_start_day: Optional[str] = None + industry: Optional[str] = None + unit_descriptor: str + unit_currency_multiplier: float + currency_code: str + + +class Analysis(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + analysis_id: Optional[str] = None + name: str + location: Location + rank: Rank + user_id: Optional[str] = None + access_type: str + status: str + readiness_status: Optional[str] = None + readiness_checks: AnalysisReadinessChecks + processing_completed: ProcessingCompleted + demand_type: DemandType + group_ids: Optional[List[str]] = None + tz: Optional[str] = None + create_dt: datetime + update_dt: datetime + processed_dt: Optional[datetime] = None + external_id: Optional[str] = None + label: Optional[List[str]] = None + + +class AnalysisResultSet(BeamPaginationResultSet): + results: List[Analysis] = Field(alias="analyses") + + +class FeatureGroup(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + feature_group: str + features: List[str] + p_value: float + important: bool + + +class FeatureImportance(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + feature_importance: List[FeatureGroup] + + +class CorrelationResultSet(BeamPaginationResultSet): + model_version: str + version: int + results: List[dict] = Field(alias="dates") + + +class CreateAnalysisGroupResponse(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + group_id: str + + +class ExcludedAnalysis(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + analysis_id: str + reason: str + excluded_from: List[str] + + +class ProcessingCompletedGroup(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + feature_importance: bool + value_quant: bool + excluded_analyses: List[ExcludedAnalysis] + + +class AnalysisGroup(BaseModel): + model_config: ConfigDict = ConfigDict(extra="allow") + + group_id: Optional[str] = None + name: str + analysis_ids: List[str] + user_id: Optional[str] = None + processing_completed: ProcessingCompletedGroup + readiness_status: Optional[str] = None + demand_type: Optional[DemandTypeGroup] = None + status: str + create_dt: datetime + update_dt: datetime + processed_dt: Optional[datetime] = None + + +class AnalysisGroupResultSet(BeamPaginationResultSet): + results: List[AnalysisGroup] = Field(alias="groups") diff --git a/predicthq/endpoints/v1/features/endpoint.py b/predicthq/endpoints/v1/features/endpoint.py index bae59ec..724189d 100644 --- a/predicthq/endpoints/v1/features/endpoint.py +++ b/predicthq/endpoints/v1/features/endpoint.py @@ -25,10 +25,13 @@ def mutate_bool_to_default_for_type(cls, user_request_spec): @accepts(query_string=False) @returns(FeatureResultSet) - def obtain_features(self, **request): + # This is a temporary solution to get the next page for Features API + # _params and _json are for internal use only + def obtain_features(self, _params: dict = None, _json: dict = None, **request): verify_ssl = request.pop("config", {}).get("verify_ssl", True) return self.client.post( self.build_url("v1", "features"), - json=request, + json=_json or request, verify=verify_ssl, + params=_params, ) diff --git a/predicthq/endpoints/v1/features/schemas.py b/predicthq/endpoints/v1/features/schemas.py index 6bbe42b..2ea0da0 100644 --- a/predicthq/endpoints/v1/features/schemas.py +++ b/predicthq/endpoints/v1/features/schemas.py @@ -1,9 +1,36 @@ +import csv from datetime import date from typing import Dict, List, Optional, Union from pydantic import BaseModel, RootModel -from predicthq.endpoints.schemas import ResultSet +from predicthq.endpoints.schemas import ArgKwargResultSet + + +class CsvMixin: + def _flatten_json(self, separator: str) -> Optional[List[dict]]: + def __flatten_json(d: dict, pk: str = "") -> dict: + flat_json = {} + for k, v in d.items(): + if isinstance(v, dict): + flat_json.update( + __flatten_json(v, f"{pk}{separator}{k}" if pk else k) + ) + continue + flat_json.update({f"{pk}{separator}{k}" if pk else k: v}) + return flat_json + + return [__flatten_json(d.model_dump(exclude_none=True)) for d in self.iter_all()] + + def to_csv(self, file: str, mode: str = "w+", separator: str = "_") -> None: + header = None + with open(file, mode=mode) as csv_file: + csv_writer = csv.writer(csv_file) + for d in self._flatten_json(separator): + if not header: + header = list(d.keys()) + csv_writer.writerow(header) + csv_writer.writerow([d[h] for h in header]) class FeatureRankLevel(BaseModel): @@ -31,5 +58,11 @@ def __getattr__(self, name: str) -> Union[date, FeatureStat, FeatureRankLevel]: return self.root[name] -class FeatureResultSet(ResultSet): +class FeatureResultSet(ArgKwargResultSet, CsvMixin): results: List[Optional[Feature]] + + def get_next(self): + if not self.has_next() or not hasattr(self, "_more"): + return + params = self._parse_params(self.next) + return self._more(_params=params, _json=self._kwargs.get("_json", {}) or self._kwargs) diff --git a/predicthq/version.py b/predicthq/version.py index ce1305b..7039708 100644 --- a/predicthq/version.py +++ b/predicthq/version.py @@ -1 +1 @@ -__version__ = "4.0.0" +__version__ = "4.1.0" diff --git a/tests/endpoints/v1/test_beam.py b/tests/endpoints/v1/test_beam.py new file mode 100644 index 0000000..c9010f5 --- /dev/null +++ b/tests/endpoints/v1/test_beam.py @@ -0,0 +1,787 @@ +import unittest + +from tests import load_fixture, with_mock_client +from predicthq.endpoints.v1.beam.schemas import ( + FeatureImportance, + CorrelationResultSet, + Analysis, + CreateAnalysisResponse, + CreateAnalysisGroupResponse, + AnalysisResultSet, + AnalysisGroupResultSet, + AnalysisGroup, +) + + +class BeamTest(unittest.TestCase): + @with_mock_client( + request_returns=load_fixture("requests_responses/beam_test/test_search") + ) + def test_search(self, client): + result = client.beam.analysis.search() + + client.request.assert_called_once_with( + "get", + "/v1/beam/analyses/", + params={}, + verify=True, + ) + + assert isinstance(result, AnalysisResultSet) + + @with_mock_client( + request_returns=load_fixture("requests_responses/beam_test/test_empty_search") + ) + def test_search_params_underscores(self, client): + client.beam.analysis.search( + updated__gt="2016-03-01", + updated__gte="2016-03-01", + updated__lt="2016-04-01", + updated__lte="2016-04-01", + q="query", + status="active", + group_id="group_id", + demand_type__interval="week", + demand_type__industry="retail", + readiness_status=["ready", "not_ready"], + include_deleted=True, + sort=["rank", "-start"], + offset=10, + limit=10, + external_id="external_id", + label=["label1", "label2"], + ) + + client.request.assert_called_once_with( + "get", + "/v1/beam/analyses/", + params={ + "updated.gt": "2016-03-01", + "updated.gte": "2016-03-01", + "updated.lt": "2016-04-01", + "updated.lte": "2016-04-01", + "q": "query", + "status": "active", + "group_id": "group_id", + "demand_type.interval": "week", + "demand_type.industry": "retail", + "readiness_status": "ready,not_ready", + "include_deleted": 1, + "sort": "rank,-start", + "offset": 10, + "limit": 10, + "external_id": "external_id", + "label": "label1,label2", + }, + verify=True, + ) + + @with_mock_client( + request_returns=load_fixture("requests_responses/beam_test/test_empty_search") + ) + def test_search_params_dicts(self, client): + client.beam.analysis.search( + updated={ + "gt": "2016-03-01", + "gte": "2016-03-01", + "lt": "2016-04-01", + "lte": "2016-04-01", + }, + q="query", + status="active", + group_id="group_id", + demand_type={"interval": "week", "industry": "retail"}, + readiness_status=["ready", "not_ready"], + include_deleted=True, + sort=["rank", "-start"], + offset=10, + limit=10, + external_id="external_id", + label=["label1", "label2"], + ) + + client.request.assert_called_once_with( + "get", + "/v1/beam/analyses/", + params={ + "updated.gt": "2016-03-01", + "updated.gte": "2016-03-01", + "updated.lt": "2016-04-01", + "updated.lte": "2016-04-01", + "q": "query", + "status": "active", + "group_id": "group_id", + "demand_type.interval": "week", + "demand_type.industry": "retail", + "readiness_status": "ready,not_ready", + "include_deleted": 1, + "sort": "rank,-start", + "offset": 10, + "limit": 10, + "external_id": "external_id", + "label": "label1,label2", + }, + verify=True, + ) + + @with_mock_client( + request_returns=load_fixture("requests_responses/beam_test/test_create") + ) + def test_create_params_underscores(self, client): + result = client.beam.analysis.create( + name="name", + location__geopoint={"lat": 1.1, "lon": 1.2}, + location__radius=1.0, + location__unit="km", + location__google_place_id="google_place_id", + location__geoscope_paths=["geoscope_path1", "geoscope_path2"], + rank__type="type", + rank__levels__phq={"min": 1.0, "max": 2.0}, + rank__levels__local={"min": 3.0, "max": 4.0}, + demand_type__industry="industry", + demand_type__unit_descriptor="unit_descriptor", + demand_type__unit_currency_multiplier=1.4, + demand_type__currency_code="currency_code", + tz="tz", + external_id="external_id", + label=["label1", "label2"], + ) + + client.request.assert_called_once_with( + "post", + "/v1/beam/analyses/", + json={ + "name": "name", + "location": { + "geopoint": {"lat": 1.1, "lon": 1.2}, + "radius": 1.0, + "unit": "km", + "google_place_id": "google_place_id", + "geoscope_paths": ["geoscope_path1", "geoscope_path2"], + }, + "rank": { + "type": "type", + "levels": { + "phq": {"min": 1.0, "max": 2.0}, + "local": {"min": 3.0, "max": 4.0}, + }, + }, + "demand_type": { + "industry": "industry", + "unit_descriptor": "unit_descriptor", + "unit_currency_multiplier": 1.4, + "currency_code": "currency_code", + }, + "tz": "tz", + "external_id": "external_id", + "label": ["label1", "label2"], + }, + verify=True, + ) + assert isinstance(result, CreateAnalysisResponse) + + @with_mock_client( + request_returns=load_fixture("requests_responses/beam_test/test_create") + ) + def test_create_params_dicts(self, client): + result = client.beam.analysis.create( + name="name", + location={ + "geopoint": {"lat": 1.1, "lon": 1.2}, + "radius": 1.0, + "unit": "km", + "google_place_id": "google_place_id", + "geoscope_paths": ["geoscope_path1", "geoscope_path2"], + }, + rank={ + "type": "type", + "levels": { + "phq": {"min": 1.0, "max": 2.0}, + "local": {"min": 3.0, "max": 4.0}, + }, + }, + demand_type={ + "industry": "industry", + "unit_descriptor": "unit_descriptor", + "unit_currency_multiplier": 1.4, + "currency_code": "currency_code", + }, + tz="tz", + external_id="external_id", + label=["label1", "label2"], + ) + + client.request.assert_called_once_with( + "post", + "/v1/beam/analyses/", + json={ + "name": "name", + "location": { + "geopoint": {"lat": 1.1, "lon": 1.2}, + "radius": 1.0, + "unit": "km", + "google_place_id": "google_place_id", + "geoscope_paths": ["geoscope_path1", "geoscope_path2"], + }, + "rank": { + "type": "type", + "levels": { + "phq": {"min": 1.0, "max": 2.0}, + "local": {"min": 3.0, "max": 4.0}, + }, + }, + "demand_type": { + "industry": "industry", + "unit_descriptor": "unit_descriptor", + "unit_currency_multiplier": 1.4, + "currency_code": "currency_code", + }, + "tz": "tz", + "external_id": "external_id", + "label": ["label1", "label2"], + }, + verify=True, + ) + + assert isinstance(result, CreateAnalysisResponse) + + @with_mock_client() + def test_update_params_underscores(self, client): + client.beam.analysis.update( + analysis_id="abc123", + name="name", + location__geopoint={"lat": 1.1, "lon": 1.2}, + location__radius=1.0, + location__unit="km", + location__google_place_id="google_place_id", + location__geoscope_paths=["geoscope_path1", "geoscope_path2"], + rank__type="type", + rank__levels__phq={"min": 1.0, "max": 2.0}, + rank__levels__local={"min": 3.0, "max": 4.0}, + demand_type__industry="industry", + demand_type__unit_descriptor="unit_descriptor", + demand_type__unit_currency_multiplier=1.4, + demand_type__currency_code="currency_code", + tz="tz", + external_id="external_id", + label=["label1", "label2"], + ) + + client.request.assert_called_once_with( + "patch", + "/v1/beam/analyses/abc123/", + json={ + "name": "name", + "location": { + "geopoint": {"lat": 1.1, "lon": 1.2}, + "radius": 1.0, + "unit": "km", + "google_place_id": "google_place_id", + "geoscope_paths": ["geoscope_path1", "geoscope_path2"], + }, + "rank": { + "type": "type", + "levels": { + "phq": {"min": 1.0, "max": 2.0}, + "local": {"min": 3.0, "max": 4.0}, + }, + }, + "demand_type": { + "industry": "industry", + "unit_descriptor": "unit_descriptor", + "unit_currency_multiplier": 1.4, + "currency_code": "currency_code", + }, + "tz": "tz", + "external_id": "external_id", + "label": ["label1", "label2"], + }, + verify=True, + ) + + @with_mock_client() + def test_update_params_dicts(self, client): + client.beam.analysis.update( + analysis_id="abc123", + name="name", + location={ + "geopoint": {"lat": 1.1, "lon": 1.2}, + "radius": 1.0, + "unit": "km", + "google_place_id": "google_place_id", + "geoscope_paths": ["geoscope_path1", "geoscope_path2"], + }, + rank={ + "type": "type", + "levels": { + "phq": {"min": 1.0, "max": 2.0}, + "local": {"min": 3.0, "max": 4.0}, + }, + }, + demand_type={ + "industry": "industry", + "unit_descriptor": "unit_descriptor", + "unit_currency_multiplier": 1.4, + "currency_code": "currency_code", + }, + tz="tz", + external_id="external_id", + label=["label1", "label2"], + ) + + client.request.assert_called_once_with( + "patch", + "/v1/beam/analyses/abc123/", + json={ + "name": "name", + "location": { + "geopoint": {"lat": 1.1, "lon": 1.2}, + "radius": 1.0, + "unit": "km", + "google_place_id": "google_place_id", + "geoscope_paths": ["geoscope_path1", "geoscope_path2"], + }, + "rank": { + "type": "type", + "levels": { + "phq": {"min": 1.0, "max": 2.0}, + "local": {"min": 3.0, "max": 4.0}, + }, + }, + "demand_type": { + "industry": "industry", + "unit_descriptor": "unit_descriptor", + "unit_currency_multiplier": 1.4, + "currency_code": "currency_code", + }, + "tz": "tz", + "external_id": "external_id", + "label": ["label1", "label2"], + }, + verify=True, + ) + + @with_mock_client() + def test_delete(self, client): + client.beam.analysis.delete(analysis_id="abc123") + + client.request.assert_called_once_with( + "delete", + "/v1/beam/analyses/abc123/", + params={}, + verify=True, + ) + + @with_mock_client() + def test_refresh(self, client): + client.beam.analysis.refresh(analysis_id="abc123") + + client.request.assert_called_once_with( + "post", + "/v1/beam/analyses/abc123/refresh/", + params={}, + verify=True, + ) + + @with_mock_client( + request_returns=load_fixture( + "requests_responses/beam_test/test_empty_correlation" + ) + ) + def test_get_correlation_results_params_underscores(self, client): + client.beam.analysis.get_correlation_results( + analysis_id="abc123", + date__gt="2024-01-01", + date__gte="2024-01-01", + date__lt="2024-01-31", + date__lte="2024-01-31", + offset=10, + limit=10, + include_features=["feature1", "feature2"], + ) + + client.request.assert_called_once_with( + "get", + "/v1/beam/analyses/abc123/correlate/", + params={ + "date.gt": "2024-01-01", + "date.gte": "2024-01-01", + "date.lt": "2024-01-31", + "date.lte": "2024-01-31", + "offset": 10, + "limit": 10, + "include_features": "feature1,feature2", + }, + verify=True, + ) + + @with_mock_client( + request_returns=load_fixture( + "requests_responses/beam_test/test_empty_correlation" + ) + ) + def test_get_correlation_results_empty_params_dicts(self, client): + client.beam.analysis.get_correlation_results( + analysis_id="abc123", + date={ + "gt": "2024-01-01", + "gte": "2024-01-01", + "lt": "2024-01-31", + "lte": "2024-01-31", + }, + offset=10, + limit=10, + include_features=["feature1", "feature2"], + ) + + client.request.assert_called_once_with( + "get", + "/v1/beam/analyses/abc123/correlate/", + params={ + "date.gt": "2024-01-01", + "date.gte": "2024-01-01", + "date.lt": "2024-01-31", + "date.lte": "2024-01-31", + "offset": 10, + "limit": 10, + "include_features": "feature1,feature2", + }, + verify=True, + ) + + @with_mock_client( + request_returns=load_fixture("requests_responses/beam_test/test_correlation") + ) + def test_get_correlation_results_params_dicts(self, client): + result = client.beam.analysis.get_correlation_results( + analysis_id="abc123", + date={ + "gt": "2024-01-01", + "gte": "2024-01-01", + "lt": "2024-01-31", + "lte": "2024-01-31", + }, + offset=10, + limit=10, + include_features=["feature1", "feature2"], + ) + + client.request.assert_called_once_with( + "get", + "/v1/beam/analyses/abc123/correlate/", + params={ + "date.gt": "2024-01-01", + "date.gte": "2024-01-01", + "date.lt": "2024-01-31", + "date.lte": "2024-01-31", + "offset": 10, + "limit": 10, + "include_features": "feature1,feature2", + }, + verify=True, + ) + + assert isinstance(result, CorrelationResultSet) + + @with_mock_client( + request_returns=load_fixture( + "requests_responses/beam_test/test_empty_feature_importance" + ) + ) + def test_feature_empty_importance(self, client): + client.beam.analysis.get_feature_importance(analysis_id="abc123") + + client.request.assert_called_once_with( + "get", + "/v1/beam/analyses/abc123/feature-importance/", + params={}, + verify=True, + ) + + @with_mock_client( + request_returns=load_fixture( + "requests_responses/beam_test/test_feature_importance" + ) + ) + def test_feature_importance(self, client): + result = client.beam.analysis.get_feature_importance(analysis_id="abc123") + + client.request.assert_called_once_with( + "get", + "/v1/beam/analyses/abc123/feature-importance/", + params={}, + verify=True, + ) + assert isinstance(result, FeatureImportance) + + @with_mock_client( + request_returns=load_fixture("requests_responses/beam_test/test_analysis") + ) + def test_get_analysis(self, client): + result = client.beam.analysis.get(analysis_id="abc123") + + client.request.assert_called_once_with( + "get", + "/v1/beam/analyses/abc123/", + params={}, + verify=True, + ) + assert isinstance(result, Analysis) + + @with_mock_client( + request_returns=load_fixture("requests_responses/beam_test/test_create_group") + ) + def test_create_group_params_underscores(self, client): + result = client.beam.analysis_group.create( + name="name", + analysis_ids=["analysis_id1", "analysis_id2"], + demand_type__unit_descriptor="unit_descriptor", + ) + + client.request.assert_called_once_with( + "post", + "/v1/beam/analysis-groups/", + json={ + "name": "name", + "analysis_ids": ["analysis_id1", "analysis_id2"], + "demand_type": {"unit_descriptor": "unit_descriptor"}, + }, + verify=True, + ) + + assert isinstance(result, CreateAnalysisGroupResponse) + + @with_mock_client( + request_returns=load_fixture("requests_responses/beam_test/test_create_group") + ) + def test_create_group_params_dicts(self, client): + result = client.beam.analysis_group.create( + name="name", + analysis_ids=["analysis_id1", "analysis_id2"], + demand_type={"unit_descriptor": "unit_descriptor"}, + ) + + client.request.assert_called_once_with( + "post", + "/v1/beam/analysis-groups/", + json={ + "name": "name", + "analysis_ids": ["analysis_id1", "analysis_id2"], + "demand_type": {"unit_descriptor": "unit_descriptor"}, + }, + verify=True, + ) + + assert isinstance(result, CreateAnalysisGroupResponse) + + @with_mock_client( + request_returns=load_fixture("requests_responses/beam_test/test_group_search") + ) + def test_search_group(self, client): + result = client.beam.analysis_group.search() + + client.request.assert_called_once_with( + "get", + "/v1/beam/analysis-groups/", + params={}, + verify=True, + ) + + assert isinstance(result, AnalysisGroupResultSet) + + @with_mock_client( + request_returns=load_fixture( + "requests_responses/beam_test/test_empty_group_search" + ) + ) + def test_search_group_params_underscores(self, client): + client.beam.analysis_group.search( + updated__gt="2016-03-01", + updated__gte="2016-03-01", + updated__lt="2016-04-01", + updated__lte="2016-04-01", + q="query", + status=["active", "inactive"], + demand_type__interval=["week", "month"], + demand_type__industry=["retail", "hospitality"], + readiness_status=["ready", "not_ready"], + include_deleted=True, + sort=["rank", "-start"], + offset=10, + limit=10, + ) + + client.request.assert_called_once_with( + "get", + "/v1/beam/analysis-groups/", + params={ + "updated.gt": "2016-03-01", + "updated.gte": "2016-03-01", + "updated.lt": "2016-04-01", + "updated.lte": "2016-04-01", + "q": "query", + "status": "active,inactive", + "demand_type.interval": "week,month", + "demand_type.industry": "retail,hospitality", + "readiness_status": "ready,not_ready", + "include_deleted": 1, + "sort": "rank,-start", + "offset": 10, + "limit": 10, + }, + verify=True, + ) + + @with_mock_client( + request_returns=load_fixture( + "requests_responses/beam_test/test_empty_group_search" + ) + ) + def test_search_group_params_dicts(self, client): + client.beam.analysis_group.search( + updated={ + "gt": "2016-03-01", + "gte": "2016-03-01", + "lt": "2016-04-01", + "lte": "2016-04-01", + }, + q="query", + status="active,inactive", + demand_type={ + "interval": "week,month", + "industry": "retail,hospitality", + }, + readiness_status="ready,not_ready", + include_deleted=1, + sort="rank,-start", + offset=10, + limit=10, + ) + + client.request.assert_called_once_with( + "get", + "/v1/beam/analysis-groups/", + params={ + "updated.gt": "2016-03-01", + "updated.gte": "2016-03-01", + "updated.lt": "2016-04-01", + "updated.lte": "2016-04-01", + "q": "query", + "status": "active,inactive", + "demand_type.interval": "week,month", + "demand_type.industry": "retail,hospitality", + "readiness_status": "ready,not_ready", + "include_deleted": 1, + "sort": "rank,-start", + "offset": 10, + "limit": 10, + }, + verify=True, + ) + + @with_mock_client( + request_returns=load_fixture("requests_responses/beam_test/test_analysis_group") + ) + def test_get_analysis_group(self, client): + result = client.beam.analysis_group.get(group_id="abc123") + + client.request.assert_called_once_with( + "get", + "/v1/beam/analysis-groups/abc123/", + params={}, + verify=True, + ) + assert isinstance(result, AnalysisGroup) + + @with_mock_client() + def test_update_group_params_underscores(self, client): + client.beam.analysis_group.update( + group_id="abc123", + name="name", + analysis_ids=["analysis_id1", "analysis_id2"], + demand_type__unit_descriptor="unit_descriptor", + ) + + client.request.assert_called_once_with( + "patch", + "/v1/beam/analysis-groups/abc123/", + json={ + "name": "name", + "analysis_ids": ["analysis_id1", "analysis_id2"], + "demand_type": {"unit_descriptor": "unit_descriptor"}, + }, + verify=True, + ) + + @with_mock_client() + def test_update_group_params_dicts(self, client): + client.beam.analysis_group.update( + group_id="abc123", + name="name", + analysis_ids=["analysis_id1", "analysis_id2"], + demand_type={"unit_descriptor": "unit_descriptor"}, + ) + + client.request.assert_called_once_with( + "patch", + "/v1/beam/analysis-groups/abc123/", + json={ + "name": "name", + "analysis_ids": ["analysis_id1", "analysis_id2"], + "demand_type": {"unit_descriptor": "unit_descriptor"}, + }, + verify=True, + ) + + @with_mock_client() + def test_delete_group(self, client): + client.beam.analysis_group.delete(group_id="abc123") + + client.request.assert_called_once_with( + "delete", + "/v1/beam/analysis-groups/abc123/", + params={}, + verify=True, + ) + + @with_mock_client() + def test_refresh_group(self, client): + client.beam.analysis_group.refresh(group_id="abc123") + + client.request.assert_called_once_with( + "post", + "/v1/beam/analysis-groups/abc123/refresh/", + params={}, + verify=True, + ) + + @with_mock_client( + request_returns=load_fixture( + "requests_responses/beam_test/test_empty_feature_importance" + ) + ) + def test_group_feature_empty_importance(self, client): + client.beam.analysis_group.get_feature_importance(group_id="abc123") + + client.request.assert_called_once_with( + "get", + "/v1/beam/analysis-groups/abc123/feature-importance/", + params={}, + verify=True, + ) + + @with_mock_client( + request_returns=load_fixture( + "requests_responses/beam_test/test_feature_importance" + ) + ) + def test_group_feature_importance(self, client): + result = client.beam.analysis_group.get_feature_importance(group_id="abc123") + + client.request.assert_called_once_with( + "get", + "/v1/beam/analysis-groups/abc123/feature-importance/", + params={}, + verify=True, + ) + assert isinstance(result, FeatureImportance) diff --git a/tests/endpoints/v1/test_features.py b/tests/endpoints/v1/test_features.py index 1e5e11f..963defe 100644 --- a/tests/endpoints/v1/test_features.py +++ b/tests/endpoints/v1/test_features.py @@ -47,6 +47,7 @@ def test_mutate_default_criteria(self, client): "phq_spend_performing_arts_transportation": {"stats": ["sum", "count"], "phq_rank": None}, }, verify=True, + params=None, ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -114,6 +115,7 @@ def test_attendance_request_params_underscores(self, client): "phq_attendance_sports_hospitality": {"stats": feature_stats}, }, verify=True, + params=None, ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -130,6 +132,7 @@ def test_attendance_request_params_underscores_without_ssl_verification(self, cl "location": {"place_id": ["4671654"]}, }, verify=False, + params=None ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -196,6 +199,7 @@ def test_attendance_request_params_dicts(self, client): "phq_attendance_sports_hospitality": feature_criteria, }, verify=True, + params=None, ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -214,6 +218,7 @@ def test_attendance_request_params_dicts_without_ssl_verification(self, client): "location": {"geo": {"lat": 41.75038, "lon": -71.49978, "radius": "30km"}}, }, verify=False, + params=None ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -250,6 +255,7 @@ def test_rank_request_params_underscores(self, client): "phq_rank_academic_holiday": True, }, verify=True, + params=None, ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -268,6 +274,7 @@ def test_rank_request_params_underscores_without_ssl_verification(self, client): "location": {"geo": {"lat": 41.75038, "lon": -71.49978, "radius": "30km"}}, }, verify=False, + params=None ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -303,6 +310,7 @@ def test_rank_request_params_dicts(self, client): "phq_rank_academic_holiday": True, }, verify=True, + params=None, ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -319,6 +327,7 @@ def test_rank_request_params_dicts_without_ssl_verification(self, client): "location": {"place_id": ["4671654"]}, }, verify=False, + params=None ) @with_client() @@ -379,6 +388,7 @@ def test_impact_severe_weather_request_params_underscores(self, client): "phq_impact_severe_weather_tropical_storm_retail": {"stats": feature_stats}, }, verify=True, + params=None, ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -425,6 +435,7 @@ def test_impact_severe_weather_request_params_dicts(self, client): "phq_impact_severe_weather_tropical_storm_retail": feature_criteria, }, verify=True, + params=None, ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -463,6 +474,7 @@ def test_viewership_request_params_underscores(self, client): "phq_viewership_sports_golf_pga_championship": {"stats": feature_stats}, }, verify=True, + params=None, ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -481,6 +493,7 @@ def test_viewership_request_params_underscores_without_ssl_verification(self, cl "location": {"place_id": ["4671654"]}, }, verify=False, + params=None ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -517,6 +530,7 @@ def test_viewership_request_params_dicts(self, client): "phq_viewership_sports_horse_racing": feature_criteria, }, verify=True, + params=None, ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -535,6 +549,7 @@ def test_viewership_request_params_dicts_without_ssl_verification(self, client): "location": {"geo": {"lat": 41.75038, "lon": -71.49978, "radius": "30km"}}, }, verify=False, + params=None ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -610,6 +625,7 @@ def test_spend_request_params_underscores(self, client): "phq_spend_performing_arts_transportation": {"stats": feature_stats}, }, verify=True, + params=None, ) @with_mock_client(request_returns=load_fixture("requests_responses/features_test/test_empty_search")) @@ -684,4 +700,5 @@ def test_spend_request_params_dicts(self, client): "phq_spend_performing_arts_transportation": feature_criteria, }, verify=True, + params=None, ) diff --git a/tests/fixtures/requests_responses/beam_test/test_analysis.json b/tests/fixtures/requests_responses/beam_test/test_analysis.json new file mode 100644 index 0000000..33a47ca --- /dev/null +++ b/tests/fixtures/requests_responses/beam_test/test_analysis.json @@ -0,0 +1,64 @@ +{ + "name": "SDK Test", + "location": { + "geopoint": { + "lat": "37.7749", + "lon": "-122.4194" + }, + "radius": 3345.52, + "unit": "m", + "google_place_id": null + }, + "rank": { + "type": "local", + "levels": { + "phq": null, + "local": { + "min": 35, + "max": null + } + } + }, + "user_id": null, + "access_type": "full", + "status": "active", + "readiness_status": "ready", + "readiness_checks": { + "date_range": { + "start": "2019-01-01T00:00:00", + "end": "2021-09-26T00:00:00" + }, + "error_code": null, + "missing_dates": [], + "validation_response": { + "missing_data_percentage": 0.0, + "consecutive_nan": 0 + }, + "best_practice": false, + "best_practice_checks": { + "industry": false, + "rank": true, + "radius": true + } + }, + "processing_completed": { + "correlation": true, + "feature_importance": true, + "value_quant": true + }, + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": "other", + "unit_descriptor": "omitted", + "currency_code": "USD", + "unit_currency_multiplier": 1.0 + }, + "group_ids": null, + "tz": "America/Los_Angeles", + "create_dt": "2025-01-15T00:30:44Z", + "update_dt": "2025-01-15T00:53:48Z", + "processed_dt": "2025-01-15T00:53:30Z", + "external_id": null, + "label": null +} \ No newline at end of file diff --git a/tests/fixtures/requests_responses/beam_test/test_analysis_group.json b/tests/fixtures/requests_responses/beam_test/test_analysis_group.json new file mode 100644 index 0000000..4423e72 --- /dev/null +++ b/tests/fixtures/requests_responses/beam_test/test_analysis_group.json @@ -0,0 +1,34 @@ +{ + "name": "test group", + "analysis_ids": [ + "2r2f23f2", + "sgds2356" + ], + "user_id": "sdf25g", + "processing_completed": { + "feature_importance": true, + "value_quant": true, + "excluded_analyses": [ + { + "analysis_id": "sdgsg35", + "reason": "analysis_deleted", + "excluded_from": [ + "feature_importance", + "value_quant" + ] + } + ] + }, + "readiness_status": "ready", + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": "retail", + "unit_descriptor": "Sales", + "currency_code": "USD" + }, + "status": "active", + "create_dt": "2024-09-13T03:59:45Z", + "update_dt": "2024-12-17T01:55:45Z", + "processed_dt": "2024-12-17T01:55:46Z" +} \ No newline at end of file diff --git a/tests/fixtures/requests_responses/beam_test/test_correlation.json b/tests/fixtures/requests_responses/beam_test/test_correlation.json new file mode 100644 index 0000000..999ff2d --- /dev/null +++ b/tests/fixtures/requests_responses/beam_test/test_correlation.json @@ -0,0 +1,1168 @@ +{ + "count": 30, + "previous": null, + "next": null, + "model_version": "2.3.1", + "version": 6, + "dates": [ + { + "date": "2021-06-01", + "actual_demand": 96.0, + "baseline_demand": 106.05615230186515, + "remainder": -10.056152301865154, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_observances": { + "rank_levels": { + "2": 1, + "3": 2 + } + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 9 + }, + { + "date": "2021-06-02", + "actual_demand": 97.0, + "baseline_demand": 106.98993686596805, + "remainder": -9.989936865968048, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-03", + "actual_demand": 108.0, + "baseline_demand": 107.78619345008876, + "remainder": 0.21380654991124004, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_observances": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 7 + }, + { + "date": "2021-06-04", + "actual_demand": 104.0, + "baseline_demand": 106.09708199255878, + "remainder": -2.0970819925587847, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-05", + "actual_demand": 85.0, + "baseline_demand": 90.29705752901104, + "remainder": -5.297057529011042, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-06", + "actual_demand": 84.0, + "baseline_demand": 77.76561500510894, + "remainder": 6.234384994891059, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-07", + "actual_demand": 94.0, + "baseline_demand": 89.68251774264668, + "remainder": 4.317482257353319, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-08", + "actual_demand": 98.0, + "baseline_demand": 104.79481768246053, + "remainder": -6.794817682460533, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-09", + "actual_demand": 118.0, + "baseline_demand": 106.99744606698593, + "remainder": 11.00255393301407, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-10", + "actual_demand": 93.0, + "baseline_demand": 109.22977624427338, + "remainder": -16.22977624427338, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_concerts": { + "sum": 1154 + }, + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_spend_concerts": { + "sum": 25444 + }, + "phq_spend_concerts_accommodation": { + "sum": 3920 + }, + "phq_spend_concerts_hospitality": { + "sum": 12188 + }, + "phq_spend_concerts_transportation": { + "sum": 9334 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 50886, + "phq_attendance_sum": 19974, + "phq_rank_count": 6 + }, + { + "date": "2021-06-11", + "actual_demand": 91.0, + "baseline_demand": 110.31791642300257, + "remainder": -19.317916423002572, + "impact_significance": "MEDIUM", + "impact_significance_score": 2, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-12", + "actual_demand": 95.0, + "baseline_demand": 95.6506657272828, + "remainder": -0.6506657272827994, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_concerts": { + "sum": 822 + }, + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_spend_concerts": { + "sum": 20195 + }, + "phq_spend_concerts_accommodation": { + "sum": 3921 + }, + "phq_spend_concerts_hospitality": { + "sum": 9216 + }, + "phq_spend_concerts_transportation": { + "sum": 7057 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 40389, + "phq_attendance_sum": 19642, + "phq_rank_count": 6 + }, + { + "date": "2021-06-13", + "actual_demand": 76.0, + "baseline_demand": 82.95270472073167, + "remainder": -6.952704720731674, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-14", + "actual_demand": 97.0, + "baseline_demand": 93.59407396673511, + "remainder": 3.4059260332648904, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_observances": { + "rank_levels": { + "3": 1, + "4": 1 + } + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 8 + }, + { + "date": "2021-06-15", + "actual_demand": 104.0, + "baseline_demand": 105.7583549266292, + "remainder": -1.7583549266291953, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-16", + "actual_demand": 88.0, + "baseline_demand": 103.1721650429274, + "remainder": -15.172165042927404, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-17", + "actual_demand": 102.0, + "baseline_demand": 100.69169170492215, + "remainder": 1.3083082950778504, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-18", + "actual_demand": 118.0, + "baseline_demand": 98.10072896104927, + "remainder": 19.89927103895073, + "impact_significance": "MEDIUM", + "impact_significance_score": 2, + "features": { + "phq_attendance_concerts": { + "sum": 787 + }, + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_spend_concerts": { + "sum": 19355 + }, + "phq_spend_concerts_accommodation": { + "sum": 3758 + }, + "phq_spend_concerts_hospitality": { + "sum": 8832 + }, + "phq_spend_concerts_transportation": { + "sum": 6764 + }, + "phq_rank_observances": { + "rank_levels": { + "2": 1 + } + }, + "phq_rank_public_holidays": { + "rank_levels": { + "4": 1, + "5": 1 + } + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + }, + "phq_impact_severe_weather_thunderstorm_retail": { + "sum": 56, + "max": 56 + } + }, + "phq_impact_sum": 56, + "phq_spend_sum": 38709, + "phq_attendance_sum": 19607, + "phq_rank_count": 9 + }, + { + "date": "2021-06-19", + "actual_demand": 80.0, + "baseline_demand": 79.90844533909288, + "remainder": 0.09155466090712139, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_community": { + "sum": 280 + }, + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_spend_community": { + "sum": 2848 + }, + "phq_spend_community_hospitality": { + "sum": 1885 + }, + "phq_spend_community_transportation": { + "sum": 962 + }, + "phq_rank_observances": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_public_holidays": { + "rank_levels": { + "4": 1, + "5": 2 + } + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + }, + "phq_impact_severe_weather_thunderstorm_retail": { + "sum": 56, + "max": 56 + } + }, + "phq_impact_sum": 56, + "phq_spend_sum": 5695, + "phq_attendance_sum": 19100, + "phq_rank_count": 10 + }, + { + "date": "2021-06-20", + "actual_demand": 84.0, + "baseline_demand": 67.07823205057903, + "remainder": 16.921767949420968, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_concerts": { + "sum": 246 + }, + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_spend_concerts": { + "sum": 3901 + }, + "phq_spend_concerts_hospitality": { + "sum": 2209 + }, + "phq_spend_concerts_transportation": { + "sum": 1692 + }, + "phq_rank_observances": { + "rank_levels": { + "2": 2, + "5": 1 + } + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + }, + "phq_impact_severe_weather_thunderstorm_retail": { + "sum": 25, + "max": 25 + } + }, + "phq_impact_sum": 25, + "phq_spend_sum": 7802, + "phq_attendance_sum": 19066, + "phq_rank_count": 9 + }, + { + "date": "2021-06-21", + "actual_demand": 91.0, + "baseline_demand": 79.1158828855306, + "remainder": 11.884117114469404, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-22", + "actual_demand": 96.0, + "baseline_demand": 93.64346589904916, + "remainder": 2.3565341009508387, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_concerts": { + "sum": 822 + }, + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_spend_concerts": { + "sum": 20230 + }, + "phq_spend_concerts_accommodation": { + "sum": 3928 + }, + "phq_spend_concerts_hospitality": { + "sum": 9232 + }, + "phq_spend_concerts_transportation": { + "sum": 7069 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 40459, + "phq_attendance_sum": 19642, + "phq_rank_count": 6 + }, + { + "date": "2021-06-23", + "actual_demand": 88.0, + "baseline_demand": 95.74263012317203, + "remainder": -7.7426301231720345, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-24", + "actual_demand": 96.0, + "baseline_demand": 99.68731430141152, + "remainder": -3.6873143014115186, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-25", + "actual_demand": 97.0, + "baseline_demand": 102.57127927550286, + "remainder": -5.57127927550286, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-26", + "actual_demand": 75.0, + "baseline_demand": 89.9836068363395, + "remainder": -14.983606836339504, + "impact_significance": "NO_IMPACT", + "impact_significance_score": 0, + "features": { + "phq_attendance_concerts": { + "sum": 319 + }, + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_spend_concerts": { + "sum": 5064 + }, + "phq_spend_concerts_hospitality": { + "sum": 2868 + }, + "phq_spend_concerts_transportation": { + "sum": 2196 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + } + }, + "phq_impact_sum": 0, + "phq_spend_sum": 10128, + "phq_attendance_sum": 19139, + "phq_rank_count": 6 + }, + { + "date": "2021-06-27", + "actual_demand": 88.0, + "baseline_demand": 80.16344951178947, + "remainder": 7.836550488210534, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + }, + "phq_impact_severe_weather_flood_retail": { + "sum": 10, + "max": 10 + } + }, + "phq_impact_sum": 10, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-28", + "actual_demand": 105.0, + "baseline_demand": 93.81334859988509, + "remainder": 11.186651400114911, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + }, + "phq_impact_severe_weather_flood_retail": { + "sum": 21, + "max": 21 + } + }, + "phq_impact_sum": 21, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-29", + "actual_demand": 109.0, + "baseline_demand": 107.64564801761523, + "remainder": 1.3543519823847703, + "impact_significance": "WEAK", + "impact_significance_score": 1, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + }, + "phq_impact_severe_weather_flood_retail": { + "sum": 56, + "max": 56 + } + }, + "phq_impact_sum": 56, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + }, + { + "date": "2021-06-30", + "actual_demand": 86.0, + "baseline_demand": 106.41714640075705, + "remainder": -20.417146400757048, + "impact_significance": "MEDIUM", + "impact_significance_score": 2, + "features": { + "phq_attendance_school_holidays": { + "sum": 18820 + }, + "phq_rank_school_holidays": { + "rank_levels": { + "3": 3, + "4": 1 + } + }, + "phq_rank_academic_session": { + "rank_levels": { + "3": 1 + } + }, + "phq_rank_academic_holiday": { + "rank_levels": { + "4": 1 + } + }, + "phq_impact_severe_weather_flood_retail": { + "sum": 31, + "max": 31 + } + }, + "phq_impact_sum": 31, + "phq_spend_sum": 0, + "phq_attendance_sum": 18820, + "phq_rank_count": 6 + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/requests_responses/beam_test/test_create.json b/tests/fixtures/requests_responses/beam_test/test_create.json new file mode 100644 index 0000000..3c7a2f4 --- /dev/null +++ b/tests/fixtures/requests_responses/beam_test/test_create.json @@ -0,0 +1,3 @@ +{ + "analysis_id": "abc123" +} \ No newline at end of file diff --git a/tests/fixtures/requests_responses/beam_test/test_create_group.json b/tests/fixtures/requests_responses/beam_test/test_create_group.json new file mode 100644 index 0000000..3540f1b --- /dev/null +++ b/tests/fixtures/requests_responses/beam_test/test_create_group.json @@ -0,0 +1,3 @@ +{ + "group_id": "abc123" +} \ No newline at end of file diff --git a/tests/fixtures/requests_responses/beam_test/test_empty_correlation.json b/tests/fixtures/requests_responses/beam_test/test_empty_correlation.json new file mode 100644 index 0000000..717649d --- /dev/null +++ b/tests/fixtures/requests_responses/beam_test/test_empty_correlation.json @@ -0,0 +1,6 @@ +{ + "model_version": "1.0", + "version": 1, + "dates": [], + "count": 0 +} \ No newline at end of file diff --git a/tests/fixtures/requests_responses/beam_test/test_empty_feature_importance.json b/tests/fixtures/requests_responses/beam_test/test_empty_feature_importance.json new file mode 100644 index 0000000..c14f62c --- /dev/null +++ b/tests/fixtures/requests_responses/beam_test/test_empty_feature_importance.json @@ -0,0 +1,100 @@ +{ + "feature_importance": [ + { + "feature_group": "concerts", + "features": [ + "phq_attendance_concerts" + ], + "p_value": 0.0, + "important": true + }, + { + "feature_group": "severe-weather", + "features": [ + "phq_impact_severe_weather_heat_wave_retail" + ], + "p_value": 0.0015, + "important": true + }, + { + "feature_group": "performing-arts", + "features": [ + "phq_attendance_performing_arts" + ], + "p_value": 0.2646, + "important": false + }, + { + "feature_group": "school-holidays", + "features": [ + "phq_attendance_school_holidays" + ], + "p_value": 0.4283, + "important": false + }, + { + "feature_group": "festivals", + "features": [ + "phq_attendance_festivals" + ], + "p_value": 0.4689, + "important": false + }, + { + "feature_group": "public-holidays", + "features": [ + "phq_rank_public_holidays" + ], + "p_value": 0.5, + "important": false + }, + { + "feature_group": "expos", + "features": [ + "phq_attendance_expos" + ], + "p_value": 0.5611, + "important": false + }, + { + "feature_group": "conferences", + "features": [ + "phq_attendance_conferences" + ], + "p_value": 0.9605, + "important": false + }, + { + "feature_group": "observances", + "features": [ + "phq_rank_observances" + ], + "p_value": 0.9945, + "important": false + }, + { + "feature_group": "sports", + "features": [ + "phq_attendance_sports" + ], + "p_value": 0.9974, + "important": false + }, + { + "feature_group": "community", + "features": [ + "phq_attendance_community" + ], + "p_value": 0.998, + "important": false + }, + { + "feature_group": "academic", + "features": [ + "phq_rank_academic_holiday" + ], + "p_value": 0.999, + "important": false + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/requests_responses/beam_test/test_empty_group_search.json b/tests/fixtures/requests_responses/beam_test/test_empty_group_search.json new file mode 100644 index 0000000..58111f6 --- /dev/null +++ b/tests/fixtures/requests_responses/beam_test/test_empty_group_search.json @@ -0,0 +1,4 @@ +{ + "count": 88, + "groups": [] +} \ No newline at end of file diff --git a/tests/fixtures/requests_responses/beam_test/test_empty_search.json b/tests/fixtures/requests_responses/beam_test/test_empty_search.json new file mode 100644 index 0000000..9ebb62b --- /dev/null +++ b/tests/fixtures/requests_responses/beam_test/test_empty_search.json @@ -0,0 +1,4 @@ +{ + "count": 0, + "analyses": [] +} \ No newline at end of file diff --git a/tests/fixtures/requests_responses/beam_test/test_feature_importance.json b/tests/fixtures/requests_responses/beam_test/test_feature_importance.json new file mode 100644 index 0000000..3681e3b --- /dev/null +++ b/tests/fixtures/requests_responses/beam_test/test_feature_importance.json @@ -0,0 +1,3 @@ +{ + "feature_importance": [] +} \ No newline at end of file diff --git a/tests/fixtures/requests_responses/beam_test/test_group_search.json b/tests/fixtures/requests_responses/beam_test/test_group_search.json new file mode 100644 index 0000000..aef9fca --- /dev/null +++ b/tests/fixtures/requests_responses/beam_test/test_group_search.json @@ -0,0 +1,308 @@ +{ + "count": 88, + "groups": [ + { + "group_id": "abc123", + "name": "test group name", + "analysis_ids": [ + "id1", + "id2" + ], + "user_id": "test user id", + "processing_completed": { + "feature_importance": true, + "value_quant": true, + "excluded_analyses": [ + { + "analysis_id": "analysis id", + "reason": "analysis_deleted", + "excluded_from": [ + "feature_importance", + "value_quant" + ] + } + ] + }, + "readiness_status": "ready", + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": "retail", + "unit_descriptor": "Sales", + "currency_code": "USD" + }, + "status": "active", + "create_dt": "2024-09-13T03:59:45Z", + "update_dt": "2024-12-17T01:55:45Z", + "processed_dt": "2024-12-17T01:55:46Z" + }, + { + "group_id": "abc123", + "name": "test group name", + "analysis_ids": [ + "id1", + "id2" + ], + "user_id": "test user id", + "processing_completed": { + "feature_importance": true, + "value_quant": true, + "excluded_analyses": [] + }, + "readiness_status": "ready", + "demand_type": { + "interval": "week", + "week_start_day": "monday", + "industry": null, + "unit_descriptor": "Sales", + "currency_code": "USD" + }, + "status": "active", + "create_dt": "2024-05-16T21:14:03.204830Z", + "update_dt": "2024-12-15T20:45:24Z", + "processed_dt": "2024-12-15T20:45:26Z" + }, + { + "group_id": "abc123", + "name": "test group name", + "analysis_ids": [ + "id1", + "id2" + ], + "user_id": "test user id", + "processing_completed": { + "feature_importance": false, + "value_quant": false, + "excluded_analyses": [ + { + "analysis_id": "analysis id", + "reason": "analysis_demand_type_inconsistent", + "excluded_from": [ + "feature_importance", + "value_quant" + ] + }, + { + "analysis_id": "analysis id", + "reason": "analysis_demand_type_inconsistent", + "excluded_from": [ + "feature_importance", + "value_quant" + ] + } + ] + }, + "readiness_status": "failed", + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": null, + "unit_descriptor": "omitted", + "currency_code": "USD" + }, + "status": "active", + "create_dt": "2024-05-07T01:45:28.369118Z", + "update_dt": "2024-11-04T03:35:35Z", + "processed_dt": "2024-11-04T03:36:02Z" + }, + { + "group_id": "abc123", + "name": "test group name", + "analysis_ids": [ + "id1", + "id2" + ], + "user_id": "test user id", + "processing_completed": { + "feature_importance": false, + "value_quant": false, + "excluded_analyses": [ + { + "analysis_id": "analysis id", + "reason": "analysis_demand_type_inconsistent", + "excluded_from": [ + "feature_importance", + "value_quant" + ] + }, + { + "analysis_id": "analysis id", + "reason": "analysis_demand_type_inconsistent", + "excluded_from": [ + "feature_importance", + "value_quant" + ] + } + ] + }, + "readiness_status": "failed", + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": null, + "unit_descriptor": "omitted", + "currency_code": "USD" + }, + "status": "active", + "create_dt": "2024-05-03T01:17:21.981509Z", + "update_dt": "2024-11-04T03:35:35Z", + "processed_dt": "2024-11-04T03:36:03Z" + }, + { + "group_id": "abc123", + "name": "test group name", + "analysis_ids": [ + "id1", + "id2" + ], + "user_id": "test user id", + "processing_completed": { + "feature_importance": true, + "value_quant": true, + "excluded_analyses": [] + }, + "readiness_status": "ready", + "demand_type": { + "interval": "week", + "week_start_day": "monday", + "industry": null, + "unit_descriptor": "omitted", + "currency_code": "USD" + }, + "status": "active", + "create_dt": "2024-07-17T21:27:50.392447Z", + "update_dt": "2024-10-16T21:31:59Z", + "processed_dt": "2024-10-16T21:40:03Z" + }, + { + "group_id": "abc123", + "name": "test group name", + "analysis_ids": [ + "id1", + "id2" + ], + "user_id": "test user id", + "processing_completed": { + "feature_importance": true, + "value_quant": true, + "excluded_analyses": [] + }, + "readiness_status": "ready", + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": null, + "unit_descriptor": "omitted", + "currency_code": "USD" + }, + "status": "active", + "create_dt": "2024-05-06T00:10:40.455098Z", + "update_dt": "2024-10-16T21:21:02Z", + "processed_dt": "2024-10-16T21:36:26Z" + }, + { + "group_id": "abc123", + "name": "test group name", + "analysis_ids": [ + "id1", + "id2" + ], + "user_id": "test user id", + "processing_completed": { + "feature_importance": true, + "value_quant": true, + "excluded_analyses": [] + }, + "readiness_status": "ready", + "demand_type": { + "interval": "week", + "week_start_day": "monday", + "industry": null, + "unit_descriptor": "omitted", + "currency_code": "USD" + }, + "status": "active", + "create_dt": "2024-05-16T20:07:33.503432Z", + "update_dt": "2024-10-16T21:14:41Z", + "processed_dt": "2024-10-16T21:19:15Z" + }, + { + "group_id": "abc123", + "name": "test group name", + "analysis_ids": [ + "id1", + "id2" + ], + "user_id": "test user id", + "processing_completed": { + "feature_importance": true, + "value_quant": true, + "excluded_analyses": [] + }, + "readiness_status": "ready", + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": null, + "unit_descriptor": "omitted", + "currency_code": "USD" + }, + "status": "active", + "create_dt": "2024-05-08T00:04:55.402304Z", + "update_dt": "2024-10-16T21:14:05Z", + "processed_dt": "2024-10-16T21:19:06Z" + }, + { + "group_id": "abc123", + "name": "test group name", + "analysis_ids": [ + "id1", + "id2" + ], + "user_id": "test user id", + "processing_completed": { + "feature_importance": true, + "value_quant": true, + "excluded_analyses": [] + }, + "readiness_status": "ready", + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": null, + "unit_descriptor": "omitted", + "currency_code": "USD" + }, + "status": "active", + "create_dt": "2024-05-08T00:06:04.760846Z", + "update_dt": "2024-10-16T21:14:05Z", + "processed_dt": "2024-10-16T21:19:05Z" + }, + { + "group_id": "abc123", + "name": "test group name", + "analysis_ids": [ + "id1", + "id2" + ], + "user_id": "test user id", + "processing_completed": { + "feature_importance": true, + "value_quant": true, + "excluded_analyses": [] + }, + "readiness_status": "ready", + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": null, + "unit_descriptor": "omitted", + "currency_code": "USD" + }, + "status": "active", + "create_dt": "2024-05-07T02:32:16.855072Z", + "update_dt": "2024-10-16T21:13:58Z", + "processed_dt": "2024-10-16T21:31:01Z" + } + ] +} \ No newline at end of file diff --git a/tests/fixtures/requests_responses/beam_test/test_search.json b/tests/fixtures/requests_responses/beam_test/test_search.json new file mode 100644 index 0000000..698e2dc --- /dev/null +++ b/tests/fixtures/requests_responses/beam_test/test_search.json @@ -0,0 +1,673 @@ +{ + "count": 1142, + "analyses": [ + { + "analysis_id": "abc123", + "name": "test name", + "location": { + "geopoint": { + "lat": "37.7749", + "lon": "-122.4194" + }, + "radius": 3345.52, + "unit": "m", + "google_place_id": null + }, + "rank": { + "type": "local", + "levels": { + "phq": null, + "local": { + "min": 35, + "max": null + } + } + }, + "user_id": null, + "access_type": "limited", + "status": "active", + "readiness_status": null, + "readiness_checks": { + "date_range": null, + "error_code": null, + "missing_dates": null, + "validation_response": null, + "best_practice": false, + "best_practice_checks": { + "industry": false, + "rank": true, + "radius": true + } + }, + "processing_completed": { + "correlation": false, + "feature_importance": false, + "value_quant": false + }, + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": "other", + "unit_descriptor": "omitted", + "currency_code": "USD", + "unit_currency_multiplier": 1.0 + }, + "group_ids": null, + "tz": null, + "create_dt": "2025-01-15T23:45:01Z", + "update_dt": "2025-01-15T23:45:01Z", + "processed_dt": null, + "external_id": null, + "label": null + }, + { + "analysis_id": "abc123", + "name": "test name", + "location": { + "geopoint": { + "lat": "37.7749", + "lon": "-122.4194" + }, + "radius": 3345.52, + "unit": "m", + "google_place_id": null + }, + "rank": { + "type": "local", + "levels": { + "phq": null, + "local": { + "min": 35, + "max": null + } + } + }, + "user_id": null, + "access_type": "full", + "status": "active", + "readiness_status": "ready", + "readiness_checks": { + "date_range": { + "start": "2019-01-01T00:00:00", + "end": "2021-09-26T00:00:00" + }, + "error_code": null, + "missing_dates": [], + "validation_response": { + "missing_data_percentage": 0.0, + "consecutive_nan": 0 + }, + "best_practice": false, + "best_practice_checks": { + "industry": false, + "rank": true, + "radius": true + } + }, + "processing_completed": { + "correlation": true, + "feature_importance": true, + "value_quant": true + }, + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": "other", + "unit_descriptor": "omitted", + "currency_code": "USD", + "unit_currency_multiplier": 1.0 + }, + "group_ids": null, + "tz": "America/Los_Angeles", + "create_dt": "2025-01-15T00:30:44Z", + "update_dt": "2025-01-15T00:53:48Z", + "processed_dt": "2025-01-15T00:53:30Z", + "external_id": null, + "label": null + }, + { + "analysis_id": "abc123", + "name": "test name", + "location": { + "geopoint": { + "lat": "38.9808979", + "lon": "-76.5400824" + }, + "radius": 9.23, + "unit": "mi", + "google_place_id": null + }, + "rank": { + "type": "phq", + "levels": { + "phq": { + "min": 35, + "max": null + }, + "local": null + } + }, + "user_id": null, + "access_type": "full", + "status": "active", + "readiness_status": "ready", + "readiness_checks": { + "date_range": { + "start": "2024-01-01T00:00:00", + "end": "2024-11-12T00:00:00" + }, + "error_code": null, + "missing_dates": [], + "validation_response": { + "missing_data_percentage": 0.0, + "consecutive_nan": 0 + }, + "best_practice": true, + "best_practice_checks": { + "industry": true, + "rank": true, + "radius": true + } + }, + "processing_completed": { + "correlation": true, + "feature_importance": true, + "value_quant": true + }, + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": "accommodation", + "unit_descriptor": "omitted", + "currency_code": "USD", + "unit_currency_multiplier": 1.0 + }, + "group_ids": null, + "tz": "America/New_York", + "create_dt": "2024-12-17T02:11:22Z", + "update_dt": "2025-01-14T02:11:24Z", + "processed_dt": "2025-01-14T02:11:15Z", + "external_id": null, + "label": null + }, + { + "analysis_id": "abc123", + "name": "test name", + "location": { + "geopoint": { + "lat": "41.4917972", + "lon": "-72.0903866" + }, + "radius": 7.14, + "unit": "mi", + "google_place_id": null + }, + "rank": { + "type": "local", + "levels": { + "phq": null, + "local": { + "min": 50, + "max": null + } + } + }, + "user_id": null, + "access_type": "full", + "status": "active", + "readiness_status": "ready", + "readiness_checks": { + "date_range": { + "start": "2019-06-01T00:00:00", + "end": "2019-12-31T00:00:00" + }, + "error_code": null, + "missing_dates": [ + "2019-11-28", + "2019-12-25" + ], + "validation_response": { + "missing_data_percentage": 0.935, + "consecutive_nan": 1 + }, + "best_practice": true, + "best_practice_checks": { + "industry": true, + "rank": true, + "radius": true + } + }, + "processing_completed": { + "correlation": true, + "feature_importance": true, + "value_quant": true + }, + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": "accommodation", + "unit_descriptor": "omitted", + "currency_code": "USD", + "unit_currency_multiplier": 1.0 + }, + "group_ids": null, + "tz": "America/New_York", + "create_dt": "2025-01-08T03:02:14Z", + "update_dt": "2025-01-08T03:03:43Z", + "processed_dt": "2025-01-08T03:03:33Z", + "external_id": null, + "label": null + }, + { + "analysis_id": "abc123", + "name": "test name", + "location": { + "geopoint": { + "lat": "41.657871", + "lon": "-91.534637" + }, + "radius": 1.76, + "unit": "km", + "google_place_id": null + }, + "rank": { + "type": "phq", + "levels": { + "phq": { + "min": 30, + "max": 100 + }, + "local": null + } + }, + "user_id": null, + "access_type": "full", + "status": "draft", + "readiness_status": "ready", + "readiness_checks": { + "date_range": { + "start": "2021-06-01T00:00:00", + "end": "2022-07-04T00:00:00" + }, + "error_code": null, + "missing_dates": [ + "2022-01-15", + "2022-01-16" + ], + "validation_response": { + "missing_data_percentage": 0.501, + "consecutive_nan": 2 + }, + "best_practice": false, + "best_practice_checks": { + "industry": false, + "rank": false, + "radius": false + } + }, + "processing_completed": { + "correlation": true, + "feature_importance": true, + "value_quant": true + }, + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": null, + "unit_descriptor": "", + "currency_code": "USD", + "unit_currency_multiplier": 75.0 + }, + "group_ids": null, + "tz": "America/Chicago", + "create_dt": "2022-09-15T03:21:44.436078Z", + "update_dt": "2024-12-16T23:02:47Z", + "processed_dt": "2024-12-16T23:02:30Z", + "external_id": null, + "label": null + }, + { + "analysis_id": "abc123", + "name": "test name", + "location": { + "geopoint": { + "lat": "25.793463", + "lon": "-80.337645" + }, + "radius": 10.0, + "unit": "km", + "google_place_id": null + }, + "rank": { + "type": "phq", + "levels": { + "phq": { + "min": 30, + "max": null + }, + "local": null + } + }, + "user_id": null, + "access_type": "full", + "status": "draft", + "readiness_status": "ready", + "readiness_checks": { + "date_range": { + "start": "2017-01-02T00:00:00", + "end": "2019-12-31T00:00:00" + }, + "error_code": null, + "missing_dates": [ + "2017-04-16", + "2017-09-08", + "2017-09-09", + "2017-09-10", + "2017-09-11", + "2017-09-12", + "2017-11-23", + "2017-12-25", + "2018-04-01", + "2018-11-22", + "2018-12-25", + "2019-04-21", + "2019-09-02", + "2019-11-28", + "2019-12-25" + ], + "validation_response": { + "missing_data_percentage": 1.371, + "consecutive_nan": 5 + }, + "best_practice": false, + "best_practice_checks": { + "industry": true, + "rank": false, + "radius": false + } + }, + "processing_completed": { + "correlation": true, + "feature_importance": true, + "value_quant": true + }, + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": "retail", + "unit_descriptor": "Rooms", + "currency_code": "USD", + "unit_currency_multiplier": 1.0 + }, + "group_ids": [ + "nbH6wEfXKA4" + ], + "tz": "America/New_York", + "create_dt": "2024-08-11T22:41:03.603998Z", + "update_dt": "2024-12-16T19:32:49Z", + "processed_dt": "2024-12-16T19:32:12Z", + "external_id": null, + "label": null + }, + { + "analysis_id": "abc123", + "name": "test name", + "location": { + "geopoint": { + "lat": "-36.84440239999999", + "lon": "174.7648646" + }, + "radius": 2.28, + "unit": "mi", + "google_place_id": null + }, + "rank": { + "type": "phq", + "levels": { + "phq": { + "min": 35, + "max": null + }, + "local": null + } + }, + "user_id": null, + "access_type": "full", + "status": "active", + "readiness_status": "ready", + "readiness_checks": { + "date_range": { + "start": "2019-06-01T00:00:00", + "end": "2019-12-31T00:00:00" + }, + "error_code": null, + "missing_dates": [ + "2019-11-28", + "2019-12-25" + ], + "validation_response": { + "missing_data_percentage": 0.935, + "consecutive_nan": 1 + }, + "best_practice": false, + "best_practice_checks": { + "industry": true, + "rank": false, + "radius": false + } + }, + "processing_completed": { + "correlation": true, + "feature_importance": true, + "value_quant": true + }, + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": "parking", + "unit_descriptor": "omitted", + "currency_code": "USD", + "unit_currency_multiplier": 1.0 + }, + "group_ids": null, + "tz": "Pacific/Auckland", + "create_dt": "2024-10-06T20:42:57Z", + "update_dt": "2024-12-16T19:32:03Z", + "processed_dt": "2024-12-16T19:31:52Z", + "external_id": null, + "label": null + }, + { + "analysis_id": "abc123", + "name": "test name", + "location": { + "geopoint": { + "lat": "38.9808979", + "lon": "-76.5400824" + }, + "radius": 9.23, + "unit": "mi", + "google_place_id": null + }, + "rank": { + "type": "phq", + "levels": { + "phq": { + "min": 35, + "max": null + }, + "local": null + } + }, + "user_id": null, + "access_type": "limited", + "status": "active", + "readiness_status": null, + "readiness_checks": { + "date_range": null, + "error_code": null, + "missing_dates": null, + "validation_response": null, + "best_practice": true, + "best_practice_checks": { + "industry": true, + "rank": true, + "radius": true + } + }, + "processing_completed": { + "correlation": false, + "feature_importance": false, + "value_quant": false + }, + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": "accommodation", + "unit_descriptor": "omitted", + "currency_code": "USD", + "unit_currency_multiplier": 1.0 + }, + "group_ids": null, + "tz": null, + "create_dt": "2024-12-16T02:08:29Z", + "update_dt": "2024-12-16T02:08:29Z", + "processed_dt": null, + "external_id": null, + "label": null + }, + { + "analysis_id": "abc123", + "name": "test name", + "location": { + "geopoint": { + "lat": "-36.848448", + "lon": "174.7596161" + }, + "radius": 3418.29, + "unit": "m", + "google_place_id": null + }, + "rank": { + "type": "local", + "levels": { + "phq": null, + "local": { + "min": 30, + "max": null + } + } + }, + "user_id": null, + "access_type": "full", + "status": "active", + "readiness_status": "ready", + "readiness_checks": { + "date_range": { + "start": "2024-01-01T00:00:00", + "end": "2024-07-01T00:00:00" + }, + "error_code": null, + "missing_dates": [], + "validation_response": { + "missing_data_percentage": 0.0, + "consecutive_nan": 0 + }, + "best_practice": false, + "best_practice_checks": { + "industry": true, + "rank": false, + "radius": true + } + }, + "processing_completed": { + "correlation": true, + "feature_importance": true, + "value_quant": true + }, + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": "retail", + "unit_descriptor": "omitted", + "currency_code": "USD", + "unit_currency_multiplier": 1.0 + }, + "group_ids": null, + "tz": "Pacific/Auckland", + "create_dt": "2024-12-16T01:58:18Z", + "update_dt": "2024-12-16T01:58:50Z", + "processed_dt": "2024-12-16T01:58:39Z", + "external_id": null, + "label": null + }, + { + "analysis_id": "abc123", + "name": "test name", + "location": { + "geopoint": { + "lat": "-36.84440239999999", + "lon": "174.7648646" + }, + "radius": 4.04, + "unit": "km", + "google_place_id": null + }, + "rank": { + "type": "phq", + "levels": { + "phq": { + "min": 35, + "max": null + }, + "local": null + } + }, + "user_id": null, + "access_type": "full", + "status": "active", + "readiness_status": "ready", + "readiness_checks": { + "date_range": { + "start": "2019-06-01T00:00:00", + "end": "2019-12-31T00:00:00" + }, + "error_code": null, + "missing_dates": [ + "2019-11-28", + "2019-12-25" + ], + "validation_response": { + "missing_data_percentage": 0.935, + "consecutive_nan": 1 + }, + "best_practice": false, + "best_practice_checks": { + "industry": true, + "rank": false, + "radius": false + } + }, + "processing_completed": { + "correlation": true, + "feature_importance": true, + "value_quant": true + }, + "demand_type": { + "interval": "day", + "week_start_day": null, + "industry": "accommodation", + "unit_descriptor": "Sales", + "currency_code": "EUR", + "unit_currency_multiplier": 1.0 + }, + "group_ids": null, + "tz": "Pacific/Auckland", + "create_dt": "2024-12-15T20:36:50Z", + "update_dt": "2024-12-16T01:46:37Z", + "processed_dt": "2024-12-16T01:46:26Z", + "external_id": null, + "label": null + } + ] +} \ No newline at end of file diff --git a/tests/test_client.py b/tests/test_client.py index 24a10a5..38c2f4c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -22,7 +22,7 @@ def tearDown(self): @with_config(ENDPOINT_URL="https://api.predicthq.com") def test_build_url(self): - assert self.client.build_url("v1") == "https://api.predicthq.com/v1/" + assert self.client.build_url("v1") == "https://api.predicthq.com/v1" def test_endpoints_initialization(self): assert isinstance(self.client.oauth2, endpoints.OAuth2Endpoint) diff --git a/usecases/beam/analysis/basics.py b/usecases/beam/analysis/basics.py new file mode 100644 index 0000000..69d4046 --- /dev/null +++ b/usecases/beam/analysis/basics.py @@ -0,0 +1,73 @@ +from predicthq import Client + +# Please copy paste your access token here +# or read our Quickstart documentation if you don't have a token yet +# https://docs.predicthq.com/guides/quickstart/ +ACCESS_TOKEN = "abc123" + +phq = Client(access_token=ACCESS_TOKEN) + +# Create a new analysis +# The created analysis id will be returned +analysis = phq.beam.analysis.create( + name="New Analysis", location__geopoint={"lat": "37.7749", "lon": "-122.4194"} +) +print(analysis.model_dump(exclude_none=True)) + + +# Upload demand data to an analysis +# The demand data can be uploaded from a CSV, NDJSON or JSON file file.abs +# The sdk accepts both a string file path or a file object.abs +# Upload demand data from a CSV file with a string file path +phq.beam.analysis.upload_demand(analysis_id="abc123", csv="./demand.csv") +# Upload demand data from a CSV file with a file object +with open("./demand.csv") as f: + phq.beam.analysis.upload_demand(analysis_id="abc123", csv=f) + +# Upload demand data from a NDJSON file with a string file path +phq.beam.analysis.upload_demand(analysis_id="abc123", ndjson="./demand.ndjson") +# Upload demand data from a NDJSON file with a file object +with open("./demand.ndjson") as f: + phq.beam.analysis.upload_demand(analysis_id="abc123", ndjson=f) + +# Upload demand data from a JSON file with a string file path +phq.beam.analysis.upload_demand(analysis_id="abc123", json="./demand.json") +# Upload demand data from a JSON file with a file object +with open("./demand.json") as f: + phq.beam.analysis.upload_demand(analysis_id="abc123", json=f) + +# Get an analysis +analysis = phq.beam.analysis.get(analysis_id="abc123") +print(analysis.model_dump(exclude_none=True)) + + +# Search for analyses +# The search() method returns an AnalysisResultSet which allows you to iterate +# over the first page of Analysis objects (10 analyses by default) +for analysis in phq.beam.analysis.search(q="My analysis").iter_all(): + print(analysis.model_dump(exclude_none=True)) + + +# Update an analysis +phq.beam.analysis.update(analysis_id="abc123", name="Updated Analysis") + + +# Delete an analysis +phq.beam.analysis.delete(analysis_id="abc123") + + +# Refresh an analysis +phq.beam.analysis.refresh(analysis_id="abc123") + + +# Get correlation results +# The get_correlation_results() method returns a CorrelationResultSet which allows you to iterate +for correlation_result in phq.beam.analysis.get_correlation_results( + analysis_id="abc123", date__gt="2024-01-01", date__lt="2024-01-31" +).iter_all(): + print(correlation_result.model_dump(exclude_none=True)) + + +# Get feature importance results +feature_importance = phq.beam.analysis.get_feature_importance(analysis_id="abc123") +print(feature_importance.model_dump(exclude_none=True)) diff --git a/usecases/beam/analysis_group/basics.py b/usecases/beam/analysis_group/basics.py new file mode 100644 index 0000000..c17a8eb --- /dev/null +++ b/usecases/beam/analysis_group/basics.py @@ -0,0 +1,46 @@ +from predicthq import Client + +# Please copy paste your access token here +# or read our Quickstart documentation if you don't have a token yet +# https://docs.predicthq.com/guides/quickstart/ +ACCESS_TOKEN = "abc123" + +phq = Client(access_token=ACCESS_TOKEN) + +# Create a new analysis group +# The created group id will be returned +group = phq.beam.analysis_group.create( + name="New Analysis Group", analysis_ids=["abc123", "def456"] +) +print(group.model_dump(exclude_none=True)) + + +# Get an analysis group +group = phq.beam.analysis_group.get(group_id="abc123") +print(group.model_dump(exclude_none=True)) + + +# Search for analysis groups +# The search() method returns an AnalysisGroupResultSet which allows you to iterate +# over the first page of AnalysisGroup objects (10 groups by default) +for group in phq.beam.analysis_group.search(q="My analyis group").iter_all(): + print(group.model_dump(exclude_none=True)) + + +# Update an analysis group +phq.beam.analysis_group.update(group_id="abc123", name="Updated Analysis Group") + + +# Delete an analysis group +phq.beam.analysis_group.delete(group_id="abc123") + + +# Refresh an analysis group +phq.beam.analysis_group.refresh(group_id="abc123") + + +# Get feature importance results +group_feature_importance = phq.beam.analysis_group.get_feature_importance( + group_id="abc123" +) +print(group_feature_importance.model_dump(exclude_none=True)) diff --git a/usecases/features/get_features_for_criteria.py b/usecases/features/get_features_for_criteria.py index 2e641d7..38775ca 100644 --- a/usecases/features/get_features_for_criteria.py +++ b/usecases/features/get_features_for_criteria.py @@ -71,3 +71,13 @@ feature.phq_viewership_sports_american_football.stats.median, feature.phq_viewership_sports_basketball_nba.stats.count, feature.phq_viewership_sports_basketball_nba.stats.median) + +# Convert the obtained features to a CSV file called features.csv in the current directory +phq.features.obtain_features( + active__gte="2019-11-28", + active__lte="2020-01-05", + location__geo={"lon": -71.49978, "lat": 41.62064, "radius": "150km"}, + phq_rank_public_holidays=True, + phq_attendance_sports__stats=["count", "median"], + phq_attendance_sports__phq_rank={"gt": 50}, +).to_csv("./features.csv", mode="w+")