Skip to content

Commit

Permalink
Add full back-reference from dataset to campaign.
Browse files Browse the repository at this point in the history
  • Loading branch information
riga committed Feb 19, 2024
1 parent 8c8cc88 commit 5531bca
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 42 deletions.
35 changes: 35 additions & 0 deletions order/adapters/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,41 @@ class OrderAdapter(Adapter):
needs_data_location = True


class CampaignAdapter(OrderAdapter):

name = "order_campaign"

def retrieve_data(
self,
data_location: str,
*,
campaign_name: str,
) -> Materialized:
# only supporting local evaluation for now
if not self.location_is_local(data_location):
raise NotImplementedError(f"non-local location {data_location} not handled by {self!r}")

# build the yaml file path
path = os.path.join(
self.remove_scheme(data_location),
"campaigns",
f"{campaign_name}.yaml",
)
if not os.path.exists(path):
raise FileNotFoundError(f"campaign file {path} does not exist")

# open the file and look for the campaign
with open(path, "r") as f:
stream = yaml.load_all(f, Loader=yaml.SafeLoader)
for entry in stream:
if entry.get("name") == campaign_name:
return Materialized(campaign=entry)
# only one campaign per file allowed
break

raise Exception(f"no campaign entry with name '{campaign_name}' found in {path}")


class DatasetsAdapter(OrderAdapter):

name = "order_datasets"
Expand Down
14 changes: 13 additions & 1 deletion order/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,9 +133,15 @@ def fget(self):
# set the value
setattr(self, lazy_attr_, materialized[adapter_model_.key])

# get back the now instantiated value
value_ = getattr(self, lazy_attr_)

# invoke the on_materialize hook
self.on_materialize(attr_, value_, adapter_model_)

# assign it to the return value for the requested attribute
if attr_ == attr:
value = getattr(self, lazy_attr_)
value = value_

# complain if the return value was not set
if value == no_value:
Expand Down Expand Up @@ -347,3 +353,9 @@ def __repr_args__(self, *, verbose: bool = False, adapters: bool = False) -> Gen
value = self.__repr_adapter__(value)

yield attr, value

def on_materialize(self, attr: str, value: Any, adapter_model: AdapterModel) -> None:
"""
A custom hook that gets invoked when a lazy attribute is materialized.
"""
return
12 changes: 8 additions & 4 deletions order/models/campaign.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,13 @@ def _dataset_materialize_callback(self, dataset: Dataset) -> None:
dataset.campaign = self

def _dataset_add_callback(self, dataset: LazyDataset | Dataset) -> None:
if isinstance(dataset, Dataset):
dataset.campaign = self
if not isinstance(dataset, Dataset):
return

dataset.campaign = self

def _dataset_remove_callback(self, dataset: LazyDataset | Dataset) -> None:
if isinstance(dataset, Dataset):
dataset.campaign = None
if not isinstance(dataset, Dataset):
return

dataset.campaign = None
42 changes: 7 additions & 35 deletions order/models/dataset.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@
from pydantic import Field, field_validator

from order.types import (
Union, List, Dict, NonEmptyStrictStr, PositiveStrictInt, Lazy, ClassVar, GeneratorType,
Union, List, Dict, NonEmptyStrictStr, PositiveStrictInt, Lazy, ClassVar, GeneratorType, Any,
)
from order.util import validated
from order.models.base import Model
# from order.util import validated
from order.models.base import Model, AdapterModel
from order.models.unique import UniqueObjectBase, UniqueObject, LazyUniqueObject, UniqueObjectIndex


Expand Down Expand Up @@ -84,45 +84,17 @@ def validate_gen_order(cls, gen_order: str) -> str:

class Dataset(UniqueObject):

campaign: Lazy["Campaign"]
variations: Dict[str, DatasetVariation] = Field(frozen=True)

lazy_cls: ClassVar[UniqueObjectBase] = LazyDataset

@validated(default=None)
def campaign(self, campaign: "Campaign" | None) -> "Campaign":
# do nothing when the previous campaign matches the new one
if campaign == self.campaign:
return campaign

# deregister from previous campaign
if self.campaign:
self.campaign.datasets.remove(self, skip_callback=True)

# None case
if campaign is None:
return campaign

# type check
if not isinstance(campaign, Campaign):
raise TypeError(f"expected Campaign object, but got '{campaign}'")

# add to the index if not already in there
if self not in campaign.datasets:
campaign.datasets.add(self, skip_callback=True)

return campaign

def __getitem__(self, name: str) -> DatasetVariation:
return self.get_info(name)

def __repr_args__(self, verbose: bool = False, adapters: bool = False) -> GeneratorType:
"""
Yields all key-values pairs to be injected into the representation.
"""
yield from super().__repr_args__(verbose=verbose, adapters=adapters)

if self.campaign:
yield "campaign", self.campaign.name
def on_materialize(self, attr: str, value: Any, adapter_model: AdapterModel) -> None:
if attr == "campaign":
value.datasets.add(self, overwrite=True, skip_callback=True)

def get_variation(self, name: str) -> DatasetVariation:
return self.variations[name]
Expand Down
5 changes: 4 additions & 1 deletion order/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,10 @@ def make_strict(cls, type_: type) -> AnnotatedType:

@classmethod
def can_make_strict(cls, type_: type) -> bool:
if type_.__dict__.get("_name") in ("Dict", "List"):
if (
getattr(type_, "__dict__", None) is not None and
type_.__dict__.get("_name") in ("Dict", "List")
):
return False

return True
2 changes: 1 addition & 1 deletion order/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ class validated(property):
.. code-block:: python
class MyClass(object):
class MyClass(object):
def __init__(self):
self._foo: str = None
Expand Down

0 comments on commit 5531bca

Please sign in to comment.