diff --git a/commcare_connect/opportunity/app_xml.py b/commcare_connect/opportunity/app_xml.py
index da2a525e..87e3e8c2 100644
--- a/commcare_connect/opportunity/app_xml.py
+++ b/commcare_connect/opportunity/app_xml.py
@@ -22,6 +22,12 @@ class Module:
time_estimate: int
+@dataclass
+class DeliverUnit:
+ id: str
+ name: str
+
+
class AppNoBuildException(CommCareHQAPIException):
pass
@@ -31,6 +37,11 @@ def get_connect_blocks_for_app(domain: str, app_id: str) -> list[Module]:
return list(itertools.chain.from_iterable(extract_connect_blocks(form_xml) for form_xml in form_xmls))
+def get_deliver_units_for_app(domain: str, app_id: str) -> list[DeliverUnit]:
+ form_xmls = get_form_xml_for_app(domain, app_id)
+ return list(itertools.chain.from_iterable(extract_deliver_units(form_xml) for form_xml in form_xmls))
+
+
def get_form_xml_for_app(domain: str, app_id: str) -> list[str]:
"""Download the CCZ for the given app and return the XML for each form."""
@@ -74,6 +85,18 @@ def extract_modules(xml: ET.Element):
yield Module(slug, name, description, int(time_estimate) if time_estimate is not None else None)
+def extract_deliver_units(form_xml):
+ xml = ET.fromstring(form_xml)
+ yield from extract_deliver_unit(xml)
+
+
+def extract_deliver_unit(xml: ET.Element):
+ for block in xml.findall(f".//{XMLNS_PREFIX}deliver"):
+ slug = block.get("id")
+ name = get_element_text(block, "name")
+ yield DeliverUnit(slug, name)
+
+
def get_element_text(parent, name) -> str | None:
element = parent.find(f"{XMLNS_PREFIX}{name}")
return element.text if element is not None else None
diff --git a/commcare_connect/opportunity/tasks.py b/commcare_connect/opportunity/tasks.py
index 0bbd9dc1..e09523dc 100644
--- a/commcare_connect/opportunity/tasks.py
+++ b/commcare_connect/opportunity/tasks.py
@@ -4,22 +4,30 @@
from django.core.files.storage import default_storage
from django.utils.timezone import now
-from commcare_connect.opportunity.app_xml import get_connect_blocks_for_app
+from commcare_connect.opportunity.app_xml import get_connect_blocks_for_app, get_deliver_units_for_app
from commcare_connect.opportunity.export import export_empty_payment_table, export_user_visit_data
from commcare_connect.opportunity.forms import DateRanges
-from commcare_connect.opportunity.models import LearnModule, Opportunity, OpportunityAccess, VisitValidationStatus
+from commcare_connect.opportunity.models import (
+ DeliverUnit,
+ LearnModule,
+ Opportunity,
+ OpportunityAccess,
+ VisitValidationStatus,
+)
from commcare_connect.users.helpers import invite_user
from commcare_connect.users.models import User
from config import celery_app
@celery_app.task()
-def create_learn_modules_assessments(opportunity_id):
+def create_learn_modules_and_deliver_units(opportunity_id):
opportunity = Opportunity.objects.filter(id=opportunity_id).first()
learn_app = opportunity.learn_app
- connect_blocks = get_connect_blocks_for_app(learn_app.cc_domain, learn_app.cc_app_id)
+ deliver_app = opportunity.deliver_app
+ learn_app_connect_blocks = get_connect_blocks_for_app(learn_app.cc_domain, learn_app.cc_app_id)
+ deliver_app_connect_blocks = get_deliver_units_for_app(deliver_app.cc_domain, deliver_app.cc_app_id)
- for block in connect_blocks:
+ for block in learn_app_connect_blocks:
LearnModule.objects.update_or_create(
app=learn_app,
slug=block.id,
@@ -30,6 +38,9 @@ def create_learn_modules_assessments(opportunity_id):
},
)
+ for block in deliver_app_connect_blocks:
+ DeliverUnit.objects.get_or_create(app=deliver_app, slug=block.id, defaults=dict(name=block.name))
+
@celery_app.task()
def add_connect_users(user_list: list[str], opportunity_id: str):
diff --git a/commcare_connect/opportunity/views.py b/commcare_connect/opportunity/views.py
index a6ff21e1..66c1793a 100644
--- a/commcare_connect/opportunity/views.py
+++ b/commcare_connect/opportunity/views.py
@@ -33,7 +33,7 @@
from commcare_connect.opportunity.tables import OpportunityAccessTable, PaymentTable, UserStatusTable, UserVisitTable
from commcare_connect.opportunity.tasks import (
add_connect_users,
- create_learn_modules_assessments,
+ create_learn_modules_and_deliver_units,
generate_payment_export,
generate_visit_export,
)
@@ -75,7 +75,7 @@ def get_form_kwargs(self):
def form_valid(self, form: OpportunityCreationForm) -> HttpResponse:
response = super().form_valid(form)
- create_learn_modules_assessments.delay(self.object.id)
+ create_learn_modules_and_deliver_units.delay(self.object.id)
return response
diff --git a/commcare_connect/templates/opportunity/opportunity_detail.html b/commcare_connect/templates/opportunity/opportunity_detail.html
index d3f76c6f..3cd705a5 100644
--- a/commcare_connect/templates/opportunity/opportunity_detail.html
+++ b/commcare_connect/templates/opportunity/opportunity_detail.html
@@ -114,27 +114,64 @@
{{ object.name }}
- Learn App Modules
-
-
-
-
- Name |
- Description |
- Time Estimate |
-
-
-
- {% for module in object.learn_app.learn_modules.all %}
-
- {{ module.name }} |
- {{ module.description }} |
- {{ module.time_estimate }} hour{{ module.time_estimate|pluralize:",s" }} |
-
- {% endfor %}
-
-
-
+
+
+
+
+
+
+
+
+ Name |
+ Description |
+ Time Estimate |
+
+
+
+ {% for module in object.learn_app.learn_modules.all %}
+
+ {{ module.name }} |
+ {{ module.description }} |
+ {{ module.time_estimate }} hour{{ module.time_estimate|pluralize:",s" }} |
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+ Name |
+ ID |
+
+
+
+ {% for unit in object.deliver_app.deliver_units.all %}
+
+ {{ unit.name }} |
+ {{ unit.slug }} |
+
+ {% endfor %}
+
+
+
+
+
+