Skip to content

Commit

Permalink
add: form creation with attachments (#92)
Browse files Browse the repository at this point in the history
  • Loading branch information
lindsay-stevens authored Jul 16, 2024
1 parent 2b41b7f commit e14a612
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 4 deletions.
24 changes: 22 additions & 2 deletions pyodk/_endpoints/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def get(
def create(
self,
definition: PathLike | str | bytes,
attachments: Iterable[PathLike | str] | None = None,
ignore_warnings: bool | None = True,
form_id: str | None = None,
project_id: int | None = None,
Expand All @@ -133,6 +134,7 @@ def create(
:param definition: The path to the file to upload (string or PathLike), or the
form definition in memory (string (XML) or bytes (XLS/XLSX)).
:param attachments: The paths of the form attachment file(s) to upload.
:param ignore_warnings: If True, create the form if there are XLSForm warnings.
:param form_id: The xmlFormId of the Form being referenced.
:param project_id: The id of the project this form belongs to.
Expand All @@ -145,7 +147,9 @@ def create(
form_id=form_id,
project_id=project_id,
)
params["publish"] = True

# Create the new Form definition, in draft state.
params["publish"] = False
response = self.session.response_or_error(
method="POST",
url=self.session.urlformat(self.urls.forms, project_id=pid),
Expand All @@ -155,7 +159,23 @@ def create(
data=form_def,
)
data = response.json()
return Form(**data)

# In case the form_id parameter was None, use the (maybe generated) response value.
form = Form(**data)
fp_ids = {"form_id": form.xmlFormId, "project_id": project_id}

# Upload the attachments, if any.
if attachments is not None:
fda = FormDraftAttachmentService(session=self.session, **self._default_kw())
for attach in attachments:
if not fda.upload(file_path=attach, **fp_ids):
raise PyODKError("Form create (attachment upload) failed.")

# Publish the draft.
if not fd.publish(**fp_ids):
raise PyODKError("Form create (draft publish) failed.")

return form

def update(
self,
Expand Down
25 changes: 24 additions & 1 deletion tests/endpoints/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ def test_get__ok(self):
)
self.assertIsInstance(observed, Form)

def test_create__ok(self):
@get_mock_context
def test_create__ok(self, ctx: MockContext):
"""Should return a FormType object."""
fixture = forms_data.test_forms
with patch.object(Session, "request") as mock_session:
Expand All @@ -126,6 +127,28 @@ def test_create__ok(self):
)
self.assertIsInstance(observed, Form)

@get_mock_context
def test_create__with_attachments__ok(self, ctx: MockContext):
"""Should return a FormType object."""
fixture = forms_data.test_forms
with patch.object(Session, "request") as mock_session:
mock_session.return_value.status_code = 200
mock_session.return_value.json.return_value = fixture["response_data"][1]
with Client() as client, utils.get_temp_file(suffix=".xml") as fp:
fp.write_text(forms_data.get_xml__range_draft())
observed = client.forms.create(
definition=fp,
project_id=fixture["project_id"],
form_id=fixture["response_data"][1]["xmlFormId"],
attachments=["/some/path/a.jpg", "/some/path/b.jpg"],
)
self.assertIsInstance(observed, Form)
self.assertEqual(2, ctx.fda_upload.call_count)
ctx.fd_publish.assert_called_once_with(
form_id=fixture["response_data"][1]["xmlFormId"],
project_id=fixture["project_id"],
)

def test_update__def_or_attach_required(self):
"""Should raise an error if both 'definition' and 'attachments' are None."""
with self.assertRaises(PyODKError) as err:
Expand Down
12 changes: 11 additions & 1 deletion tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ def test_form_create__new_definition_xlsx(self):
form = self.client.forms.create(definition=wb)
self.assertTrue(form.xmlFormId.startswith("uuid:"))

def test_form_create__new_definition_xlsx_and_attachments(self):
"""Should create a new form with the new definition and attachment."""
form_def = forms_data.get_md__pull_data()
wb = md_table_to_bytes(mdstr=form_def)
form = self.client.forms.create(
definition=wb,
attachments=[(RESOURCES / "forms" / "fruits.csv").as_posix()],
)
self.assertTrue(form.xmlFormId.startswith("uuid:"))

# Below tests assume project has forms by these names already published.
def test_form_update__new_definition(self):
"""Should create a new version with the new definition."""
Expand Down Expand Up @@ -175,7 +185,7 @@ def test_form_update__new_definition_and_attachments__non_ascii_dingbat(self):
self.assertEqual(form.xmlFormId, "✅")

def test_form_update__with_version_updater__non_ascii_specials(self):
"""Should create a new version with new definition and attachment."""
"""Should create a new version with new definition."""
self.client.forms.update(
form_id="'=+/*-451%/%",
attachments=[],
Expand Down

0 comments on commit e14a612

Please sign in to comment.