Skip to content

Commit

Permalink
[researcher-apis] renames data quantities endpoints, reworks some tes…
Browse files Browse the repository at this point in the history
…ts for appversion stuff, documents why it is NOT a certain way.
  • Loading branch information
biblicabeebli committed Jul 22, 2024
1 parent a9c466e commit a41fefa
Show file tree
Hide file tree
Showing 8 changed files with 121 additions and 39 deletions.
2 changes: 1 addition & 1 deletion data_access_api_reference/other_api_reference_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
# Endpoint takes one parameter, study_id, returns a json list of participant ids for that study.
# This endpoint is provisionally deprecated, it is not guaranteed to be available in the future.

# TARGET_ENDPOINT_URL = f"{MY_BEIWE_SERVER}/get-participant-data-info/v1"
# TARGET_ENDPOINT_URL = f"{MY_BEIWE_SERVER}/get-participant-data-quantities/v1"
# Endpoint takes one parameter, study_id, returns complex json of participants and their data
# quantity metrics as reported on the dashboard.

Expand Down
3 changes: 3 additions & 0 deletions database/user_models_participant.py
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,9 @@ def heartbeat_notifications(cls) -> QuerySet[ParticipantActionLog]:


class AppVersionHistory(TimestampedModel):
""" We can't apply a unique constraint on these fields because that would cover over some
possible bugs that we want to be able to detect (and further complicate the participant
credential code path). """
participant = models.ForeignKey(Participant, null=False, on_delete=models.PROTECT, related_name="app_version_history")
app_version_code = models.CharField(max_length=16, blank=False, null=False)
app_version_name = models.CharField(max_length=16, blank=False, null=False)
Expand Down
3 changes: 2 additions & 1 deletion endpoints/data_api_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def download_study_survey_history(request: ApiStudyResearcherRequest):

@require_POST
@api_study_credential_check()
def get_participant_data_info(request: ApiStudyResearcherRequest, study_id: str = None):
def get_participant_data_quantities(request: ApiStudyResearcherRequest, study_id: str = None):
""" Returns a JSON response of the participant data upload summaries. """
summary_data = get_participant_data_upload_summary(request.api_study)
return HttpResponse(orjson.dumps(summary_data), status=200, content_type="application/json")
Expand Down Expand Up @@ -140,6 +140,7 @@ def get_participant_table_data(request: ApiStudyResearcherRequest):

## Summary Statistics and Forest Output


@require_POST
@api_study_credential_check()
def get_summary_statistics(request: ApiStudyResearcherRequest, study_id: str = None):
Expand Down
11 changes: 2 additions & 9 deletions tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ class SmartRequestsTestCase(BasicSessionTestCase):
REDIRECT_ENDPOINT_NAME = None
IGNORE_THIS_ENDPOINT = "ignore this endpoint" # turns out we need to suppress this sometimes...

# Never add this test to a subclass
def test_has_valid_endpoint_name_and_is_placed_in_correct_file(self):
# case: [currently] subclasses that but are intended to be further subclassed are contained
# in the tests.common, so they can break the rules.
Expand Down Expand Up @@ -498,15 +499,7 @@ def smart_post(self, *reverse_args, reverse_kwargs=None, **post_params) -> HttpR
msg="last_os_version did not update")
self.assertEqual(tracker_vals["device_status_report"], post_params["device_status_report"],
msg="device_status_report did not update")
else:
self.assertEqual(tracker_vals["last_version_code"], orig_vals["last_version_code"],
msg="last_version_code updated")
self.assertEqual(tracker_vals["last_version_name"], orig_vals["last_version_name"],
msg="last_version_name updated")
self.assertEqual(tracker_vals["last_os_version"], orig_vals["last_os_version"],
msg="last_os_version updated")
self.assertEqual(tracker_vals["device_status_report"], orig_vals["device_status_report"],
msg="device_status_report updated")

# reset the toggle after every request
self.INJECT_DEVICE_TRACKER_PARAMS = True
return ret
Expand Down
3 changes: 2 additions & 1 deletion tests/test_data_api_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ def test_two_participants(self):


