-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Link table of contents within the PDF (#577)
* part of #554 * little more for #554 Beginnng of adding setup questions to the PDF * include setup questions as part of the release JSON #554 * switch branch to TOC only and leave PDF questions for later #576
- Loading branch information
Showing
9 changed files
with
413 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
""" | ||
Note: Will need some redoing when dataset_questions and epsilon_questions are "collapsed" into one variable | ||
- e.g. https://github.com/opendp/dpcreator/issues/440 | ||
Translate the depositor setup questions into JSON for use in the release | ||
Example output: | ||
- DepositorSetupInfo.dataset_questions | ||
- {"radio_best_describes": "notHarmButConfidential", | ||
"radio_only_one_individual_per_row": "yes", | ||
"radio_depend_on_private_information": "yes"} | ||
- DepositorSetupInfo.epsilon_questions | ||
- {"secret_sample": "yes", | ||
"population_size": "1000000", | ||
"observations_number_can_be_public": "yes"} | ||
""" | ||
from __future__ import annotations | ||
import json | ||
from django.core.serializers.json import DjangoJSONEncoder | ||
|
||
from opendp_apps.analysis.models import DepositorSetupInfo | ||
from opendp_apps.analysis import static_vals as astatic | ||
from opendp_apps.model_helpers.basic_err_check import BasicErrCheck | ||
|
||
class SetupQuestionFormatter(BasicErrCheck): | ||
"""Format the setup questions for use in a release""" | ||
|
||
def __init__(self, depositor_setup_info: DepositorSetupInfo): | ||
self.dsetup_info = depositor_setup_info | ||
self.formatted_questions = [] | ||
|
||
self.format_info() | ||
|
||
def format_info(self): | ||
if self.has_error(): | ||
return | ||
|
||
setup_questions = {} | ||
if self.dsetup_info.dataset_questions: | ||
setup_questions = dict(setup_questions, **self.dsetup_info.dataset_questions) | ||
|
||
if self.dsetup_info.epsilon_questions: | ||
setup_questions = dict(setup_questions, **self.dsetup_info.epsilon_questions) | ||
|
||
qnum = 0 | ||
for qattr in astatic.SETUP_QUESTION_LIST: | ||
qnum += 1 | ||
if qattr in setup_questions: | ||
val = setup_questions.get(qattr) | ||
else: | ||
val = '(not answered)' | ||
|
||
qinfo = astatic.SETUP_QUESTION_LOOKUP.get(qattr) | ||
if qinfo: | ||
qtext, qcontext = astatic.SETUP_QUESTION_LOOKUP.get(qattr) | ||
else: | ||
qtext = None | ||
qcontext = None | ||
|
||
info = dict(question_num=qnum, | ||
text=qtext, | ||
attribute=qattr, | ||
answer=val, | ||
context=qcontext | ||
) | ||
|
||
# Population size also given, add it to the info dict | ||
if qattr == astatic.SETUP_Q_02_ATTR: | ||
setup_answer = astatic.SETUP_Q_02_ANSWERS.get(val) | ||
if setup_answer and len(setup_answer) == 2: | ||
info['longAnswer'], info['privacy_params'] = setup_answer | ||
|
||
if qattr == astatic.SETUP_Q_04_ATTR and val == 'yes': | ||
info[astatic.SETUP_Q_04a_ATTR] = setup_questions.get(astatic.SETUP_Q_04a_ATTR) | ||
|
||
self.formatted_questions.append(info) | ||
|
||
def as_json(self): | ||
if self.has_error(): | ||
return None | ||
|
||
return json.dumps(self.formatted_questions, cls=DjangoJSONEncoder, indent=4) | ||
|
||
def as_dict(self): | ||
if self.has_error(): | ||
return None | ||
|
||
return self.formatted_questions | ||
|
||
""" | ||
docker-compose run server python manage.py shell | ||
from opendp_apps.analysis.models import DepositorSetupInfo | ||
from opendp_apps.analysis.setup_question_formatter import SetupQuestionFormatter | ||
d = DepositorSetupInfo.objects.first() | ||
setup = SetupQuestionFormatter(d) | ||
print(setup.as_json()) | ||
""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
53 changes: 53 additions & 0 deletions
53
server/opendp_apps/analysis/testing/test_setup_question_formatter.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
""" | ||
Test of epsilon addition and offsetting floating point anomaly | ||
""" | ||
from django.test import TestCase | ||
|
||
from opendp_apps.analysis import static_vals as astatic | ||
from opendp_apps.analysis.models import DepositorSetupInfo | ||
from opendp_apps.analysis.setup_question_formatter import SetupQuestionFormatter | ||
|
||
from opendp_apps.model_helpers.msg_util import msgt | ||
|
||
|
||
class TestSetupQuestionFormatter(TestCase): | ||
|
||
def setUp(self): | ||
|
||
self.params_01_qs_set1 = {"radio_best_describes": "notHarmButConfidential", | ||
"radio_only_one_individual_per_row": "yes", | ||
"radio_depend_on_private_information": "yes"} | ||
self.params_01_qs_set2 = {"secret_sample": "yes", | ||
"population_size": "1000000", | ||
"observations_number_can_be_public": "yes"} | ||
|
||
self.deposit_info1 = DepositorSetupInfo(**{'dataset_questions': self.params_01_qs_set1, | ||
'epsilon_questions': self.params_01_qs_set2}) | ||
|
||
self.params_02_qs_set1 = {"radio_best_describes": "notHarmButConfidential", | ||
"radio_only_one_individual_per_row": "yes", | ||
"radio_depend_on_private_information": "yes"} | ||
self.params_02_qs_set2 = {"secret_sample": "no", | ||
"observations_number_can_be_public": "yes"} | ||
|
||
self.deposit_info2 = DepositorSetupInfo(**{'dataset_questions': self.params_02_qs_set1, | ||
'epsilon_questions': self.params_02_qs_set2}) | ||
|
||
|
||
def test_10_good_format(self): | ||
"""Test that the formatter works correctly""" | ||
msgt(self.test_10_good_format.__doc__) | ||
|
||
util = SetupQuestionFormatter(self.deposit_info1) | ||
|
||
fmt_dict = util.as_dict() | ||
print(util.as_json()) | ||
|
||
self.assertEqual(len(fmt_dict), 5) | ||
|
||
self.assertEqual(fmt_dict[1]['attribute'], astatic.SETUP_Q_02_ATTR) | ||
|
||
self.assertEqual(fmt_dict[1]['privacy_params'], | ||
{"epsilon": 1, "delta": 5}) | ||
|
||
self.assertEqual(fmt_dict[3]['population_size'], "1000000") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.