Skip to content

Commit

Permalink
Looping 2.5 - Simple Repeating Blocks in List Collector (No Item Prog…
Browse files Browse the repository at this point in the history
…ress Tracking) (#1100)

* Schema for repeating blocks and section summary incomplete + ListCollector Question handling many add types by enum.

* Repeating blocks are presented to the user and can be answered however are not posted correctly and navigation not tested.

* List Collector now always enters add_block & both add_block and edit_block go to repeating_blocks if existing.

* make format

* edit_block does not lead to repeating_blocks. All repeating_blocks show on list collector summary.

* Blocks in repeating_blocks calculated upfront. Answers from repeating_blocks shown on list collector summary underneath the relevant list item.

* Reverting list_collector handler and fixing invalid use of set

* Return to summary reused throughout ListActions
  • Loading branch information
kylelawsonAND authored May 16, 2023
1 parent fe76286 commit 7bda62b
Show file tree
Hide file tree
Showing 8 changed files with 191 additions and 292 deletions.
17 changes: 16 additions & 1 deletion app/questionnaire/questionnaire_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"ListEditQuestion",
"ListRemoveQuestion",
"PrimaryPersonListAddOrEditQuestion",
"ListRepeatingBlock",
]

RELATIONSHIP_CHILDREN = ["UnrelatedQuestion"]
Expand Down Expand Up @@ -66,6 +67,7 @@ def __init__(
] = defaultdict(set)
self._language_code = language_code
self._questionnaire_json = questionnaire_json
self._repeating_blocks_by_id: dict[str, ImmutableDict] = {}

# The ordering here is required as they depend on each other.
self._sections_by_id = self._get_sections_by_id()
Expand Down Expand Up @@ -209,6 +211,12 @@ def _get_blocks_by_id(self) -> dict[str, ImmutableDict]:
nested_block_id = nested_block["id"]
blocks[nested_block_id] = nested_block
self._parent_id_map[nested_block_id] = block_id
if block.get("repeating_blocks"):
for repeating_block in block["repeating_blocks"]:
repeating_block_id = repeating_block["id"]
blocks[repeating_block_id] = repeating_block
self._parent_id_map[repeating_block_id] = block_id
self._repeating_blocks_by_id[repeating_block_id] = repeating_block

return blocks

Expand Down Expand Up @@ -816,7 +824,7 @@ def _block_for_answer(self, answer_id: str) -> ImmutableDict | None:
parent_block_id = self._parent_id_map[block_id]
parent_block = self.get_block(parent_block_id)

if parent_block and parent_block["type"] == "ListCollector":
if parent_block and parent_block["type"] == "ListCollector" and block_id not in self._repeating_blocks_by_id:
return parent_block

return self.get_block(block_id)
Expand Down Expand Up @@ -967,6 +975,13 @@ def get_item_anchor(self, section_id: str, list_name: str) -> str | None:
if item["for_list"] == list_name and item.get("item_anchor_answer_id"):
return f"#{str(item['item_anchor_answer_id'])}"

def is_block_in_repeating_blocks(self, block_id: str) -> bool:
return block_id in self._repeating_blocks_by_id

def is_answer_in_repeating_blocks(self, answer_id: str) -> bool:
if block := self.get_block_for_answer_id(answer_id):
return self.is_block_in_repeating_blocks(block_id=block["id"])