class TestGetParticipantDataInfo(DataApiTest):
ENDPOINT_NAME = "data_api_endpoints.get_participant_data_info"
ENDPOINT_NAME = "data_api_endpoints.get_participant_data_quantities"

@property
def ref_zero_row_output(self):
Expand Down Expand Up @@ -387,6 +387,7 @@ def test_three_participants_with_data(self):
}
)


class TestDownloadStudyInterventions(DataApiTest):
ENDPOINT_NAME = "data_api_endpoints.download_study_interventions"

Expand Down
134 changes: 109 additions & 25 deletions tests/test_mobile_endpoints.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# trunk-ignore-all(bandit/B106)
# trunk-ignore-all(ruff/B018)
import json
from datetime import date, datetime, timedelta
from unittest.mock import MagicMock, patch
Expand All @@ -24,7 +25,114 @@
from tests.common import ParticipantSessionTest


# trunk-ignore-all(ruff/B018)
## we have some meta tests to test side effects of events, stick them at the top

class TestAppVersionHistory(ParticipantSessionTest):
ENDPOINT_NAME = "mobile_endpoints.get_latest_surveys"

def test_app_version_code_update_triggers_app_version_code_history(self):
self.default_participant.update_only(last_version_code="1.0.0")
self.assertFalse(AppVersionHistory.objects.exists())
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, version_code="1.0.1")
self.default_participant.refresh_from_db()
self.assertEqual(self.default_participant.last_version_code, "1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)
# now test that the same event doesn't trigger a new history entry
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, version_code="1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)

def test_app_vaersion_name_update_triggers_app_version_name_history(self):
self.default_participant.update_only(last_version_name="1.0.0")
self.assertFalse(AppVersionHistory.objects.exists())
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, version_name="1.0.1")
self.default_participant.refresh_from_db()
self.assertEqual(self.default_participant.last_version_name, "1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)
# now test that the same event doesn't trigger a new history entry
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, version_name="1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)

def test_os_version_update_triggers_os_version_history(self):
self.default_participant.update_only(last_os_version="1.0.0")
self.assertFalse(AppVersionHistory.objects.exists())
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, os_version="1.0.1")
self.default_participant.refresh_from_db()
self.assertEqual(self.default_participant.last_os_version, "1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)
# now test that the same event doesn't trigger a new history entry
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, os_version="1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)

def test_version_code_and_name_update_triggers_both(self):
# AI generated test
self.default_participant.update_only(last_version_code="1.0.0", last_version_name="1.0.0")
self.assertFalse(AppVersionHistory.objects.exists())
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, version_code="1.0.1", version_name="1.0.1")
self.default_participant.refresh_from_db()
self.assertEqual(self.default_participant.last_version_code, "1.0.1")
self.assertEqual(self.default_participant.last_version_name, "1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)
# now test that the same event doesn't trigger a new history entry
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, version_code="1.0.1", version_name="1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)

def test_os_version_and_version_code_update_triggers_both(self):
# AI generated test
self.default_participant.update_only(last_os_version="1.0.0", last_version_code="1.0.0")
self.assertFalse(AppVersionHistory.objects.exists())
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, os_version="1.0.1", version_code="1.0.1")
self.default_participant.refresh_from_db()
self.assertEqual(self.default_participant.last_os_version, "1.0.1")
self.assertEqual(self.default_participant.last_version_code, "1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)
# now test that the same event doesn't trigger a new history entry
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, os_version="1.0.1", version_code="1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)

def test_os_version_and_version_name_update_triggers_both(self):
# AI generated test
self.default_participant.update_only(last_os_version="1.0.0", last_version_name="1.0.0")
self.assertFalse(AppVersionHistory.objects.exists())
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, os_version="1.0.1", version_name="1.0.1")
self.default_participant.refresh_from_db()
self.assertEqual(self.default_participant.last_os_version, "1.0.1")
self.assertEqual(self.default_participant.last_version_name, "1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)
# now test that the same event doesn't trigger a new history entry
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, os_version="1.0.1", version_name="1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)

def test_os_version_and_version_name_and_version_code_update_triggers_all(self):
# AI generated test
self.default_participant.update_only(
last_os_version="1.0.0", last_version_name="1.0.0", last_version_code="1.0.0"
)
self.assertFalse(AppVersionHistory.objects.exists())
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, os_version="1.0.1", version_name="1.0.1", version_code="1.0.1")
self.default_participant.refresh_from_db()
self.assertEqual(self.default_participant.last_os_version, "1.0.1")
self.assertEqual(self.default_participant.last_version_name, "1.0.1")
self.assertEqual(self.default_participant.last_version_code, "1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)
# now test that the same event doesn't trigger a new history entry
self.INJECT_DEVICE_TRACKER_PARAMS = False
self.smart_post_status_code(200, os_version="1.0.1", version_name="1.0.1", version_code="1.0.1")
self.assertEqual(AppVersionHistory.objects.count(), 1)



#
## mobile endpoints
Expand Down Expand Up @@ -82,30 +190,6 @@ def test_no_surveys(self):
resp = self.smart_post_status_code(200)
self.assertEqual(resp.content, b"[]")

def test_version_code_update_triggers_app_version_code_history(self):
self.default_participant.update_only(last_version_code="1.0.0")
self.assertFalse(AppVersionHistory.objects.exists())
self.smart_post_status_code(200, version_code="1.0.1")
self.default_participant.refresh_from_db()
self.assertEqual(self.default_participant.last_version_code, "1.0.1")
self.assertTrue(AppVersionHistory.objects.exists())

def test_version_code_update_triggers_app_version_name_history(self):
self.default_participant.update_only(last_version_name="1.0.0")
self.assertFalse(AppVersionHistory.objects.exists())
self.smart_post_status_code(200, version_name="1.0.1")
self.default_participant.refresh_from_db()
self.assertEqual(self.default_participant.last_version_name, "1.0.1")
self.assertTrue(AppVersionHistory.objects.exists())

def test_version_code_update_triggers_os_version_history(self):
self.default_participant.update_only(last_os_version="1.0.0")
self.assertFalse(AppVersionHistory.objects.exists())
self.smart_post_status_code(200, os_version="1.0.1")
self.default_participant.refresh_from_db()
self.assertEqual(self.default_participant.last_os_version, "1.0.1")
self.assertTrue(AppVersionHistory.objects.exists())

def test_basic_survey(self):
self.assertIsNone(self.default_participant.last_get_latest_surveys)
self.default_survey
Expand Down
2 changes: 1 addition & 1 deletion tests/test_study_endpoints.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def test_date_in_past(self):
self.session_study.refresh_from_db()
self.assertEqual(self.session_study.end_date, date(2020, 1, 1))

def test_has_valid_endpoint_name_and_is_placed_in_correct_file(self):
def test_date_in_far_future(self):
d = date.today()+timedelta(days=200)
self.set_session_study_relation(ResearcherRole.site_admin)
self.smart_post_redirect(self.session_study.id, end_date=d.isoformat())
Expand Down
2 changes: 1 addition & 1 deletion urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ def path(
path("get-studies/v1", data_api_endpoints.get_studies)
path("get-users/v1", data_api_endpoints.get_participant_ids_in_study) # deprecated June 2024
path("get-participant-ids/v1", data_api_endpoints.get_participant_ids_in_study)
path("get-participant-data-info/v1", data_api_endpoints.get_participant_data_info)
path("get-participant-data-quantities/v1", data_api_endpoints.get_participant_data_quantities)
path("get-interventions/v1", data_api_endpoints.download_study_interventions)
path("get-survey-history/v1", data_api_endpoints.download_study_survey_history)
path("get-participant-upload-history/v1", data_api_endpoints.get_participant_upload_history)
Expand Down

0 comments on commit a41fefa

Please sign in to comment.