Skip to content

Commit

Permalink
Use StrEnum for CompletionStatus/Use typed dict for progress (#1205)
Browse files Browse the repository at this point in the history
  • Loading branch information
MebinAbraham authored Sep 14, 2023
1 parent 735c453 commit 25c7518
Show file tree
Hide file tree
Showing 19 changed files with 697 additions and 686 deletions.
2 changes: 1 addition & 1 deletion app/data_models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from .answer import Answer, AnswerValueTypes
from .fulfilment_request import FulfilmentRequest
from .progress_store import CompletionStatus
from .progress import CompletionStatus
from .questionnaire_store import (
AnswerStore,
ListStore,
Expand Down
20 changes: 14 additions & 6 deletions app/data_models/progress.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
from __future__ import annotations

from dataclasses import dataclass
from enum import StrEnum
from typing import Mapping, Optional, TypedDict


class ProgressDictType(TypedDict, total=False):
class CompletionStatus(StrEnum):
COMPLETED: str = "COMPLETED"
IN_PROGRESS: str = "IN_PROGRESS"
NOT_STARTED: str = "NOT_STARTED"
INDIVIDUAL_RESPONSE_REQUESTED: str = "INDIVIDUAL_RESPONSE_REQUESTED"


class ProgressDict(TypedDict, total=False):
section_id: str
block_ids: list[str]
status: str
list_item_id: str
status: CompletionStatus
list_item_id: str | None


@dataclass
class Progress:
section_id: str
block_ids: list[str]
status: str
status: CompletionStatus
list_item_id: Optional[str] = None

@classmethod
def from_dict(cls, progress_dict: ProgressDictType) -> Progress:
def from_dict(cls, progress_dict: ProgressDict) -> Progress:
return cls(
section_id=progress_dict["section_id"],
block_ids=progress_dict["block_ids"],
status=progress_dict["status"],
status=CompletionStatus(progress_dict["status"]),
list_item_id=progress_dict.get("list_item_id"),
)

Expand Down
35 changes: 14 additions & 21 deletions app/data_models/progress_store.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,11 @@
from dataclasses import astuple, dataclass
from typing import Iterable, Iterator, MutableMapping
from typing import Iterable, MutableMapping

from app.data_models.progress import Progress, ProgressDictType
from app.data_models import CompletionStatus
from app.data_models.progress import Progress, ProgressDict
from app.questionnaire.location import Location, SectionKey
from app.utilities.types import LocationType


@dataclass
class CompletionStatus:
COMPLETED: str = "COMPLETED"
IN_PROGRESS: str = "IN_PROGRESS"
NOT_STARTED: str = "NOT_STARTED"
INDIVIDUAL_RESPONSE_REQUESTED: str = "INDIVIDUAL_RESPONSE_REQUESTED"

def __iter__(self) -> Iterator[tuple[str]]:
return iter(astuple(self))


class ProgressStore:
"""
An object that stores and updates references to sections and list items
Expand All @@ -25,7 +14,7 @@ class ProgressStore:

def __init__(
self,
progress: Iterable[ProgressDictType] | None = None,
progress: Iterable[ProgressDict] | None = None,
) -> None:
"""
Instantiate a ProgressStore object that tracks the progress status of Sections & Repeating Sections,
Expand All @@ -52,7 +41,7 @@ def __contains__(self, section_key: SectionKey) -> bool:

@staticmethod
def _build_map(
section_list: Iterable[ProgressDictType],
section_list: Iterable[ProgressDict],
) -> MutableMapping:
"""
Builds the ProgressStore's data structure from a list of section dictionaries.
Expand Down Expand Up @@ -100,14 +89,14 @@ def is_section_complete(self, section_key: SectionKey) -> bool:

def section_keys(
self,
statuses: Iterable[str] | None = None,
statuses: Iterable[CompletionStatus] | None = None,
section_ids: Iterable[str] | None = None,
) -> list[SectionKey]:
"""
Return the Keys of the Section and Repeating Blocks progresses stored in this ProgressStore.
"""
if not statuses:
statuses = {*CompletionStatus()}
statuses = CompletionStatus

section_keys = [
section_key
Expand All @@ -124,7 +113,9 @@ def section_keys(
if any(section_id in progress_key for section_id in section_ids)
]

def update_section_status(self, status: str, section_key: SectionKey) -> bool:
def update_section_status(
self, status: CompletionStatus, section_key: SectionKey
) -> bool:
"""
Updates the status of the Section or Repeating Blocks for a list item specified by the key based on the given section id and list item id.
"""
Expand All @@ -143,7 +134,7 @@ def update_section_status(self, status: str, section_key: SectionKey) -> bool:

return updated

def get_section_status(self, section_key: SectionKey) -> str:
def get_section_status(self, section_key: SectionKey) -> CompletionStatus:
"""
Return the CompletionStatus of the Section or Repeating Blocks for a list item,
specified by the given section_id and list_item_id in SectionKey.
Expand All @@ -154,7 +145,9 @@ def get_section_status(self, section_key: SectionKey) -> str:

return CompletionStatus.NOT_STARTED

def get_block_status(self, *, block_id: str, section_key: SectionKey) -> str:
def get_block_status(
self, *, block_id: str, section_key: SectionKey
) -> CompletionStatus:
"""
Return the completion status of the block specified by the given block_id,
if it is part of the progress of the given Section or Repeating Blocks for list item
Expand Down
3 changes: 2 additions & 1 deletion app/questionnaire/path_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

from werkzeug.datastructures import ImmutableDict

from app.data_models import CompletionStatus
from app.data_models.answer_store import AnswerStore
from app.data_models.list_store import ListStore
from app.data_models.metadata_proxy import MetadataProxy
from app.data_models.progress_store import CompletionStatus, ProgressStore
from app.data_models.progress_store import ProgressStore
from app.data_models.supplementary_data_store import SupplementaryDataStore
from app.questionnaire.location import Location, SectionKey
from app.questionnaire.questionnaire_schema import QuestionnaireSchema
Expand Down
3 changes: 1 addition & 2 deletions app/questionnaire/questionnaire_store_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
from ordered_set import OrderedSet
from werkzeug.datastructures import ImmutableDict

from app.data_models import AnswerValueTypes, QuestionnaireStore
from app.data_models import AnswerValueTypes, CompletionStatus, QuestionnaireStore
from app.data_models.answer_store import Answer
from app.data_models.progress_store import CompletionStatus
from app.data_models.relationship_store import RelationshipDict, RelationshipStore
from app.questionnaire import QuestionnaireSchema
from app.questionnaire.location import Location, SectionKey
Expand Down
6 changes: 3 additions & 3 deletions app/views/contexts/hub_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from flask_babel import lazy_gettext
from werkzeug.datastructures import ImmutableDict

from app.data_models.progress_store import CompletionStatus
from app.data_models import CompletionStatus
from app.questionnaire.location import SectionKey
from app.views.contexts import Context

Expand Down Expand Up @@ -81,7 +81,7 @@ def __call__(
def get_row_context_for_section(
self,
section_name: Optional[str],
section_status: str,
section_status: CompletionStatus,
section_url: str,
row_id: str,
) -> dict[str, Union[str, list]]:
Expand Down Expand Up @@ -117,7 +117,7 @@ def get_row_context_for_section(

@staticmethod
def get_section_url(
section_id: str, list_item_id: Optional[str], section_status: str
section_id: str, list_item_id: Optional[str], section_status: CompletionStatus
) -> str:
if section_status == CompletionStatus.INDIVIDUAL_RESPONSE_REQUESTED:
return url_for(
Expand Down
2 changes: 1 addition & 1 deletion app/views/handlers/individual_response.py
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ def _render_block(self) -> dict[str, Any]:
data_to_render=self.block_definition, list_item_id=self._list_item_id
)

def _update_section_status(self, status: str) -> None:
def _update_section_status(self, status: CompletionStatus) -> None:
self._questionnaire_store.progress_store.update_section_status(
status,
SectionKey(self.individual_section_id, self._list_item_id),
Expand Down
16 changes: 8 additions & 8 deletions tests/app/data_model/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
import pytest
from mock.mock import Mock

from app.data_models import ListStore, QuestionnaireStore
from app.data_models import CompletionStatus, ListStore, QuestionnaireStore
from app.data_models.answer_store import Answer
from app.data_models.progress_store import CompletionStatus
from app.data_models.progress import ProgressDict
from app.data_models.session_store import SessionStore
from app.storage import storage_encryption
from tests.app.parser.conftest import get_response_expires_at
Expand Down Expand Up @@ -92,12 +92,12 @@ def basic_input():
"ANSWERS": [{"answer_id": "test", "value": "test"}],
"LISTS": [],
"PROGRESS": [
{
"section_id": "a-test-section",
"list_item_id": "abc123",
"status": CompletionStatus.COMPLETED,
"block_ids": ["a-test-block"],
}
ProgressDict(
section_id="a-test-section",
list_item_id="abc123",
status=CompletionStatus.COMPLETED,
block_ids=["a-test-block"],
)
],
"SUPPLEMENTARY_DATA": {"data": {}, "list_mappings": {}},
"RESPONSE_METADATA": {"test-meta": "test"},
Expand Down
Loading

0 comments on commit 25c7518

Please sign in to comment.