diff --git a/commcare_connect/form_receiver/processor.py b/commcare_connect/form_receiver/processor.py
index d927e8c4..f79899e5 100644
--- a/commcare_connect/form_receiver/processor.py
+++ b/commcare_connect/form_receiver/processor.py
@@ -8,7 +8,7 @@
Assessment,
CommCareApp,
CompletedModule,
- DeliverForm,
+ DeliverUnit,
LearnModule,
Opportunity,
UserVisit,
@@ -17,6 +17,7 @@
LEARN_MODULE_JSONPATH = parse("$..module")
ASSESSMENT_JSONPATH = parse("$..assessment")
+DELIVER_UNIT_JSONPATH = parse("$..deliver")
def process_xform(xform: XForm):
@@ -24,13 +25,13 @@ def process_xform(xform: XForm):
app = get_app(xform.domain, xform.app_id)
user = get_user(xform)
- if process_deliver_form(user, xform):
- return
+ opportunity = get_opportunity(deliver_app=app)
+ if opportunity:
+ process_deliver_form(user, xform, app, opportunity)
- opportunity = get_opportunity_for_learn_app(app)
- if not opportunity:
- raise ProcessingError(f"No active opportunities found for CommCare app {app.cc_app_id}.")
- process_learn_form(user, xform, app, opportunity)
+ opportunity = get_opportunity(learn_app=app)
+ if opportunity:
+ process_learn_form(user, xform, app, opportunity)
def process_learn_form(user, xform: XForm, app: CommCareApp, opportunity: Opportunity):
@@ -121,35 +122,58 @@ def process_assessments(user, xform: XForm, app: CommCareApp, opportunity: Oppor
return ProcessingError("Learn Assessment is already completed")
-def process_deliver_form(user, xform):
- try:
- deliver_form = DeliverForm.objects.filter(
- xmlns=xform.xmlns, app__cc_domain=xform.domain, app__cc_app_id=xform.app_id
- ).get()
- except DeliverForm.DoesNotExist:
- return False
- except DeliverForm.MultipleObjectsReturned:
- raise ProcessingError(f"Multiple deliver forms found for this app and XMLNS: {xform.app_id}, {xform.xmlns}")
+def process_deliver_form(user, xform: XForm, app: CommCareApp, opportunity: Opportunity):
+ matches = [
+ match.value for match in DELIVER_UNIT_JSONPATH.find(xform.form) if match.value["@xmlns"] == CCC_LEARN_XMLNS
+ ]
+ if matches:
+ for deliver_unit_block in matches:
+ process_deliver_unit(user, xform, app, opportunity, deliver_unit_block)
+
+def process_deliver_unit(user, xform: XForm, app: CommCareApp, opportunity: Opportunity, deliver_unit_block: dict):
+ deliver_unit = get_or_create_deliver_unit(app, deliver_unit_block)
UserVisit.objects.create(
- opportunity=deliver_form.opportunity,
+ opportunity=opportunity,
user=user,
- deliver_form=deliver_form,
+ deliver_unit=deliver_unit,
+ entity_id=deliver_unit_block.get("entity_id"),
+ entity_name=deliver_unit_block.get("entity_name"),
visit_date=xform.metadata.timeStart,
xform_id=xform.id,
app_build_id=xform.build_id,
app_build_version=xform.metadata.app_build_version,
form_json=xform.raw_form,
)
- return True
-def get_opportunity_for_learn_app(app):
+def get_or_create_deliver_unit(app, unit_data):
+ unit, _ = DeliverUnit.objects.get_or_create(
+ app=app,
+ slug=unit_data["@id"],
+ defaults={
+ "name": unit_data["name"],
+ },
+ )
+ return unit
+
+
+def get_opportunity(*, learn_app=None, deliver_app=None):
+ if not learn_app and not deliver_app:
+ raise ValueError("One of learn_app or deliver_app must be provided")
+
+ kwargs = {}
+ if learn_app:
+ kwargs = {"learn_app": learn_app}
+ if deliver_app:
+ kwargs = {"deliver_app": deliver_app}
+
try:
- return Opportunity.objects.get(learn_app=app, active=True)
+ return Opportunity.objects.get(active=True, **kwargs)
except Opportunity.DoesNotExist:
pass
except Opportunity.MultipleObjectsReturned:
+ app = learn_app or deliver_app
raise ProcessingError(f"Multiple active opportunities found for CommCare app {app.cc_app_id}.")
diff --git a/commcare_connect/form_receiver/tests/test_process_xform.py b/commcare_connect/form_receiver/tests/test_process_xform.py
index 9bd88f68..f84cde6b 100644
--- a/commcare_connect/form_receiver/tests/test_process_xform.py
+++ b/commcare_connect/form_receiver/tests/test_process_xform.py
@@ -2,10 +2,12 @@
from unittest import mock
from commcare_connect.form_receiver.processor import process_deliver_form, process_learn_form
-from commcare_connect.form_receiver.tests.xforms import AssessmentStubFactory, LearnModuleJsonFactory, get_form_model
-from commcare_connect.opportunity.models import UserVisit
-from commcare_connect.opportunity.tests.factories import OpportunityFactory
-from commcare_connect.users.models import User
+from commcare_connect.form_receiver.tests.xforms import (
+ AssessmentStubFactory,
+ DeliverUnitStubFactory,
+ LearnModuleJsonFactory,
+ get_form_model,
+)
LEARN_PROCESSOR_PATCHES = [
"commcare_connect.form_receiver.processor.process_learn_modules",
@@ -13,37 +15,43 @@
]
-def test_process_learn_form_no_matching_blocks(user: User):
+def test_process_learn_form_no_matching_blocks():
with mock.patch("commcare_connect.form_receiver.processor.process_learn_modules") as process_learn_modules:
- process_learn_form(user, get_form_model(), None, None)
+ process_learn_form(None, get_form_model(), None, None)
assert process_learn_modules.call_count == 0
-def test_process_learn_module(user: User):
+def test_process_learn_module():
learn_module = LearnModuleJsonFactory().json
xform = get_form_model(form_block=learn_module)
with patch_multiple(*LEARN_PROCESSOR_PATCHES) as [process_learn_module, process_assessment]:
- process_learn_form(user, xform, None, None)
+ process_learn_form(None, xform, None, None)
assert process_learn_module.call_count == 1
assert process_assessment.call_count == 0
-def test_process_assessment(user: User):
+def test_process_assessment():
assessment = AssessmentStubFactory().json
xform = get_form_model(form_block=assessment)
with patch_multiple(*LEARN_PROCESSOR_PATCHES) as [process_learn_module, process_assessment]:
- process_learn_form(user, xform, None, None)
+ process_learn_form(None, xform, None, None)
assert process_learn_module.call_count == 0
assert process_assessment.call_count == 1
-def test_process_deliver_form(user: User):
- opportunity = OpportunityFactory()
- deliver_form = opportunity.deliver_form.first()
- app = deliver_form.app
- xform = get_form_model(xmlns=deliver_form.xmlns, app_id=app.cc_app_id, domain=app.cc_domain)
- assert process_deliver_form(user, xform)
- assert UserVisit.objects.filter(user=user, deliver_form=deliver_form, xform_id=xform.id).count() == 1
+def test_process_deliver_form():
+ deliver_block = DeliverUnitStubFactory().json
+ xform = get_form_model(form_block=deliver_block)
+ with mock.patch("commcare_connect.form_receiver.processor.process_deliver_unit") as process_deliver_unit:
+ process_deliver_form(None, xform, None, None)
+ assert process_deliver_unit.call_count == 1
+
+
+def test_process_deliver_form_no_matches():
+ xform = get_form_model()
+ with mock.patch("commcare_connect.form_receiver.processor.process_deliver_unit") as process_deliver_unit:
+ process_deliver_form(None, xform, None, None)
+ assert process_deliver_unit.call_count == 0
@contextmanager
diff --git a/commcare_connect/form_receiver/tests/test_receiver_integration.py b/commcare_connect/form_receiver/tests/test_receiver_integration.py
index b21b429a..f9dfb085 100644
--- a/commcare_connect/form_receiver/tests/test_receiver_integration.py
+++ b/commcare_connect/form_receiver/tests/test_receiver_integration.py
@@ -2,9 +2,14 @@
from rest_framework.test import APIClient
from commcare_connect.form_receiver.tests.test_receiver_endpoint import add_credentials
-from commcare_connect.form_receiver.tests.xforms import AssessmentStubFactory, LearnModuleJsonFactory, get_form_json
+from commcare_connect.form_receiver.tests.xforms import (
+ AssessmentStubFactory,
+ DeliverUnitStubFactory,
+ LearnModuleJsonFactory,
+ get_form_json,
+)
from commcare_connect.opportunity.models import Assessment, CompletedModule, LearnModule, Opportunity, UserVisit
-from commcare_connect.opportunity.tests.factories import LearnModuleFactory, OpportunityFactory
+from commcare_connect.opportunity.tests.factories import DeliverUnitFactory, LearnModuleFactory, OpportunityFactory
from commcare_connect.users.models import ConnectIDUserLink, User
from commcare_connect.users.tests.factories import MobileUserFactory
@@ -100,16 +105,21 @@ def test_form_receiver_assessment(
@pytest.mark.django_db
def test_receiver_deliver_form(mobile_user_with_connect_link: User, api_client: APIClient, opportunity: Opportunity):
- deliver_form = opportunity.deliver_form.first()
+ deliver_unit = DeliverUnitFactory(app=opportunity.deliver_app)
+ stub = DeliverUnitStubFactory(id=deliver_unit.slug)
form_json = get_form_json(
- xmlns=deliver_form.xmlns,
- domain=opportunity.deliver_app.cc_domain,
- app_id=opportunity.deliver_app.cc_app_id,
+ form_block=stub.json,
+ domain=deliver_unit.app.cc_domain,
+ app_id=deliver_unit.app.cc_app_id,
)
assert UserVisit.objects.filter(user=mobile_user_with_connect_link).count() == 0
make_request(api_client, form_json, mobile_user_with_connect_link)
assert UserVisit.objects.filter(user=mobile_user_with_connect_link).count() == 1
+ visit = UserVisit.objects.get(user=mobile_user_with_connect_link)
+ assert visit.deliver_unit == deliver_unit
+ assert visit.entity_id == stub.entity_id
+ assert visit.entity_name == stub.entity_name
def _get_form_json(learn_app, module_id, form_block=None):
@@ -124,4 +134,5 @@ def _get_form_json(learn_app, module_id, form_block=None):
def make_request(api_client, form_json, user, expected_status_code=200):
add_credentials(api_client, user)
response = api_client.post("/api/receiver/", data=form_json, format="json")
+ print(response.data)
assert response.status_code == expected_status_code, response.data
diff --git a/commcare_connect/form_receiver/tests/xforms.py b/commcare_connect/form_receiver/tests/xforms.py
index 71c8fc71..7b24b100 100644
--- a/commcare_connect/form_receiver/tests/xforms.py
+++ b/commcare_connect/form_receiver/tests/xforms.py
@@ -53,6 +53,17 @@
% CCC_LEARN_XMLNS
)
+DELIVER_UNIT_XML_TEMPLATE = (
+ """
+
+ {name}
+ {entity_id}
+ {entity_name}
+
+"""
+ % CCC_LEARN_XMLNS
+)
+
def get_form_json(xmlns=DEFAULT_XMLNS, form_block=None, **kwargs):
form = deepcopy(MOCK_FORM)
@@ -78,15 +89,11 @@ class LearnModuleJsonFactory(factory.StubFactory):
@factory.lazy_attribute
def json(self):
- return _get_learn_module_json(self)
-
-
-def _get_learn_module_json(stub):
- xml = MODULE_XML_TEMPLATE.format(
- id=stub.id, name=stub.name, description=stub.description, time_estimate=stub.time_estimate
- )
- _, module = xml2json(xml)
- return module
+ xml = MODULE_XML_TEMPLATE.format(
+ id=self.id, name=self.name, description=self.description, time_estimate=self.time_estimate
+ )
+ _, module = xml2json(xml)
+ return module
class AssessmentStubFactory(factory.StubFactory):
@@ -95,10 +102,21 @@ class AssessmentStubFactory(factory.StubFactory):
@factory.lazy_attribute
def json(self):
- return _get_assessment_json(self)
+ xml = ASSESSMENT_XML_TEMPLATE.format(id=self.id, score=self.score)
+ _, module = xml2json(xml)
+ return module
+
+class DeliverUnitStubFactory(factory.StubFactory):
+ id = factory.Faker("slug")
+ name = factory.Faker("name")
+ entity_id = factory.Faker("uuid4")
+ entity_name = factory.Faker("name")
-def _get_assessment_json(stub):
- xml = ASSESSMENT_XML_TEMPLATE.format(id=stub.id, score=stub.score)
- _, module = xml2json(xml)
- return module
+ @factory.lazy_attribute
+ def json(self):
+ xml = DELIVER_UNIT_XML_TEMPLATE.format(
+ id=self.id, name=self.name, entity_id=self.entity_id, entity_name=self.entity_name
+ )
+ _, module = xml2json(xml)
+ return module
diff --git a/commcare_connect/opportunity/admin.py b/commcare_connect/opportunity/admin.py
index 69503169..c20a0147 100644
--- a/commcare_connect/opportunity/admin.py
+++ b/commcare_connect/opportunity/admin.py
@@ -5,7 +5,7 @@
Assessment,
CommCareApp,
CompletedModule,
- DeliverForm,
+ DeliverUnit,
LearnModule,
Opportunity,
OpportunityAccess,
@@ -18,7 +18,7 @@
admin.site.register(Opportunity)
admin.site.register(CommCareApp)
-admin.site.register(DeliverForm)
+admin.site.register(DeliverUnit)
admin.site.register(LearnModule)
admin.site.register(CompletedModule)
admin.site.register(Assessment)
diff --git a/commcare_connect/opportunity/export.py b/commcare_connect/opportunity/export.py
index b53294f2..89186a11 100644
--- a/commcare_connect/opportunity/export.py
+++ b/commcare_connect/opportunity/export.py
@@ -18,13 +18,17 @@ def export_user_visit_data(
user_visits = user_visits.filter(status__in=status)
table = UserVisitTable(user_visits)
- exclude_columns = ("visit_date",)
+ exclude_columns = ("visit_date", "form_json")
columns = [
column
for column in table.columns.iterall()
if not (column.column.exclude_from_export or column.name in exclude_columns)
]
- base_data = [[row.get_cell_value(column.name) for column in columns] for row in table.rows]
+ base_data = [
+ # form_json must be the last column in the row
+ [row.get_cell_value(column.name) for column in columns] + [row.get_cell_value("form_json")]
+ for row in table.rows
+ ]
base_headers = [force_str(column.header, strings_only=True) for column in columns]
return get_flattened_dataset(base_headers, base_data)
@@ -45,7 +49,7 @@ def get_flattened_dataset(headers: list[str], data: list[list]) -> Dataset:
schema.update(flat_json.keys())
schema = sorted(schema, key=_schema_sort)
- headers = headers[:-1] + schema
+ headers = headers + schema
dataset = Dataset(title="Export", headers=headers)
for row, flat_json in zip(data, flat_data):
diff --git a/commcare_connect/opportunity/forms.py b/commcare_connect/opportunity/forms.py
index 1d2ac225..8b1208c6 100644
--- a/commcare_connect/opportunity/forms.py
+++ b/commcare_connect/opportunity/forms.py
@@ -7,7 +7,6 @@
from commcare_connect.opportunity.models import (
CommCareApp,
- DeliverForm,
HQApiKey,
Opportunity,
OpportunityAccess,
@@ -97,25 +96,17 @@ def __init__(self, *args, **kwargs):
Row(Field("learn_app_description")),
Row(Field("learn_app_passing_score")),
Row(Field("deliver_app")),
- Row(Field("deliver_form")),
Row(Field("api_key")),
Submit("submit", "Submit"),
)
- app_choices = []
- form_choices = []
- for app in self.applications:
- app_choices.append((app["id"], app["name"]))
- for form in app["forms"]:
- form_choices.append((form["id"], form["name"]))
+ app_choices = [(app["id"], app["name"]) for app in self.applications]
self.fields["learn_app"] = forms.ChoiceField(choices=app_choices)
self.fields["learn_app_description"] = forms.CharField(widget=forms.Textarea)
self.fields["learn_app_passing_score"] = forms.IntegerField(max_value=100, min_value=0)
self.fields["deliver_app"] = forms.ChoiceField(choices=app_choices)
self.fields["deliver_app"].widget.attrs.update({"id": "deliver_app_select"})
- self.fields["deliver_form"] = forms.ChoiceField(choices=form_choices)
- self.fields["deliver_form"].widget.attrs.update({"id": "deliver_form_select"})
self.fields["api_key"] = forms.CharField(max_length=50)
def clean(self):
@@ -139,7 +130,6 @@ def clean(self):
def save(self, commit=True):
organization = Organization.objects.filter(slug=self.org_slug).first()
- deliver_form = DeliverForm()
for app in self.applications:
if app["id"] == self.cleaned_data["learn_app"]:
@@ -168,12 +158,6 @@ def save(self, commit=True):
},
)
- for form in app["forms"]:
- if form["id"] == self.cleaned_data["deliver_form"]:
- deliver_form.xmlns = form["xmlns"]
- deliver_form.name = form["name"]
- deliver_form.app = self.instance.deliver_app
-
self.instance.created_by = self.user.email
self.instance.modified_by = self.user.email
self.instance.organization = organization
@@ -182,10 +166,6 @@ def save(self, commit=True):
self.instance.api_key = api_key
super().save(commit=commit)
- deliver_form.opportunity = self.instance
- deliver_form.clean()
- deliver_form.save()
-
return self.instance
diff --git a/commcare_connect/opportunity/migrations/0019_deliverunit_remove_uservisit_deliver_form_and_more.py b/commcare_connect/opportunity/migrations/0019_deliverunit_remove_uservisit_deliver_form_and_more.py
new file mode 100644
index 00000000..8e49a9e1
--- /dev/null
+++ b/commcare_connect/opportunity/migrations/0019_deliverunit_remove_uservisit_deliver_form_and_more.py
@@ -0,0 +1,76 @@
+# Generated by Django 4.2.5 on 2023-09-29 07:40
+
+from django.db import migrations, models
+import django.db.models.deletion
+from django.utils.text import slugify
+
+
+def migrate_deliver_form_to_deliver_unit(apps, schema_editor):
+ DeliverForm = apps.get_model("opportunity.DeliverForm")
+ DeliverUnit = apps.get_model("opportunity.DeliverUnit")
+ UserVisit = apps.get_model("opportunity.UserVisit")
+ forms_to_units = {}
+ for form in DeliverForm.objects.all():
+ unit = DeliverUnit.objects.create(
+ app=form.app,
+ slug=slugify(form.xmlns),
+ name=form.name,
+ )
+ forms_to_units[form.id] = unit.id
+
+ for form_id, unit_id in forms_to_units.items():
+ UserVisit.objects.filter(deliver_form_id=form_id).update(deliver_unit_id=unit_id)
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("opportunity", "0018_merge_20230919_2049"),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name="DeliverUnit",
+ fields=[
+ ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
+ ("slug", models.SlugField()),
+ ("name", models.CharField(max_length=255)),
+ (
+ "app",
+ models.ForeignKey(
+ on_delete=django.db.models.deletion.CASCADE,
+ related_name="deliver_units",
+ to="opportunity.commcareapp",
+ ),
+ ),
+ ],
+ ),
+ migrations.AddField(
+ model_name="uservisit",
+ name="deliver_unit",
+ field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to="opportunity.deliverunit"),
+ ),
+ migrations.RunPython(migrate_deliver_form_to_deliver_unit, reverse_code=migrations.RunPython.noop),
+ migrations.AddField(
+ model_name="uservisit",
+ name="entity_id",
+ field=models.CharField(blank=True, max_length=64, null=True),
+ ),
+ migrations.AddField(
+ model_name="uservisit",
+ name="entity_name",
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ # drop null=True from deliver_unit
+ migrations.AlterField(
+ model_name="uservisit",
+ name="deliver_unit",
+ field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to="opportunity.deliverunit"),
+ ),
+ migrations.RemoveField(
+ model_name="uservisit",
+ name="deliver_form",
+ ),
+ migrations.DeleteModel(
+ name="DeliverForm",
+ ),
+ ]
diff --git a/commcare_connect/opportunity/models.py b/commcare_connect/opportunity/models.py
index 38d36cb1..8a4bac24 100644
--- a/commcare_connect/opportunity/models.py
+++ b/commcare_connect/opportunity/models.py
@@ -76,26 +76,6 @@ def remaining_budget(self) -> int:
return self.total_budget - used_budget
-class DeliverForm(models.Model):
- app = models.ForeignKey(
- CommCareApp,
- on_delete=models.CASCADE,
- related_name="deliver_form",
- related_query_name="deliver_form",
- )
- opportunity = models.ForeignKey(
- Opportunity,
- on_delete=models.CASCADE,
- related_name="deliver_form",
- related_query_name="deliver_form",
- )
- name = models.CharField(max_length=255)
- xmlns = models.CharField(max_length=255)
-
- def __str__(self):
- return self.name
-
-
class LearnModule(models.Model):
app = models.ForeignKey(
CommCareApp,
@@ -169,18 +149,12 @@ def learn_progress(self):
@property
def visit_count(self):
- deliver_forms = self.opportunity.deliver_form.all()
- user_visits = UserVisit.objects.filter(user=self.user_id, deliver_form__in=deliver_forms).order_by(
- "visit_date"
- )
+ user_visits = UserVisit.objects.filter(user=self.user_id, opportunity=self.opportunity).order_by("visit_date")
return user_visits.count()
@property
def last_visit_date(self):
- deliver_forms = self.opportunity.deliver_form.all()
- user_visits = UserVisit.objects.filter(user=self.user_id, deliver_form__in=deliver_forms).order_by(
- "visit_date"
- )
+ user_visits = UserVisit.objects.filter(user=self.user_id, opportunity=self.opportunity).order_by("visit_date")
if user_visits.exists():
return user_visits.first().visit_date
@@ -188,6 +162,19 @@ def last_visit_date(self):
return
+class DeliverUnit(models.Model):
+ app = models.ForeignKey(
+ CommCareApp,
+ on_delete=models.CASCADE,
+ related_name="deliver_units",
+ )
+ slug = models.SlugField()
+ name = models.CharField(max_length=255)
+
+ def __str__(self):
+ return self.name
+
+
class VisitValidationStatus(models.TextChoices):
pending = "pending", gettext("Pending")
approved = "approved", gettext("Approved")
@@ -209,10 +196,9 @@ class UserVisit(XFormBaseModel):
User,
on_delete=models.CASCADE,
)
- deliver_form = models.ForeignKey(
- DeliverForm,
- on_delete=models.PROTECT,
- )
+ deliver_unit = models.ForeignKey(DeliverUnit, on_delete=models.PROTECT)
+ entity_id = models.CharField(max_length=64, null=True, blank=True)
+ entity_name = models.CharField(max_length=255, null=True, blank=True)
visit_date = models.DateTimeField()
status = models.CharField(
max_length=50, choices=VisitValidationStatus.choices, default=VisitValidationStatus.pending
diff --git a/commcare_connect/opportunity/tables.py b/commcare_connect/opportunity/tables.py
index d4289b46..3db5d20a 100644
--- a/commcare_connect/opportunity/tables.py
+++ b/commcare_connect/opportunity/tables.py
@@ -29,7 +29,9 @@ class UserVisitTable(tables.Table):
verbose_name="Visit date", accessor="visit_date", format="c", visible=False
)
- deliver_form = columns.Column("Form Name", accessor="deliver_form.name")
+ deliver_unit = columns.Column("Unit Name", accessor="deliver_unit.name")
+ entity_id = columns.Column("Entity ID", accessor="entity_id", visible=False)
+ entity_name = columns.Column("Entity Name", accessor="entity_name")
class Meta:
model = UserVisit
@@ -41,7 +43,7 @@ class Meta:
"status",
"username",
"user.name",
- "deliver_form",
+ "deliver_unit",
)
empty_text = "No forms."
orderable = False
diff --git a/commcare_connect/opportunity/tests/factories.py b/commcare_connect/opportunity/tests/factories.py
index 3ee5772e..e8fd8098 100644
--- a/commcare_connect/opportunity/tests/factories.py
+++ b/commcare_connect/opportunity/tests/factories.py
@@ -1,4 +1,4 @@
-from factory import CREATE_STRATEGY, DictFactory, Faker, RelatedFactory, SelfAttribute, SubFactory
+from factory import CREATE_STRATEGY, DictFactory, Faker, SelfAttribute, SubFactory
from factory.django import DjangoModelFactory
from commcare_connect.opportunity.models import VisitValidationStatus
@@ -44,12 +44,6 @@ class OpportunityFactory(DjangoModelFactory):
budget_per_visit = Faker("pyint", min_value=100, max_value=1000)
total_budget = Faker("pyint", min_value=1000, max_value=10000)
- deliver_form = RelatedFactory(
- "commcare_connect.opportunity.tests.factories.DeliverFormFactory",
- factory_related_name="opportunity",
- app=SelfAttribute("..deliver_app"),
- )
-
class Meta:
model = "opportunity.Opportunity"
@@ -65,25 +59,21 @@ class Meta:
model = "opportunity.LearnModule"
-class DeliverFormFactory(DjangoModelFactory):
+class DeliverUnitFactory(DjangoModelFactory):
app = SubFactory(CommCareAppFactory)
- opportunity = SubFactory(
- OpportunityFactory,
- deliver_app=SelfAttribute("..app"),
- organization=SelfAttribute("..app.organization"),
- deliver_form=None,
- )
+ slug = Faker("pystr")
name = Faker("name")
- xmlns = Faker("url")
class Meta:
- model = "opportunity.DeliverForm"
+ model = "opportunity.DeliverUnit"
class UserVisitFactory(DjangoModelFactory):
opportunity = SubFactory(OpportunityFactory)
user = SubFactory("commcare_connect.users.tests.factories.UserFactory")
- deliver_form = SubFactory(DeliverFormFactory, opportunity=SelfAttribute("..opportunity"))
+ deliver_unit = SubFactory(DeliverUnitFactory)
+ entity_id = Faker("uuid4")
+ entity_name = Faker("name")
status = Faker("enum", enum_cls=VisitValidationStatus)
visit_date = Faker("date")
form_json = Faker("pydict", value_types=[str, int, float, bool])
diff --git a/commcare_connect/opportunity/tests/test_export.py b/commcare_connect/opportunity/tests/test_export.py
index 8eaf0429..3c7b6aa2 100644
--- a/commcare_connect/opportunity/tests/test_export.py
+++ b/commcare_connect/opportunity/tests/test_export.py
@@ -4,39 +4,42 @@
from commcare_connect.opportunity.export import export_user_visit_data, get_flattened_dataset
from commcare_connect.opportunity.forms import DateRanges
from commcare_connect.opportunity.models import UserVisit
-from commcare_connect.opportunity.tests.factories import DeliverFormFactory
+from commcare_connect.opportunity.tests.factories import DeliverUnitFactory, OpportunityFactory
def test_export_user_visit_data(mobile_user_with_connect_link):
- deliver_form = DeliverFormFactory()
+ deliver_unit = DeliverUnitFactory()
+ opportunity = OpportunityFactory()
date1 = now()
date2 = now()
UserVisit.objects.bulk_create(
[
UserVisit(
- opportunity=deliver_form.opportunity,
+ opportunity=opportunity,
user=mobile_user_with_connect_link,
visit_date=date1,
- deliver_form=deliver_form,
+ deliver_unit=deliver_unit,
form_json={"form": {"name": "test_form1"}},
),
UserVisit(
- opportunity=deliver_form.opportunity,
+ opportunity=opportunity,
user=mobile_user_with_connect_link,
visit_date=date2,
- deliver_form=deliver_form,
+ deliver_unit=deliver_unit,
+ entity_id="abc",
+ entity_name="A B C",
form_json={"form": {"name": "test_form2", "group": {"q": "b"}}},
),
]
)
- exporter = export_user_visit_data(deliver_form.opportunity, DateRanges.LAST_30_DAYS, [])
+ exporter = export_user_visit_data(opportunity, DateRanges.LAST_30_DAYS, [])
username = mobile_user_with_connect_link.username
name = mobile_user_with_connect_link.name
assert exporter.export("csv") == (
- "Visit ID,Visit date,Status,Username,Name of User,Form Name,form.name,form.group.q\r\n"
- f",{date1.isoformat()},Pending,{username},{name},{deliver_form.name},test_form1,\r\n"
- f",{date2.isoformat()},Pending,{username},{name},{deliver_form.name},test_form2,b\r\n"
+ "Visit ID,Visit date,Status,Username,Name of User,Unit Name,Entity ID,Entity Name,form.name,form.group.q\r\n"
+ f",{date1.isoformat()},Pending,{username},{name},{deliver_unit.name},,,test_form1,\r\n"
+ f",{date2.isoformat()},Pending,{username},{name},{deliver_unit.name},abc,A B C,test_form2,b\r\n"
)
@@ -56,7 +59,7 @@ def test_export_user_visit_data(mobile_user_with_connect_link):
],
)
def test_get_flattened_dataset(data, expected):
- headers = ["header1", "header2", "header3"]
+ headers = ["header1", "header2"]
data = [
["value1", "value2", data],
]
diff --git a/commcare_connect/opportunity/tests/test_factories.py b/commcare_connect/opportunity/tests/test_factories.py
deleted file mode 100644
index 900cd73e..00000000
--- a/commcare_connect/opportunity/tests/test_factories.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import pytest
-
-from commcare_connect.opportunity.tests.factories import DeliverFormFactory, OpportunityFactory
-
-
-@pytest.mark.django_db
-def test_opportunity_factory():
- opportunity = OpportunityFactory()
- assert opportunity.organization == opportunity.learn_app.organization
- assert opportunity.organization == opportunity.deliver_app.organization
- assert opportunity.deliver_form.count() == 1
- assert opportunity.deliver_form.first().app == opportunity.deliver_app
-
-
-@pytest.mark.django_db
-def test_deliver_form_factory():
- deliver_form = DeliverFormFactory()
- opportunity = deliver_form.opportunity
- assert deliver_form.app == opportunity.deliver_app
- assert deliver_form.app.organization == opportunity.organization
- assert deliver_form.app.organization == opportunity.learn_app.organization
diff --git a/commcare_connect/opportunity/views.py b/commcare_connect/opportunity/views.py
index 4d669fef..a6ff21e1 100644
--- a/commcare_connect/opportunity/views.py
+++ b/commcare_connect/opportunity/views.py
@@ -66,11 +66,6 @@ class OpportunityCreate(OrganizationUserMixin, CreateView):
def get_success_url(self):
return reverse("opportunity:list", args=(self.request.org.slug,))
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- context["applications"] = get_applications_for_user(self.request.user)
- return context
-
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["applications"] = get_applications_for_user(self.request.user)
diff --git a/commcare_connect/templates/opportunity/opportunity_create.html b/commcare_connect/templates/opportunity/opportunity_create.html
index e1a523dd..6944ea1d 100644
--- a/commcare_connect/templates/opportunity/opportunity_create.html
+++ b/commcare_connect/templates/opportunity/opportunity_create.html
@@ -11,31 +11,5 @@
Create Opportunity
{% csrf_token %}
{% crispy form %}
-
- {{ applications|json_script:"applications-data" }}
-
{% endblock content %}
diff --git a/commcare_connect/utils/commcarehq_api.py b/commcare_connect/utils/commcarehq_api.py
index ab52b858..72384ae7 100644
--- a/commcare_connect/utils/commcarehq_api.py
+++ b/commcare_connect/utils/commcarehq_api.py
@@ -88,24 +88,6 @@ async def _get_commcare_app_json(client, domain):
response = await client.get(f"{settings.COMMCARE_HQ_URL}/a/{domain}/api/v0.5/application/")
data = response.json()
- def _get_name(block: dict):
- name_data = block.get("name", {})
- for lang in ["en"] + list(name_data):
- if lang in name_data:
- return name_data[lang]
-
for application in data.get("objects", []):
- forms = [
- {
- "module": _get_name(module),
- "id": form.get("unique_id"),
- "name": _get_name(form),
- "xmlns": form.get("xmlns"),
- }
- for module in application.get("modules", [])
- for form in module.get("forms", [])
- ]
- applications.append(
- {"id": application.get("id"), "name": application.get("name"), "domain": domain, "forms": forms}
- )
+ applications.append({"id": application.get("id"), "name": application.get("name"), "domain": domain})
return applications