Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cleanup: move config to python code #45

Merged
merged 1 commit into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion accounts/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin
from django.utils.translation import gettext_lazy as _


from .models import CustomUser


Expand Down
3 changes: 1 addition & 2 deletions accounts/backends.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model

from django.contrib.auth.backends import ModelBackend

UserModel = get_user_model()

Expand Down
120 changes: 120 additions & 0 deletions d.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import logging
from dataclasses import dataclass
from enum import Enum
from typing import Optional

logger = logging.getLogger(__name__)


DEFAULT_START = -1
DEFAULT_END = 30


@dataclass
class AgeBracket:
index: int
_name: Optional[str] = None
start: Optional[int] = DEFAULT_START
end: Optional[int] = DEFAULT_END
_length_in_days: Optional[int] = None

@property
def length_in_days(self):
if self._length_in_days is not None:
return self._length_in_days
return (self.end - self.start) * 365

@property
def name(self):
if self._name is not None:
return self._name
return f"{self.start} to {self.end}"

@property
def daily_probability(self):
return 1 / self.length_in_days

def __str__(self):
return self.name

def __repr__(self):
return f"<{self.__class__.__name__}: {self.name}>"


class AgeBrackets(Enum):
"""
Age Brackets Enum.

to get a age bracket dataclass:
ab = AgeBrackets.BIRTH_TO_ONE.value
# ab = <AgeBracket: Birth to 1>


if you do AgeBrackets.BIRTH_TO_ONE that's actually the Enum member, not the AgeBracket:
not_ab = AgeBrackets.BIRTH_TO_ONE
# not_ab = <AgeBrackets.BIRTH_TO_ONE: <AgeBracket: Fostering> != <PlacementCategory: Fostering>


to get a placement category name:
pc_name = PlacementCategories.FOSTERING.value.name
# pc = "Fostering"

if you do PlacementCategories.FOSTERING.name that's actually the name of the Enum member, not the name of the PlacementCategory:
not_pc_name = PlacementCategories.FOSTERING.name
# not_pc_name = "FOSTERING" != "Fostering"

"""

BIRTH_TO_ONE = AgeBracket(_name="Birth to 1", end=1, index=0, _length_in_days=365)
ONE_TO_FIVE = AgeBracket(start=1, end=5, index=1)
FIVE_TO_TEN = AgeBracket(start=5, end=10, index=2)
TEN_TO_SIXTEEN = AgeBracket(start=10, end=16, index=3)
SIXTEEN_TO_EIGHTEEN = AgeBracket(_name="16 to 18+", start=16, index=4)

@classmethod
def values(cls) -> list[AgeBracket]:
return [a.value for a in cls._members_by_index()]

@classmethod
def _members_by_index(cls) -> list["AgeBrackets"]:
"""
Returns a list of all members of the enum ordered by index.
"""
return sorted(list(cls.__members__.values()), key=lambda x: x.value.index)

@property
def next(self) -> Optional["AgeBrackets"]:
"""
Returns the next AgeBrackets in the enum.
If the current AgeBrackets is the last one, returns None.
"""
members = self._members_by_index()
current_index = members.index(self)
if current_index == len(members) - 1:
return None
return members[current_index + 1]

@property
def previous(self) -> Optional["AgeBrackets"]:
"""
Returns the previous AgeBrackets in the enum.
If the current AgeBrackets is the first one, returns None.
"""
members = self._members_by_index()
current_index = members.index(self)
if current_index == 0:
return None
return members[current_index - 1]

@classmethod
def bracket_for_age(cls, age: float) -> Optional[AgeBracket]:
for bracket in cls:
if bracket.value.start <= age < bracket.value.end:
return bracket.value
return None


d = AgeBrackets.BIRTH_TO_ONE.name


