Skip to content

Commit

Permalink
Merge branch 'main' into 44-add-adjusted-forecast-scenario-page
Browse files Browse the repository at this point in the history
  • Loading branch information
amynickolls committed Jun 26, 2024
2 parents bdb2501 + 8b1ada6 commit ca03c34
Show file tree
Hide file tree
Showing 21 changed files with 548 additions and 507 deletions.
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 @@ -17,7 +17,7 @@
from dm_regional_app.forms import DynamicForm, 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 @@ -569,8 +569,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 @@ -604,7 +603,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 @@ -641,16 +639,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

0 comments on commit ca03c34

Please sign in to comment.