Coverage for cc_modules/tests/cc_redcap_tests.py : 16%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#!/usr/bin/env python
3"""camcops_server/cc_modules/tests/cc_redcap_tests.py
5===============================================================================
7 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com).
9 This file is part of CamCOPS.
11 CamCOPS is free software: you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
16 CamCOPS is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
24===============================================================================
25"""
26import os
27import tempfile
28from typing import Generator, TYPE_CHECKING
29from unittest import mock, TestCase
31from pandas import DataFrame
32import pendulum
33import redcap
35from camcops_server.cc_modules.cc_constants import ConfigParamExportRecipient
36from camcops_server.cc_modules.cc_exportrecipient import ExportRecipient
37from camcops_server.cc_modules.cc_exportrecipientinfo import ExportRecipientInfo
38from camcops_server.cc_modules.cc_redcap import (
39 MISSING_EVENT_TAG_OR_ATTRIBUTE,
40 RedcapExportException,
41 RedcapFieldmap,
42 RedcapNewRecordUploader,
43 RedcapRecordStatus,
44 RedcapTaskExporter,
45)
46from camcops_server.cc_modules.cc_unittest import DemoDatabaseTestCase
48if TYPE_CHECKING:
49 from camcops_server.cc_modules.cc_patient import Patient
52# =============================================================================
53# Unit testing
54# =============================================================================
57class MockProject(mock.Mock):
58 def __init__(self, *args, **kwargs) -> None:
59 super().__init__(*args, **kwargs)
61 self.export_project_info = mock.Mock()
62 self.export_records = mock.Mock()
63 self.generate_next_record_name = mock.Mock()
64 self.import_file = mock.Mock()
65 self.import_records = mock.Mock()
66 self.is_longitudinal = mock.Mock(return_value=False)
69class MockRedcapTaskExporter(RedcapTaskExporter):
70 def __init__(self) -> None:
71 mock_project = MockProject()
72 self.get_project = mock.Mock(return_value=mock_project)
74 config = mock.Mock()
75 self.req = mock.Mock(config=config)
78class MockRedcapNewRecordUploader(RedcapNewRecordUploader):
79 # noinspection PyMissingConstructor
80 def __init__(self) -> None:
81 self.req = mock.Mock()
82 self.project = MockProject()
83 self.task = mock.Mock(tablename="mock_task")
86class RedcapExporterTests(TestCase):
87 def test_next_instance_id_converted_to_int(self) -> None:
88 import numpy
90 records = DataFrame({
91 "record_id": ["1", "1", "1", "1", "1"],
92 "redcap_repeat_instrument": ["bmi", "bmi", "bmi", "bmi", "bmi"],
93 "redcap_repeat_instance": [
94 numpy.float64(1.0),
95 numpy.float64(2.0),
96 numpy.float64(3.0),
97 numpy.float64(4.0),
98 numpy.float64(5.0),
99 ],
101 })
103 next_instance_id = RedcapTaskExporter._get_next_instance_id(
104 records, "bmi", "record_id", "1"
105 )
107 self.assertEqual(next_instance_id, 6)
108 self.assertEqual(type(next_instance_id), int)
111class RedcapExportErrorTests(TestCase):
112 def test_raises_when_fieldmap_has_unknown_symbols(self) -> None:
113 exporter = MockRedcapNewRecordUploader()
115 task = mock.Mock(tablename="bmi")
116 fieldmap = {"pa_height": "sys.platform"}
118 field_dict = {}
120 with self.assertRaises(RedcapExportException) as cm:
121 exporter.transform_fields(field_dict, task, fieldmap)
123 message = str(cm.exception)
124 self.assertIn("Error in formula 'sys.platform':", message)
125 self.assertIn("Task: 'bmi'", message)
126 self.assertIn("REDCap field: 'pa_height'", message)
127 self.assertIn("'sys' is not defined", message)
129 def test_raises_when_fieldmap_empty_in_config(self) -> None:
131 exporter = MockRedcapTaskExporter()
133 recipient = mock.Mock(redcap_fieldmap_filename="")
134 with self.assertRaises(RedcapExportException) as cm:
135 exporter.get_fieldmap_filename(recipient)
137 message = str(cm.exception)
138 self.assertIn(f"{ConfigParamExportRecipient.REDCAP_FIELDMAP_FILENAME} "
139 f"is empty in the config file", message)
141 def test_raises_when_fieldmap_not_set_in_config(self) -> None:
143 exporter = MockRedcapTaskExporter()
145 recipient = mock.Mock(redcap_fieldmap_filename=None)
146 with self.assertRaises(RedcapExportException) as cm:
147 exporter.get_fieldmap_filename(recipient)
149 message = str(cm.exception)
150 self.assertIn(f"{ConfigParamExportRecipient.REDCAP_FIELDMAP_FILENAME} "
151 f"is not set in the config file", message)
153 def test_raises_when_error_from_redcap_on_import(self) -> None:
154 exporter = MockRedcapNewRecordUploader()
155 exporter.project.import_records.side_effect = redcap.RedcapError(
156 "Something went wrong"
157 )
159 with self.assertRaises(RedcapExportException) as cm:
160 record = {}
161 exporter.upload_record(record)
162 message = str(cm.exception)
164 self.assertIn("Something went wrong", message)
166 def test_raises_when_error_from_redcap_on_init(self) -> None:
167 with mock.patch("redcap.project.Project.__init__") as mock_init:
168 mock_init.side_effect = redcap.RedcapError(
169 "Something went wrong"
170 )
172 with self.assertRaises(RedcapExportException) as cm:
173 exporter = RedcapTaskExporter()
174 recipient = mock.Mock()
175 exporter.get_project(recipient)
177 message = str(cm.exception)
179 self.assertIn("Something went wrong", message)
181 def test_raises_when_field_not_a_file_field(self) -> None:
182 exporter = MockRedcapNewRecordUploader()
183 exporter.project.import_file.side_effect = ValueError(
184 "Error with file field"
185 )
187 task = mock.Mock()
189 with self.assertRaises(RedcapExportException) as cm:
190 record_id = 1
191 repeat_instance = 1
192 file_dict = {"medication_items": b"not a real file"}
193 exporter.upload_files(task, record_id, repeat_instance, file_dict)
194 message = str(cm.exception)
196 self.assertIn("Error with file field", message)
198 def test_raises_when_error_from_redcap_on_import_file(self) -> None:
199 exporter = MockRedcapNewRecordUploader()
200 exporter.project.import_file.side_effect = redcap.RedcapError(
201 "Something went wrong"
202 )
204 task = mock.Mock()
206 with self.assertRaises(RedcapExportException) as cm:
207 record_id = 1
208 repeat_instance = 1
209 file_dict = {"medication_items": b"not a real file"}
210 exporter.upload_files(task, record_id, repeat_instance, file_dict)
211 message = str(cm.exception)
213 self.assertIn("Something went wrong", message)
216class RedcapFieldmapTests(TestCase):
217 def test_raises_when_xml_file_missing(self) -> None:
218 with self.assertRaises(RedcapExportException) as cm:
219 RedcapFieldmap("/does/not/exist/bmi.xml")
221 message = str(cm.exception)
223 self.assertIn("Unable to open fieldmap file", message)
224 self.assertIn("bmi.xml", message)
226 def test_raises_when_fieldmap_missing(self) -> None:
227 with tempfile.NamedTemporaryFile(
228 mode="w", suffix="xml") as fieldmap_file:
229 fieldmap_file.write("""<?xml version="1.0" encoding="UTF-8"?>
230<someothertag></someothertag>
231""")
232 fieldmap_file.flush()
234 with self.assertRaises(RedcapExportException) as cm:
235 RedcapFieldmap(fieldmap_file.name)
237 message = str(cm.exception)
238 self.assertIn(("Expected the root tag to be 'fieldmap' instead of "
239 "'someothertag'"), message)
240 self.assertIn(fieldmap_file.name, message)
242 def test_raises_when_root_tag_missing(self) -> None:
243 with tempfile.NamedTemporaryFile(
244 mode="w", suffix="xml") as fieldmap_file:
245 fieldmap_file.write("""<?xml version="1.0" encoding="UTF-8"?>
246""")
247 fieldmap_file.flush()
249 with self.assertRaises(RedcapExportException) as cm:
250 RedcapFieldmap(fieldmap_file.name)
252 message = str(cm.exception)
253 self.assertIn("There was a problem parsing", message)
254 self.assertIn(fieldmap_file.name, message)
256 def test_raises_when_patient_missing(self) -> None:
257 with tempfile.NamedTemporaryFile(
258 mode="w", suffix="xml") as fieldmap_file:
259 fieldmap_file.write(
260 """<?xml version="1.0" encoding="UTF-8"?>
261 <fieldmap>
262 </fieldmap>
263 """)
264 fieldmap_file.flush()
266 with self.assertRaises(RedcapExportException) as cm:
267 RedcapFieldmap(fieldmap_file.name)
269 message = str(cm.exception)
270 self.assertIn("'patient' is missing from", message)
271 self.assertIn(fieldmap_file.name, message)
273 def test_raises_when_patient_missing_attributes(self) -> None:
274 with tempfile.NamedTemporaryFile(
275 mode="w", suffix="xml") as fieldmap_file:
276 fieldmap_file.write(
277 """<?xml version="1.0" encoding="UTF-8"?>
278 <fieldmap>
279 <patient />
280 </fieldmap>
281 """)
282 fieldmap_file.flush()
284 with self.assertRaises(RedcapExportException) as cm:
285 RedcapFieldmap(fieldmap_file.name)
287 message = str(cm.exception)
288 self.assertIn(
289 "'patient' must have attributes: instrument, redcap_field",
290 message
291 )
292 self.assertIn(fieldmap_file.name, message)
294 def test_raises_when_record_missing(self) -> None:
295 with tempfile.NamedTemporaryFile(
296 mode="w", suffix="xml") as fieldmap_file:
297 fieldmap_file.write(
298 """<?xml version="1.0" encoding="UTF-8"?>
299 <fieldmap>
300 <patient instrument="patient_record" redcap_field="patient_id" />
301 </fieldmap>
302 """) # noqa: E501
303 fieldmap_file.flush()
305 with self.assertRaises(RedcapExportException) as cm:
306 RedcapFieldmap(fieldmap_file.name)
308 message = str(cm.exception)
309 self.assertIn("'record' is missing from", message)
310 self.assertIn(fieldmap_file.name, message)
312 def test_raises_when_record_missing_attributes(self) -> None:
313 with tempfile.NamedTemporaryFile(
314 mode="w", suffix="xml") as fieldmap_file:
315 fieldmap_file.write(
316 """<?xml version="1.0" encoding="UTF-8"?>
317 <fieldmap>
318 <patient instrument="patient_record" redcap_field="patient_id" />
319 <record />
320 </fieldmap>
321 """) # noqa: E501
322 fieldmap_file.flush()
324 with self.assertRaises(RedcapExportException) as cm:
325 RedcapFieldmap(fieldmap_file.name)
327 message = str(cm.exception)
328 self.assertIn(
329 "'record' must have attributes: instrument, redcap_field",
330 message
331 )
332 self.assertIn(fieldmap_file.name, message)
334 def test_raises_when_instruments_missing(self) -> None:
335 with tempfile.NamedTemporaryFile(
336 mode="w", suffix="xml") as fieldmap_file:
337 fieldmap_file.write(
338 """<?xml version="1.0" encoding="UTF-8"?>
339 <fieldmap>
340 <patient instrument="patient_record" redcap_field="patient_id" />
341 <record instrument="patient_record" redcap_field="record_id" />
342 </fieldmap>
343 """) # noqa: E501
344 fieldmap_file.flush()
346 with self.assertRaises(RedcapExportException) as cm:
347 RedcapFieldmap(fieldmap_file.name)
349 message = str(cm.exception)
350 self.assertIn("'instruments' tag is missing from", message)
351 self.assertIn(fieldmap_file.name, message)
353 def test_raises_when_instruments_missing_attributes(self) -> None:
354 with tempfile.NamedTemporaryFile(
355 mode="w", suffix="xml") as fieldmap_file:
356 fieldmap_file.write(
357 """<?xml version="1.0" encoding="UTF-8"?>
358 <fieldmap>
359 <patient instrument="patient_record" redcap_field="patient_id" />
360 <record instrument="patient_record" redcap_field="record_id" />
361 <instruments>
362 <instrument />
363 </instruments>
364 </fieldmap>
365 """) # noqa: E501
366 fieldmap_file.flush()
368 with self.assertRaises(RedcapExportException) as cm:
369 RedcapFieldmap(fieldmap_file.name)
371 message = str(cm.exception)
372 self.assertIn(
373 "'instrument' must have attributes: name, task",
374 message
375 )
376 self.assertIn(fieldmap_file.name, message)
378 def test_raises_when_file_fields_missing_attributes(self) -> None:
379 with tempfile.NamedTemporaryFile(
380 mode="w", suffix="xml") as fieldmap_file:
381 fieldmap_file.write(
382 """<?xml version="1.0" encoding="UTF-8"?>
383 <fieldmap>
384 <patient instrument="patient_record" redcap_field="patient_id" />
385 <record instrument="patient_record" redcap_field="record_id" />
386 <instruments>
387 <instrument name="bmi" task="bmi">
388 <files>
389 <field />
390 </files>
391 </instrument>
392 </instruments>
393 </fieldmap>
394 """) # noqa: E501
395 fieldmap_file.flush()
397 with self.assertRaises(RedcapExportException) as cm:
398 RedcapFieldmap(fieldmap_file.name)
400 message = str(cm.exception)
401 self.assertIn(
402 "'field' must have attributes: name, formula",
403 message
404 )
405 self.assertIn(fieldmap_file.name, message)
407 def test_raises_when_fields_missing_attributes(self) -> None:
408 with tempfile.NamedTemporaryFile(
409 mode="w", suffix="xml") as fieldmap_file:
410 fieldmap_file.write(
411 """<?xml version="1.0" encoding="UTF-8"?>
412 <fieldmap>
413 <patient instrument="patient_record" redcap_field="patient_id" />
414 <record instrument="patient_record" redcap_field="record_id" />
415 <instruments>
416 <instrument name="bmi" task="bmi">
417 <fields>
418 <field />
419 </fields>
420 </instrument>
421 </instruments>
422 </fieldmap>
423 """) # noqa: E501
424 fieldmap_file.flush()
426 with self.assertRaises(RedcapExportException) as cm:
427 RedcapFieldmap(fieldmap_file.name)
429 message = str(cm.exception)
430 self.assertIn(
431 "'field' must have attributes: name, formula",
432 message
433 )
434 self.assertIn(fieldmap_file.name, message)
437# =============================================================================
438# Integration testing
439# =============================================================================
441class RedcapExportTestCase(DemoDatabaseTestCase):
442 fieldmap = ""
444 def setUp(self) -> None:
445 recipientinfo = ExportRecipientInfo()
447 self.recipient = ExportRecipient(recipientinfo)
448 self.recipient.primary_idnum = 1001
450 # auto increment doesn't work for BigInteger with SQLite
451 self.recipient.id = 1
452 self.recipient.recipient_name = "test"
453 self.recipient.redcap_fieldmap_filename = os.path.join(
454 self.tmpdir_obj.name, "redcap_fieldmap.xml"
455 )
456 self.write_fieldmaps(self.recipient.redcap_fieldmap_filename)
458 super().setUp()
460 def write_fieldmaps(self, filename: str) -> None:
461 with open(filename, "w") as f:
462 f.write(self.fieldmap)
464 def create_patient_with_idnum_1001(self) -> "Patient":
465 from camcops_server.cc_modules.cc_idnumdef import IdNumDefinition
466 from camcops_server.cc_modules.cc_patient import Patient
467 from camcops_server.cc_modules.cc_patientidnum import PatientIdNum
469 patient = Patient()
470 patient.id = 2
471 self._apply_standard_db_fields(patient)
472 patient.forename = "Forename2"
473 patient.surname = "Surname2"
474 patient.dob = pendulum.parse("1975-12-12")
475 self.dbsession.add(patient)
477 idnumdef_1001 = IdNumDefinition()
478 idnumdef_1001.which_idnum = 1001
479 idnumdef_1001.description = "Test idnumdef 1001"
480 self.dbsession.add(idnumdef_1001)
481 self.dbsession.commit()
483 patient_idnum1 = PatientIdNum()
484 patient_idnum1.id = 3
485 self._apply_standard_db_fields(patient_idnum1)
486 patient_idnum1.patient_id = patient.id
487 patient_idnum1.which_idnum = 1001
488 patient_idnum1.idnum_value = 555
489 self.dbsession.add(patient_idnum1)
490 self.dbsession.commit()
492 return patient
495class BmiRedcapExportTestCase(RedcapExportTestCase):
496 def __init__(self, *args, **kwargs) -> None:
497 super().__init__(*args, **kwargs)
498 self.id_sequence = self.get_id()
500 @staticmethod
501 def get_id() -> Generator[int, None, None]:
502 i = 1
504 while True:
505 yield i
506 i += 1
509class BmiRedcapValidFieldmapTestCase(BmiRedcapExportTestCase):
510 fieldmap = """<?xml version="1.0" encoding="UTF-8"?>
511<fieldmap>
512 <patient instrument="patient_record" redcap_field="patient_id" />
513 <record instrument="instrument_with_record_id" redcap_field="record_id" />
514 <instruments>
515 <instrument task="bmi" name="bmi">
516 <fields>
517 <field name="pa_height" formula="format(task.height_m, '.1f')" />
518 <field name="pa_weight" formula="format(task.mass_kg, '.1f')" />
519 <field name="bmi_date" formula="format_datetime(task.when_created, DateFormat.ISO8601_DATE_ONLY)" />
520 </fields>
521 </instrument>
522 </instruments>
523</fieldmap>""" # noqa: E501
526class BmiRedcapExportTests(BmiRedcapValidFieldmapTestCase):
527 """
528 These are more of a test of the fieldmap code than anything
529 related to the BMI task
530 """
532 def create_tasks(self) -> None:
533 from camcops_server.tasks.bmi import Bmi
535 patient = self.create_patient_with_idnum_1001()
536 self.task = Bmi()
537 self.apply_standard_task_fields(self.task)
538 self.task.id = next(self.id_sequence)
539 self.task.height_m = 1.83
540 self.task.mass_kg = 67.57
541 self.task.patient_id = patient.id
542 self.dbsession.add(self.task)
543 self.dbsession.commit()
545 def test_record_exported(self) -> None:
546 from camcops_server.cc_modules.cc_exportmodels import (
547 ExportedTask,
548 ExportedTaskRedcap
549 )
551 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
552 exported_task_redcap = ExportedTaskRedcap(exported_task)
554 exporter = MockRedcapTaskExporter()
555 project = exporter.get_project()
556 project.export_records.return_value = DataFrame({"patient_id": []})
557 project.import_records.return_value = ["123,0"]
558 project.export_project_info.return_value = {
559 "record_autonumbering_enabled": 1
560 }
562 exporter.export_task(self.req, exported_task_redcap)
563 self.assertEquals(exported_task_redcap.redcap_record_id, "123")
564 self.assertEquals(exported_task_redcap.redcap_instrument_name, "bmi")
565 self.assertEquals(exported_task_redcap.redcap_instance_id, 1)
567 args, kwargs = project.export_records.call_args
569 self.assertIn("bmi", kwargs['forms'])
570 self.assertIn("patient_record", kwargs['forms'])
571 self.assertIn("instrument_with_record_id", kwargs['forms'])
573 # Initial call with original record
574 args, kwargs = project.import_records.call_args_list[0]
576 rows = args[0]
577 record = rows[0]
579 self.assertEquals(record["redcap_repeat_instrument"], "bmi")
580 self.assertEquals(record["redcap_repeat_instance"], 1)
581 self.assertEquals(record["record_id"], "0")
582 self.assertEquals(record["bmi_complete"],
583 RedcapRecordStatus.COMPLETE.value)
584 self.assertEquals(record["bmi_date"], "2010-07-07")
586 self.assertEquals(record["pa_height"], "1.8")
587 self.assertEquals(record["pa_weight"], "67.6")
589 self.assertEquals(kwargs["return_content"], "auto_ids")
590 self.assertTrue(kwargs["force_auto_number"])
592 # Second call with updated patient ID
593 args, kwargs = project.import_records.call_args_list[1]
594 rows = args[0]
595 record = rows[0]
597 self.assertEquals(record["patient_id"], 555)
599 def test_record_exported_with_non_integer_id(self) -> None:
600 from camcops_server.cc_modules.cc_exportmodels import (
601 ExportedTask,
602 ExportedTaskRedcap
603 )
605 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
606 exported_task_redcap = ExportedTaskRedcap(exported_task)
608 exporter = MockRedcapTaskExporter()
609 project = exporter.get_project()
610 project.export_records.return_value = DataFrame({"patient_id": []})
611 project.import_records.return_value = ["15-123,0"]
612 project.export_project_info.return_value = {
613 "record_autonumbering_enabled": 1
614 }
616 exporter.export_task(self.req, exported_task_redcap)
617 self.assertEquals(exported_task_redcap.redcap_record_id, "15-123")
619 def test_record_id_generated_when_no_autonumbering(self) -> None:
620 from camcops_server.cc_modules.cc_exportmodels import (
621 ExportedTask,
622 ExportedTaskRedcap
623 )
625 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
626 exported_task_redcap = ExportedTaskRedcap(exported_task)
628 exporter = MockRedcapTaskExporter()
629 project = exporter.get_project()
630 project.export_records.return_value = DataFrame({"patient_id": []})
631 project.import_records.return_value = {"count": 1}
632 project.export_project_info.return_value = {
633 "record_autonumbering_enabled": 0
634 }
635 project.generate_next_record_name.return_value = "15-29"
637 exporter.export_task(self.req, exported_task_redcap)
639 # Initial call with original record
640 args, kwargs = project.import_records.call_args_list[0]
642 rows = args[0]
643 record = rows[0]
645 self.assertEquals(record["record_id"], "15-29")
646 self.assertEquals(kwargs["return_content"], "count")
647 self.assertFalse(kwargs["force_auto_number"])
649 def test_record_imported_when_no_existing_records(self) -> None:
650 from camcops_server.cc_modules.cc_exportmodels import (
651 ExportedTask,
652 ExportedTaskRedcap,
653 )
655 exporter = MockRedcapTaskExporter()
656 project = exporter.get_project()
657 project.export_records.return_value = DataFrame()
658 project.import_records.return_value = ["1,0"]
659 project.export_project_info.return_value = {
660 "record_autonumbering_enabled": 1
661 }
663 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
664 exported_task_redcap = ExportedTaskRedcap(exported_task)
665 exporter.export_task(self.req, exported_task_redcap)
667 self.assertEquals(exported_task_redcap.redcap_record_id, "1")
668 self.assertEquals(exported_task_redcap.redcap_instrument_name, "bmi")
669 self.assertEquals(exported_task_redcap.redcap_instance_id, 1)
672class BmiRedcapUpdateTests(BmiRedcapValidFieldmapTestCase):
673 def create_tasks(self) -> None:
674 from camcops_server.tasks.bmi import Bmi
675 patient = self.create_patient_with_idnum_1001()
676 self.task1 = Bmi()
677 self.apply_standard_task_fields(self.task1)
678 self.task1.id = next(self.id_sequence)
679 self.task1.height_m = 1.83
680 self.task1.mass_kg = 67.57
681 self.task1.patient_id = patient.id
682 self.dbsession.add(self.task1)
684 self.task2 = Bmi()
685 self.apply_standard_task_fields(self.task2)
686 self.task2.id = next(self.id_sequence)
687 self.task2.height_m = 1.83
688 self.task2.mass_kg = 68.5
689 self.task2.patient_id = patient.id
690 self.dbsession.add(self.task2)
691 self.dbsession.commit()
693 def test_existing_record_id_used_for_update(self) -> None:
694 from camcops_server.cc_modules.cc_exportmodels import (
695 ExportedTask,
696 ExportedTaskRedcap,
697 )
699 exporter = MockRedcapTaskExporter()
700 project = exporter.get_project()
701 project.export_records.return_value = DataFrame({"patient_id": []})
702 project.import_records.return_value = ["123,0"]
703 project.export_project_info.return_value = {
704 "record_autonumbering_enabled": 1
705 }
707 exported_task1 = ExportedTask(task=self.task1, recipient=self.recipient)
708 exported_task_redcap1 = ExportedTaskRedcap(exported_task1)
709 exporter.export_task(self.req, exported_task_redcap1)
710 self.assertEquals(exported_task_redcap1.redcap_record_id, "123")
711 self.assertEquals(exported_task_redcap1.redcap_instrument_name, "bmi")
712 self.assertEquals(exported_task_redcap1.redcap_instance_id, 1)
714 project.export_records.return_value = DataFrame({
715 "record_id": ["123"],
716 "patient_id": [555],
717 "redcap_repeat_instrument": ["bmi"],
718 "redcap_repeat_instance": [1],
719 })
720 exported_task2 = ExportedTask(task=self.task2, recipient=self.recipient)
721 exported_task_redcap2 = ExportedTaskRedcap(exported_task2)
723 exporter.export_task(self.req, exported_task_redcap2)
724 self.assertEquals(exported_task_redcap2.redcap_record_id, "123")
725 self.assertEquals(exported_task_redcap2.redcap_instrument_name, "bmi")
726 self.assertEquals(exported_task_redcap2.redcap_instance_id, 2)
728 # Third call (after initial record and patient ID)
729 args, kwargs = project.import_records.call_args_list[2]
731 rows = args[0]
732 record = rows[0]
734 self.assertEquals(record["record_id"], "123")
735 self.assertEquals(record["redcap_repeat_instance"], 2)
736 self.assertEquals(kwargs["return_content"], "count")
737 self.assertFalse(kwargs["force_auto_number"])
740class Phq9RedcapExportTests(RedcapExportTestCase):
741 """
742 These are more of a test of the fieldmap code than anything
743 related to the PHQ9 task. For these we have also renamed the record_id
744 field.
745 """
746 fieldmap = """<?xml version="1.0" encoding="UTF-8"?>
747<fieldmap>
748 <patient instrument="patient_record" redcap_field="patient_id" />
749 <record instrument="patient_record" redcap_field="my_record_id" />
750 <instruments>
751 <instrument task="phq9" name="patient_health_questionnaire_9">
752 <fields>
753 <field name="phq9_how_difficult" formula="task.q10 + 1 if task.q10 is not None else None" />
754 <field name="phq9_total_score" formula="task.total_score()" />
755 <field name="phq9_first_name" formula="task.patient.forename" />
756 <field name="phq9_last_name" formula="task.patient.surname" />
757 <field name="phq9_date_enrolled" formula="format_datetime(task.when_created,DateFormat.ISO8601_DATE_ONLY)" />
758 <field name="phq9_1" formula="task.q1" />
759 <field name="phq9_2" formula="task.q2" />
760 <field name="phq9_3" formula="task.q3" />
761 <field name="phq9_4" formula="task.q4" />
762 <field name="phq9_5" formula="task.q5" />
763 <field name="phq9_6" formula="task.q6" />
764 <field name="phq9_7" formula="task.q7" />
765 <field name="phq9_8" formula="task.q8" />
766 <field name="phq9_9" formula="task.q9" />
767 </fields>
768 </instrument>
769 </instruments>
770</fieldmap>""" # noqa: E501
772 def __init__(self, *args, **kwargs) -> None:
773 super().__init__(*args, **kwargs)
774 self.id_sequence = self.get_id()
776 @staticmethod
777 def get_id() -> Generator[int, None, None]:
778 i = 1
780 while True:
781 yield i
782 i += 1
784 def create_tasks(self) -> None:
785 from camcops_server.tasks.phq9 import Phq9
786 patient = self.create_patient_with_idnum_1001()
787 self.task = Phq9()
788 self.apply_standard_task_fields(self.task)
789 self.task.id = next(self.id_sequence)
790 self.task.q1 = 0
791 self.task.q2 = 1
792 self.task.q3 = 2
793 self.task.q4 = 3
794 self.task.q5 = 0
795 self.task.q6 = 1
796 self.task.q7 = 2
797 self.task.q8 = 3
798 self.task.q9 = 0
799 self.task.q10 = 3
800 self.task.patient_id = patient.id
801 self.dbsession.add(self.task)
802 self.dbsession.commit()
804 def test_record_exported(self) -> None:
805 from camcops_server.cc_modules.cc_exportmodels import (
806 ExportedTask,
807 ExportedTaskRedcap,
808 )
810 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
811 exported_task_redcap = ExportedTaskRedcap(exported_task)
813 exporter = MockRedcapTaskExporter()
814 project = exporter.get_project()
815 project.export_records.return_value = DataFrame({"patient_id": []})
816 project.import_records.return_value = ["123,0"]
817 project.export_project_info.return_value = {
818 "record_autonumbering_enabled": 1
819 }
821 exporter.export_task(self.req, exported_task_redcap)
822 self.assertEquals(exported_task_redcap.redcap_record_id, "123")
823 self.assertEquals(exported_task_redcap.redcap_instrument_name,
824 "patient_health_questionnaire_9")
825 self.assertEquals(exported_task_redcap.redcap_instance_id, 1)
827 # Initial call with new record
828 args, kwargs = project.import_records.call_args_list[0]
830 rows = args[0]
831 record = rows[0]
833 self.assertEquals(record["redcap_repeat_instrument"],
834 "patient_health_questionnaire_9")
835 self.assertEquals(record["my_record_id"], "0")
836 self.assertEquals(record["patient_health_questionnaire_9_complete"],
837 RedcapRecordStatus.COMPLETE.value)
838 self.assertEquals(record["phq9_how_difficult"], 4)
839 self.assertEquals(record["phq9_total_score"], 12)
840 self.assertEquals(record["phq9_first_name"], "Forename2")
841 self.assertEquals(record["phq9_last_name"], "Surname2")
842 self.assertEquals(record["phq9_date_enrolled"], "2010-07-07")
844 self.assertEquals(record["phq9_1"], 0)
845 self.assertEquals(record["phq9_2"], 1)
846 self.assertEquals(record["phq9_3"], 2)
847 self.assertEquals(record["phq9_4"], 3)
848 self.assertEquals(record["phq9_5"], 0)
849 self.assertEquals(record["phq9_6"], 1)
850 self.assertEquals(record["phq9_7"], 2)
851 self.assertEquals(record["phq9_8"], 3)
852 self.assertEquals(record["phq9_9"], 0)
854 self.assertEquals(kwargs["return_content"], "auto_ids")
855 self.assertTrue(kwargs["force_auto_number"])
857 # Second call with patient ID
858 args, kwargs = project.import_records.call_args_list[1]
860 rows = args[0]
861 record = rows[0]
862 self.assertEquals(record["patient_id"], 555)
865class MedicationTherapyRedcapExportTests(RedcapExportTestCase):
866 """
867 These are more of a test of the file upload code than anything
868 related to the KhandakerMojoMedicationTherapy task
869 """
870 fieldmap = """<?xml version="1.0" encoding="UTF-8"?>
871<fieldmap>
872 <event name="event_1_arm_1" />
873 <patient instrument="patient_record" redcap_field="patient_id" />
874 <record instrument="patient_record" redcap_field="record_id" />
875 <instruments>
876 <instrument task="khandaker_mojo_medicationtherapy" name="medication_table">
877 <files>
878 <field name="medtbl_medication_items" formula="task.get_pdf(request)" />
879 </files>
880 </instrument>
881 </instruments>
882</fieldmap>""" # noqa: E501
884 def __init__(self, *args, **kwargs) -> None:
885 super().__init__(*args, **kwargs)
886 self.id_sequence = self.get_id()
888 @staticmethod
889 def get_id() -> Generator[int, None, None]:
890 i = 1
892 while True:
893 yield i
894 i += 1
896 def create_tasks(self) -> None:
897 from camcops_server.tasks.khandaker_mojo_medicationtherapy import (
898 KhandakerMojoMedicationTherapy,
899 )
901 patient = self.create_patient_with_idnum_1001()
902 self.task = KhandakerMojoMedicationTherapy()
903 self.apply_standard_task_fields(self.task)
904 self.task.id = next(self.id_sequence)
905 self.task.patient_id = patient.id
906 self.dbsession.add(self.task)
907 self.dbsession.commit()
909 def test_record_exported(self) -> None:
910 from camcops_server.cc_modules.cc_exportmodels import (
911 ExportedTask,
912 ExportedTaskRedcap
913 )
915 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
916 exported_task_redcap = ExportedTaskRedcap(exported_task)
918 exporter = MockRedcapTaskExporter()
919 project = exporter.get_project()
920 project.export_records.return_value = DataFrame({"patient_id": []})
921 project.import_records.return_value = ["123,0"]
922 project.export_project_info.return_value = {
923 "record_autonumbering_enabled": 1
924 }
926 # We can't just look at the call_args on the mock object because
927 # the file will already have been closed by then
928 # noinspection PyUnusedLocal
929 def read_pdf_bytes(*import_file_args, **import_file_kwargs) -> None:
930 # record, field, fname, fobj
931 file_obj = import_file_args[3]
932 read_pdf_bytes.pdf_header = file_obj.read(5)
934 project.import_file.side_effect = read_pdf_bytes
936 exporter.export_task(self.req, exported_task_redcap)
937 self.assertEquals(exported_task_redcap.redcap_record_id, "123")
938 self.assertEquals(exported_task_redcap.redcap_instrument_name,
939 "medication_table")
940 self.assertEquals(exported_task_redcap.redcap_instance_id, 1)
942 args, kwargs = project.import_file.call_args
944 record_id = args[0]
945 fieldname = args[1]
946 filename = args[2]
948 self.assertEquals(record_id, "123")
949 self.assertEquals(fieldname, "medtbl_medication_items")
950 self.assertEquals(
951 filename,
952 "khandaker_mojo_medicationtherapy_123_medtbl_medication_items"
953 )
955 self.assertEquals(kwargs["repeat_instance"], 1)
956 # noinspection PyUnresolvedReferences
957 self.assertEquals(read_pdf_bytes.pdf_header, b"%PDF-")
958 self.assertEquals(kwargs["event"], "event_1_arm_1")
961class MultipleTaskRedcapExportTests(RedcapExportTestCase):
962 fieldmap = """<?xml version="1.0" encoding="UTF-8"?>
963<fieldmap>
964 <patient instrument="patient_record" redcap_field="patient_id" />
965 <record instrument="patient_record" redcap_field="record_id" />
966 <instruments>
967 <instrument task="bmi" name="bmi" event="bmi_event">
968 <fields>
969 <field name="pa_height" formula="format(task.height_m, '.1f')" />
970 <field name="pa_weight" formula="format(task.mass_kg, '.1f')" />
971 <field name="bmi_date" formula="format_datetime(task.when_created, DateFormat.ISO8601_DATE_ONLY)" />
972 </fields>
973 </instrument>
974 <instrument task="khandaker_mojo_medicationtherapy" name="medication_table" event="mojo_event">
975 <files>
976 <field name="medtbl_medication_items" formula="task.get_pdf(request)" />
977 </files>
978 </instrument>
979 </instruments>
980</fieldmap>
981""" # noqa: E501
983 def __init__(self, *args, **kwargs) -> None:
984 super().__init__(*args, **kwargs)
985 self.id_sequence = self.get_id()
987 @staticmethod
988 def get_id() -> Generator[int, None, None]:
989 i = 1
991 while True:
992 yield i
993 i += 1
995 def create_tasks(self) -> None:
996 from camcops_server.tasks.khandaker_mojo_medicationtherapy import (
997 KhandakerMojoMedicationTherapy,
998 )
1000 patient = self.create_patient_with_idnum_1001()
1001 self.mojo_task = KhandakerMojoMedicationTherapy()
1002 self.apply_standard_task_fields(self.mojo_task)
1003 self.mojo_task.id = next(self.id_sequence)
1004 self.mojo_task.patient_id = patient.id
1005 self.dbsession.add(self.mojo_task)
1006 self.dbsession.commit()
1008 from camcops_server.tasks.bmi import Bmi
1009 self.bmi_task = Bmi()
1010 self.apply_standard_task_fields(self.bmi_task)
1011 self.bmi_task.id = next(self.id_sequence)
1012 self.bmi_task.height_m = 1.83
1013 self.bmi_task.mass_kg = 67.57
1014 self.bmi_task.patient_id = patient.id
1015 self.dbsession.add(self.bmi_task)
1016 self.dbsession.commit()
1018 def test_instance_ids_on_different_tasks_in_same_record(self) -> None:
1019 from camcops_server.cc_modules.cc_exportmodels import (
1020 ExportedTask,
1021 ExportedTaskRedcap,
1022 )
1023 exporter = MockRedcapTaskExporter()
1024 project = exporter.get_project()
1025 project.export_records.return_value = DataFrame({"patient_id": []})
1026 project.import_records.return_value = ["123,0"]
1027 project.export_project_info.return_value = {
1028 "record_autonumbering_enabled": 1
1029 }
1031 exported_task_mojo = ExportedTask(task=self.mojo_task,
1032 recipient=self.recipient)
1033 exported_task_redcap_mojo = ExportedTaskRedcap(exported_task_mojo)
1034 exporter.export_task(self.req, exported_task_redcap_mojo)
1035 self.assertEquals(exported_task_redcap_mojo.redcap_record_id, "123")
1036 args, kwargs = project.import_file.call_args
1038 self.assertEquals(kwargs["repeat_instance"], 1)
1040 project.export_records.return_value = DataFrame({
1041 "record_id": ["123"],
1042 "patient_id": [555],
1043 "redcap_repeat_instrument": ["khandaker_mojo_medicationtherapy"],
1044 "redcap_repeat_instance": [1],
1045 })
1046 exported_task_bmi = ExportedTask(task=self.bmi_task,
1047 recipient=self.recipient)
1048 exported_task_redcap_bmi = ExportedTaskRedcap(exported_task_bmi)
1050 exporter.export_task(self.req, exported_task_redcap_bmi)
1052 # Import of second task, but is first instance
1053 # (third call to import_records)
1054 args, kwargs = project.import_records.call_args_list[2]
1056 rows = args[0]
1057 record = rows[0]
1059 self.assertEquals(record["redcap_repeat_instance"], 1)
1061 def test_imported_into_different_events(self) -> None:
1062 from camcops_server.cc_modules.cc_exportmodels import (
1063 ExportedTask,
1064 ExportedTaskRedcap,
1065 )
1066 exporter = MockRedcapTaskExporter()
1067 project = exporter.get_project()
1069 project.is_longitudinal = mock.Mock(return_value=True)
1070 project.export_records.return_value = DataFrame({"patient_id": []})
1071 project.import_records.return_value = ["123,0"]
1072 project.export_project_info.return_value = {
1073 "record_autonumbering_enabled": 1
1074 }
1076 exported_task_mojo = ExportedTask(task=self.mojo_task,
1077 recipient=self.recipient)
1078 exported_task_redcap_mojo = ExportedTaskRedcap(exported_task_mojo)
1080 exporter.export_task(self.req, exported_task_redcap_mojo)
1082 args, kwargs = project.import_records.call_args_list[0]
1083 rows = args[0]
1084 record = rows[0]
1086 self.assertEquals(record["redcap_event_name"], "mojo_event")
1087 args, kwargs = project.import_file.call_args
1089 self.assertEquals(kwargs["event"], "mojo_event")
1091 exported_task_bmi = ExportedTask(task=self.bmi_task,
1092 recipient=self.recipient)
1093 exported_task_redcap_bmi = ExportedTaskRedcap(exported_task_bmi)
1095 exporter.export_task(self.req, exported_task_redcap_bmi)
1097 # Import of second task (third call to import_records)
1098 args, kwargs = project.import_records.call_args_list[2]
1099 rows = args[0]
1100 record = rows[0]
1101 self.assertEquals(record["redcap_event_name"], "bmi_event")
1104class BadConfigurationRedcapTests(RedcapExportTestCase):
1105 def __init__(self, *args, **kwargs) -> None:
1106 super().__init__(*args, **kwargs)
1107 self.id_sequence = self.get_id()
1109 @staticmethod
1110 def get_id() -> Generator[int, None, None]:
1111 i = 1
1113 while True:
1114 yield i
1115 i += 1
1117 def create_tasks(self) -> None:
1118 from camcops_server.tasks.bmi import Bmi
1119 patient = self.create_patient_with_idnum_1001()
1120 self.task = Bmi()
1121 self.apply_standard_task_fields(self.task)
1122 self.task.id = next(self.id_sequence)
1123 self.task.height_m = 1.83
1124 self.task.mass_kg = 67.57
1125 self.task.patient_id = patient.id
1126 self.dbsession.add(self.task)
1127 self.dbsession.commit()
1130class MissingInstrumentRedcapTests(BadConfigurationRedcapTests):
1131 fieldmap = """<?xml version="1.0" encoding="UTF-8"?>
1132<fieldmap>
1133 <patient instrument="patient_record" redcap_field="patient_id" />
1134 <record instrument="patient_record" redcap_field="record_id" />
1135 <instruments>
1136 <instrument task="phq9" name="patient_health_questionnaire_9">
1137 <fields>
1138 </fields>
1139 </instrument>
1140 </instruments>
1141</fieldmap>""" # noqa: E501
1143 def test_raises_when_instrument_missing_from_fieldmap(self) -> None:
1144 from camcops_server.cc_modules.cc_exportmodels import (
1145 ExportedTask,
1146 ExportedTaskRedcap
1147 )
1149 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
1150 exported_task_redcap = ExportedTaskRedcap(exported_task)
1152 exporter = MockRedcapTaskExporter()
1153 project = exporter.get_project()
1154 project.export_records.return_value = DataFrame({"patient_id": []})
1155 project.import_records.return_value = ["123,0"]
1157 with self.assertRaises(RedcapExportException) as cm:
1158 exporter.export_task(self.req, exported_task_redcap)
1160 message = str(cm.exception)
1161 self.assertIn("Instrument for task 'bmi' is missing from the fieldmap",
1162 message)
1165class IncorrectRecordIdRedcapTests(BadConfigurationRedcapTests):
1166 fieldmap = """<?xml version="1.0" encoding="UTF-8"?>
1167<fieldmap>
1168 <patient instrument="patient_record" redcap_field="patient_id" />
1169 <record instrument="patient_record" redcap_field="my_record_id" />
1170 <instruments>
1171 <instrument task="bmi" name="bmi">
1172 <fields>
1173 </fields>
1174 </instrument>
1175 </instruments>
1176</fieldmap>""" # noqa: E501
1178 def test_raises_when_record_id_is_incorrect(self) -> None:
1179 from camcops_server.cc_modules.cc_exportmodels import (
1180 ExportedTask,
1181 ExportedTaskRedcap
1182 )
1184 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
1185 exported_task_redcap = ExportedTaskRedcap(exported_task)
1187 exporter = MockRedcapTaskExporter()
1188 project = exporter.get_project()
1189 project.export_records.return_value = DataFrame({
1190 "record_id": ["123"],
1191 "patient_id": [555],
1192 "redcap_repeat_instrument": ["bmi"],
1193 "redcap_repeat_instance": [1],
1194 })
1195 project.import_records.return_value = ["123,0"]
1196 project.export_project_info.return_value = {
1197 "record_autonumbering_enabled": 1
1198 }
1200 with self.assertRaises(RedcapExportException) as cm:
1201 exporter.export_task(self.req, exported_task_redcap)
1203 message = str(cm.exception)
1204 self.assertIn("Field 'my_record_id' does not exist in REDCap",
1205 message)
1208class IncorrectPatientIdRedcapTests(BadConfigurationRedcapTests):
1209 fieldmap = """<?xml version="1.0" encoding="UTF-8"?>
1210<fieldmap>
1211 <patient instrument="patient_record" redcap_field="my_patient_id" />
1212 <record instrument="patient_record" redcap_field="record_id" />
1213 <instruments>
1214 <instrument task="bmi" name="bmi">
1215 <fields>
1216 </fields>
1217 </instrument>
1218 </instruments>
1219</fieldmap>""" # noqa: E501
1221 def test_raises_when_patient_id_is_incorrect(self) -> None:
1222 from camcops_server.cc_modules.cc_exportmodels import (
1223 ExportedTask,
1224 ExportedTaskRedcap
1225 )
1227 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
1228 exported_task_redcap = ExportedTaskRedcap(exported_task)
1230 exporter = MockRedcapTaskExporter()
1231 project = exporter.get_project()
1232 project.export_records.return_value = DataFrame({
1233 "record_id": ["123"],
1234 "patient_id": [555],
1235 "redcap_repeat_instrument": ["bmi"],
1236 "redcap_repeat_instance": [1],
1237 })
1238 project.import_records.return_value = ["123,0"]
1239 project.export_project_info.return_value = {
1240 "record_autonumbering_enabled": 1
1241 }
1243 with self.assertRaises(RedcapExportException) as cm:
1244 exporter.export_task(self.req, exported_task_redcap)
1246 message = str(cm.exception)
1247 self.assertIn("Field 'my_patient_id' does not exist in REDCap",
1248 message)
1251class MissingPatientInstrumentRedcapTests(BadConfigurationRedcapTests):
1252 fieldmap = """<?xml version="1.0" encoding="UTF-8"?>
1253<fieldmap>
1254 <patient instrument="patient_record" redcap_field="my_patient_id" />
1255 <record instrument="patient_record" redcap_field="record_id" />
1256 <instruments>
1257 <instrument task="bmi" name="bmi">
1258 <fields>
1259 </fields>
1260 </instrument>
1261 </instruments>
1262</fieldmap>""" # noqa: E501
1264 def test_raises_when_instrument_is_missing(self) -> None:
1265 from camcops_server.cc_modules.cc_exportmodels import (
1266 ExportedTask,
1267 ExportedTaskRedcap
1268 )
1270 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
1271 exported_task_redcap = ExportedTaskRedcap(exported_task)
1273 exporter = MockRedcapTaskExporter()
1274 project = exporter.get_project()
1275 project.export_records.side_effect = redcap.RedcapError(
1276 "Something went wrong"
1277 )
1279 with self.assertRaises(RedcapExportException) as cm:
1280 exporter.export_task(self.req, exported_task_redcap)
1282 message = str(cm.exception)
1283 self.assertIn("Something went wrong", message)
1286class MissingEventRedcapTests(BadConfigurationRedcapTests):
1287 fieldmap = """<?xml version="1.0" encoding="UTF-8"?>
1288<fieldmap>
1289 <patient instrument="patient_record" redcap_field="my_patient_id" />
1290 <record instrument="patient_record" redcap_field="record_id" />
1291 <instruments>
1292 <instrument task="bmi" name="bmi">
1293 <fields>
1294 </fields>
1295 </instrument>
1296 </instruments>
1297</fieldmap>""" # noqa: E501
1299 def test_raises_for_longitudinal_project(self) -> None:
1300 from camcops_server.cc_modules.cc_exportmodels import (
1301 ExportedTask,
1302 ExportedTaskRedcap
1303 )
1305 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
1306 exported_task_redcap = ExportedTaskRedcap(exported_task)
1308 exporter = MockRedcapTaskExporter()
1309 project = exporter.get_project()
1311 project.is_longitudinal = mock.Mock(return_value=True)
1313 with self.assertRaises(RedcapExportException) as cm:
1314 exporter.export_task(self.req, exported_task_redcap)
1316 message = str(cm.exception)
1317 self.assertEqual(MISSING_EVENT_TAG_OR_ATTRIBUTE, message)
1320class MissingInstrumentEventRedcapTests(BadConfigurationRedcapTests):
1321 fieldmap = """<?xml version="1.0" encoding="UTF-8"?>
1322<fieldmap>
1323 <patient instrument="patient_record" redcap_field="my_patient_id" />
1324 <record instrument="patient_record" redcap_field="record_id" />
1325 <instruments>
1326 <instrument task="bmi" name="bmi">
1327 <fields>
1328 </fields>
1329 </instrument>
1330 <instrument task="phq9" name="phq9" event="phq9_event">
1331 <fields>
1332 </fields>
1333 </instrument>
1334 </instruments>
1335</fieldmap>""" # noqa: E501
1337 def test_raises_when_instrument_missing_event(self) -> None:
1338 from camcops_server.cc_modules.cc_exportmodels import (
1339 ExportedTask,
1340 ExportedTaskRedcap
1341 )
1343 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
1344 exported_task_redcap = ExportedTaskRedcap(exported_task)
1346 exporter = MockRedcapTaskExporter()
1347 project = exporter.get_project()
1349 project.is_longitudinal = mock.Mock(return_value=True)
1351 with self.assertRaises(RedcapExportException) as cm:
1352 exporter.export_task(self.req, exported_task_redcap)
1354 message = str(cm.exception)
1355 self.assertEqual(MISSING_EVENT_TAG_OR_ATTRIBUTE, message)
1358class AnonymousTaskRedcapTests(RedcapExportTestCase):
1359 def create_tasks(self) -> None:
1360 from camcops_server.tasks.apeq_cpft_perinatal import APEQCPFTPerinatal
1361 self.task = APEQCPFTPerinatal()
1362 self.apply_standard_task_fields(self.task)
1363 self.task.id = 1
1364 self.dbsession.add(self.task)
1365 self.dbsession.commit()
1367 def test_raises_when_task_is_anonymous(self) -> None:
1368 from camcops_server.cc_modules.cc_exportmodels import (
1369 ExportedTask,
1370 ExportedTaskRedcap
1371 )
1373 exported_task = ExportedTask(task=self.task, recipient=self.recipient)
1374 exported_task_redcap = ExportedTaskRedcap(exported_task)
1376 exporter = MockRedcapTaskExporter()
1378 with self.assertRaises(RedcapExportException) as cm:
1379 exporter.export_task(self.req, exported_task_redcap)
1381 message = str(cm.exception)
1382 self.assertIn("Skipping anonymous task 'apeq_cpft_perinatal'", message)