print(type(d))
15 changes: 5 additions & 10 deletions dm_regional_app/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from dm_regional_app.forms import HistoricDataFilter, PredictFilter
from dm_regional_app.models import SavedScenario, SessionScenario
from dm_regional_app.utils import apply_filters
from ssda903 import Config
from ssda903.config import PlacementCategories
from ssda903.population_stats import PopulationStats
from ssda903.predictor import predict
from ssda903.reader import read_data
Expand Down Expand Up @@ -146,8 +146,7 @@ def prediction(request):
else:
empty_dataframe = False

config = Config()
stats = PopulationStats(historic_data, config)
stats = PopulationStats(historic_data)

# Call predict function with default dates
prediction = predict(
Expand Down Expand Up @@ -181,7 +180,6 @@ def historic_data(request):
if "session_scenario_id" in request.session:
pk = request.session["session_scenario_id"]
session_scenario = get_object_or_404(SessionScenario, pk=pk)
config = Config()
# read data
datacontainer = read_data(source=settings.DATA_SOURCE)

Expand Down Expand Up @@ -218,16 +216,13 @@ def historic_data(request):
data = apply_filters(datacontainer.enriched_view, form.initial)

entry_into_care_count = data.loc[
data.placement_type_before
== datacontainer.config.PlacementCategories.NOT_IN_CARE
data.placement_type_before == PlacementCategories.NOT_IN_CARE.value.label
]["CHILD"].nunique()
exiting_care_count = data.loc[
data.placement_type_after
== datacontainer.config.PlacementCategories.NOT_IN_CARE
data.placement_type_after == PlacementCategories.NOT_IN_CARE.value.label
]["CHILD"].nunique()

config = Config()
stats = PopulationStats(data, config)
stats = PopulationStats(data)

chart = historic_chart(stats)

Expand Down
2 changes: 0 additions & 2 deletions ssda903/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
from .config import Config
from .datacontainer import DemandModellingDataContainer
from .datastore import StorageDataStore
from .population_stats import PopulationStats

__all__ = [
"DemandModellingDataContainer",
"PopulationStats",
"Config",
"StorageDataStore",
]
7 changes: 5 additions & 2 deletions ssda903/config/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
from ._configuration_loader import Config
from ._age_brackets import AgeBrackets
from ._costs import Costs
from ._placement_categories import PlacementCategories

__all__ = ["Config"]
YEAR_IN_DAYS = 365.24
__all__ = ["AgeBrackets", "PlacementCategories", "Costs", "YEAR_IN_DAYS"]
157 changes: 88 additions & 69 deletions ssda903/config/_age_brackets.py
Original file line number Diff line number Diff line change
@@ -1,96 +1,115 @@
import logging
from dataclasses import dataclass
from enum import Enum
from typing import Any, Dict, Optional, Tuple

from ._placement_categories import PlacementCategories
from typing import Optional

logger = logging.getLogger(__name__)


class AgeBrackets(Enum):
def __init__(self, config):
PlacementCategories = config.get("placement_categories", [])
logger.debug("Configuring AgeBracket with %s", config)

self.__index = config["index"]
self.__start = config.get("min", -1)
self.__end = config.get("max", 30)
self.__label = config.get("label", f"{self.start} to {self.end}")
self.__length_in_days = config.get(
"length_in_days", (self.end - self.start) * 365
)
self.__placement_categories = tuple(
[PlacementCategories[cat] for cat in config.get("categories", [])]
+ [PlacementCategories.OTHER]
)
self.__index = config.get("index", 0)
self._value_ = self.__label
DEFAULT_START = -1
DEFAULT_END = 30

@property
def start(self) -> int:
return self.__start

@property
def end(self) -> int:
return self.__end
@dataclass
class AgeBracket:
index: int
_label: Optional[str] = None
start: Optional[int] = DEFAULT_START
end: Optional[int] = DEFAULT_END
_length_in_days: Optional[int] = None

@property
def placement_categories(self) -> Tuple[PlacementCategories]:
return self.__placement_categories
def length_in_days(self):
if self._length_in_days is not None:
return self._length_in_days
return (self.end - self.start) * 365

@property
def label(self):
return self.__label
if self._label is not None:
return self._label
return f"{self.start} to {self.end}"

@property
def index(self):
return self.__index
def daily_probability(self):
return 1 / self.length_in_days

@property
def next(self):
return type(self).for_index(self.index + 1)
def __str__(self):
return self.label

@property
def previous(self):
return type(self).for_index(self.index - 1)
def __repr__(self):
return f"<{self.__class__.__name__}: {self.label}>"

@property
def length_in_days(self):
return self.__length_in_days

@property
def daily_probability(self):
return 1 / self.length_in_days
class AgeBrackets(Enum):
"""
Age Brackets Enum.

@classmethod
def bracket_for_age(cls, age: float) -> Optional["AgeBrackets"]:
for bracket in cls:
if bracket.start <= age < bracket.end:
return bracket
return None
to get a age bracket dataclass:
ab = AgeBrackets.BIRTH_TO_ONE.value
ab will be <AgeBracket: Birth to 1>

@classmethod
def for_index(cls, index: int) -> Optional["AgeBrackets"]:
for bracket in cls:
if bracket.index == index:
return bracket
return None

def __str__(self):
return self.label
if you do AgeBrackets.BIRTH_TO_ONE that's actually the Enum member, not the AgeBracket:
not_ab = AgeBrackets.BIRTH_TO_ONE
not_ab will be <AgeBrackets.BIRTH_TO_ONE: <AgeBracket: Birth to 1>
which is not the same as <AgeBracket: Birth to 1>

def __repr__(self):
return f"<{self.__class__.__name__}.{self.name}: {self.label}>"
to get a age bracket label:
ab_label = AgeBrackets.BIRTH_TO_ONE.value.label
ab_label will be "Birth to 1"

def __lt__(self, other):
return self.__index < other.__index
if you do AgeBrackets.BIRTH_TO_ONE.name that's actually the name of the Enum member, not the label of the AgeBracket:
not_ab_label = AgeBrackets.BIRTH_TO_ONE.name
not_ab_label will be "BIRTH_TO_ONE"
which is not the same as "Birth to 1"

"""

def build_age_brackets(config: Dict[str, Any], placement_categories=None):
config = config.copy()
for ix, v in enumerate(config.values()):
v["index"] = ix
if placement_categories:
v["placement_categories"] = placement_categories
BIRTH_TO_ONE = AgeBracket(_label="Birth to 1", end=1, index=0, _length_in_days=365)
ONE_TO_FIVE = AgeBracket(start=1, end=5, index=1)
FIVE_TO_TEN = AgeBracket(start=5, end=10, index=2)
TEN_TO_SIXTEEN = AgeBracket(start=10, end=16, index=3)
SIXTEEN_TO_EIGHTEEN = AgeBracket(_label="16 to 18+", start=16, index=4)

@classmethod
def values(cls) -> list[AgeBracket]:
return [a.value for a in cls._members_by_index()]

return AgeBrackets("AgeBrackets", config)
@classmethod
def _members_by_index(cls) -> list["AgeBrackets"]:
"""
Returns a list of all members of the enum ordered by index.
"""
return sorted(list(cls.__members__.values()), key=lambda x: x.value.index)

@property
def next(self) -> Optional["AgeBrackets"]:
"""
Returns the next AgeBrackets in the enum.
If the current AgeBrackets is the last one, returns None.
"""
members = self._members_by_index()
current_index = members.index(self)
if current_index == len(members) - 1:
return None
return members[current_index + 1]

@property
def previous(self) -> Optional["AgeBrackets"]:
"""
Returns the previous AgeBrackets in the enum.
If the current AgeBrackets is the first one, returns None.
"""
members = self._members_by_index()
current_index = members.index(self)
if current_index == 0:
return None
return members[current_index - 1]

@classmethod
def bracket_for_age(cls, age: float) -> Optional[AgeBracket]:
for bracket in cls:
if bracket.value.start <= age < bracket.value.end:
return bracket.value
return None
Loading
Loading