def get_sources_for_type_from_data(
*,
Expand Down
32 changes: 26 additions & 6 deletions app/views/contexts/summary/list_collector_block.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,19 +162,26 @@ def _add_link(

def _get_related_answers(
self, list_model: ListModel
) -> Optional[dict[str, list[dict[str, Any]]]]:
) -> dict[str, list[dict]] | None:
section_id = self._section["id"]

related_answers = self._schema.get_related_answers_for_list_for_section(
section_id=section_id, list_name=list_model.name
)
if not related_answers:

blocks: list[dict] = []

if related_answers:
blocks += self._get_blocks_for_related_answers(related_answers)

if list_model:
blocks += self._get_blocks_for_repeating_blocks(list_model)

if not blocks:
return None

related_answers_blocks = {}

blocks = self.get_blocks_for_related_answers(related_answers)

for list_id in list_model:
serialized_blocks = [
Block(
Expand All @@ -200,9 +207,9 @@ def _get_related_answers(

return related_answers_blocks

def get_blocks_for_related_answers(
def _get_blocks_for_related_answers(
self, related_answers: tuple
) -> list[dict[str, Any]]:
) -> list[dict]:
blocks = []
answers_by_block = defaultdict(list)

Expand Down Expand Up @@ -234,3 +241,16 @@ def get_blocks_for_related_answers(
blocks.append(mutable_block)

return blocks

def _get_blocks_for_repeating_blocks(self, list_model: ListModel) -> list[dict]:
blocks: defaultdict[ImmutableDict, list[str]] = defaultdict(list)
for list_id in list_model:
list_answers = [(answer_id, answer_list_id)
for (answer_id, answer_list_id) in self._answer_store.answer_map
if answer_list_id == list_id and self._schema.is_answer_in_repeating_blocks(answer_id)]
for (answer_id, answer_list_id) in list_answers:
block = self._schema.get_block_for_answer_id(answer_id)
if block:
blocks[block].append(answer_id)

return [self._schema.get_mutable_deepcopy(block) for (block, answer_id) in blocks.items()]
2 changes: 2 additions & 0 deletions app/views/handlers/block_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from app.views.handlers.list_collector import ListCollector
from app.views.handlers.list_edit_question import ListEditQuestion
from app.views.handlers.list_remove_question import ListRemoveQuestion
from app.views.handlers.list_repeating_block import ListRepeatingBlock
from app.views.handlers.primary_person_list_collector import PrimaryPersonListCollector
from app.views.handlers.primary_person_question import PrimaryPersonQuestion
from app.views.handlers.question import Question
Expand All @@ -23,6 +24,7 @@
"ListAddQuestion": ListAddQuestion,
"ListEditQuestion": ListEditQuestion,
"ListRemoveQuestion": ListRemoveQuestion,
"ListRepeatingBlock": ListRepeatingBlock,
"PrimaryPersonListCollector": PrimaryPersonListCollector,
"PrimaryPersonListAddOrEditQuestion": PrimaryPersonQuestion,
"RelationshipCollector": RelationshipCollector,
Expand Down
18 changes: 7 additions & 11 deletions app/views/handlers/list_action.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,7 @@ def is_location_valid(self):
return True

def get_previous_location_url(self):
if (
self._return_to == "section-summary"
and self.router.can_display_section_summary(
self.parent_location.section_id, self.parent_location.list_item_id
)
):
if self._is_returning_to_section_summary():
return self.get_section_summary_url()

block_id = self._request_args.get("previous")
Expand All @@ -56,11 +51,8 @@ def get_section_summary_url(self):
)

def get_next_location_url(self):
if self._return_to == "section-summary":
if self.router.can_display_section_summary(
self.parent_location.section_id, self.parent_location.list_item_id
):
return self.get_section_summary_url()
if self._is_returning_to_section_summary():
return self.get_section_summary_url()

if self.router.is_block_complete(
block_id=self.parent_location.block_id,
Expand Down Expand Up @@ -115,3 +107,7 @@ def _get_location_url(
return_to_answer_id=return_to_answer_id,
return_to_block_id=return_to_block_id,
)

def _is_returning_to_section_summary(self) -> bool:
return self._return_to == "section-summary" and \
self.router.can_display_section_summary(self.parent_location.section_id, self.parent_location.list_item_id)
28 changes: 25 additions & 3 deletions app/views/handlers/list_add_question.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
from typing import MutableMapping
from typing import Any, MutableMapping

from flask import url_for

from app.views.handlers.list_action import ListAction


class ListAddQuestion(ListAction):
def __init__(self, *args: Any):
self._list_item_id: str | None = None
super().__init__(*args)

def is_location_valid(self):
if not super().is_location_valid() or self._current_location.list_item_id:
return False
return True

def get_next_location_url(self):
if self._list_item_id and (
repeating_blocks := self.parent_block.get("repeating_blocks")
):
repeating_block_url = url_for(
"questionnaire.block",
list_name=self.parent_block["for_list"],
list_item_id=self._list_item_id,
block_id=repeating_blocks[0]["id"],
return_to=self._return_to,
return_to_answer_id=self._return_to_answer_id,
return_to_block_id=self._return_to_block_id,
)
return repeating_block_url

return self.parent_location.url(
return_to=self._return_to,
return_to_answer_id=self._return_to_answer_id,
Expand All @@ -18,7 +38,7 @@ def get_next_location_url(self):

def handle_post(self):
# Ensure the section is in progress when user adds an item
list_item_id = self.questionnaire_store_updater.add_list_item(
self._list_item_id = self.questionnaire_store_updater.add_list_item(
self.parent_block["for_list"]
)

Expand All @@ -31,7 +51,9 @@ def handle_post(self):

# pylint: disable=no-member
# wtforms Form parents are not discoverable in the 2.3.3 implementation
self.questionnaire_store_updater.update_answers(self.form.data, list_item_id)
self.questionnaire_store_updater.update_answers(
self.form.data, self._list_item_id
)

self.evaluate_and_update_section_status_on_list_change(
self.parent_block["for_list"]
Expand Down
27 changes: 27 additions & 0 deletions app/views/handlers/list_repeating_block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from flask import url_for

from app.views.handlers.list_edit_question import ListEditQuestion


class ListRepeatingBlock(ListEditQuestion):
def get_next_location_url(self) -> str:
if self._is_returning_to_section_summary():
return self.get_section_summary_url()

repeating_block_ids = [
block["id"] for block in self.parent_block["repeating_blocks"]
]
next_block_index = repeating_block_ids.index(self.rendered_block["id"]) + 1
if next_block_index < len(repeating_block_ids):
repeating_block_url = url_for(
"questionnaire.block",
list_name=self._current_location.list_name,
list_item_id=self._current_location.list_item_id,
block_id=repeating_block_ids[next_block_index],
return_to=self._return_to,
return_to_answer_id=self._return_to_answer_id,
return_to_block_id=self._return_to_block_id,
)
return repeating_block_url

return super().get_next_location_url()
Loading

0 comments on commit 7bda62b

Please sign in to comment.