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