aboutsummaryrefslogtreecommitdiff
path: root/tests/models/legacy
diff options
context:
space:
mode:
Diffstat (limited to 'tests/models/legacy')
-rw-r--r--tests/models/legacy/__init__.py0
-rw-r--r--tests/models/legacy/data.py265
-rw-r--r--tests/models/legacy/test_offerwall_parse_response.py186
-rw-r--r--tests/models/legacy/test_profiling_questions.py81
-rw-r--r--tests/models/legacy/test_user_question_answer_in.py304
5 files changed, 836 insertions, 0 deletions
diff --git a/tests/models/legacy/__init__.py b/tests/models/legacy/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/models/legacy/__init__.py
diff --git a/tests/models/legacy/data.py b/tests/models/legacy/data.py
new file mode 100644
index 0000000..20f3231
--- /dev/null
+++ b/tests/models/legacy/data.py
@@ -0,0 +1,265 @@
+# https://fsb.generalresearch.com/00ff1d9b71b94bf4b20d22cd56774120/offerwall/45b7228a7/?bpuid=379fb74f-05b2-42dc-b283
+# -47e1c8678b04&duration=1200&format=json&country_iso=us
+RESPONSE_45b7228a7 = (
+ '{"info": {"success": true}, "offerwall": {"availability_count": 9, "buckets": [{"category": [{"adwords_id": '
+ 'null, "adwords_label": null, "id": "c82cf98c578a43218334544ab376b00e", "label": "Social Research", "p": 1.0}], '
+ '"description": "", "duration": {"max": 719, "min": 72, "q1": 144, "q2": 621, "q3": 650}, '
+ '"id": "5503c471d95645dd947080704d3760b3", "name": "", "payout": {"max": 132, "min": 68, "q1": 113, "q2": 124, '
+ '"q3": 128}, "quality_score": 1.0, "uri": '
+ '"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=5503c471d95645dd947080704d3760b3&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=16fa868", "x": 0, "y": 0}, '
+ '{"category": [{"adwords_id": null, "adwords_label": null, "id": "c82cf98c578a43218334544ab376b00e", '
+ '"label": "Social Research", "p": 0.6666666666666666}, {"adwords_id": "5000", "adwords_label": "World '
+ 'Localities", "id": "cd3f9374ba5d4e5692ee5691320ecc8b", "label": "World Localities", "p": 0.16666666666666666}, '
+ '{"adwords_id": "14", "adwords_label": "People & Society", "id": "c8642a1b86d9460cbe8f7e8ae6e56ee4", '
+ '"label": "People & Society", "p": 0.16666666666666666}], "description": "", "duration": {"max": 1180, '
+ '"min": 144, "q1": 457, "q2": 621, "q3": 1103}, "id": "56f437aa5da443748872390a5cbf6103", "name": "", "payout": {'
+ '"max": 113, "min": 24, "q1": 40, "q2": 68, "q3": 68}, "quality_score": 0.17667012, '
+ '"uri": "https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=56f437aa5da443748872390a5cbf6103&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=16fa868", "x": 1, "y": 0}, '
+ '{"category": [{"adwords_id": null, "adwords_label": null, "id": "c82cf98c578a43218334544ab376b00e", '
+ '"label": "Social Research", "p": 0.6666666666666666}, {"adwords_id": "5000", "adwords_label": "World '
+ 'Localities", "id": "cd3f9374ba5d4e5692ee5691320ecc8b", "label": "World Localities", "p": 0.16666666666666666}, '
+ '{"adwords_id": "14", "adwords_label": "People & Society", "id": "c8642a1b86d9460cbe8f7e8ae6e56ee4", '
+ '"label": "People & Society", "p": 0.16666666666666666}], "description": "", "duration": {"max": 1180, '
+ '"min": 144, "q1": 457, "q2": 1103, "q3": 1128}, "id": "a5b8403e4a4a4ed1a21ef9b3a721ab02", "name": "", '
+ '"payout": {"max": 68, "min": 14, "q1": 24, "q2": 40, "q3": 68}, "quality_score": 0.01, '
+ '"uri": "https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=a5b8403e4a4a4ed1a21ef9b3a721ab02&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=16fa868", "x": 2, "y": 0}], '
+ '"id": "2fba2999baf0423cad0c49eceea4eb33", "payout_format": "${payout/100:.2f}"}}'
+)
+
+RESPONSE_b145b803 = (
+ '{"info":{"success":true},"offerwall":{"availability_count":10,"buckets":[{"category":[{"adwords_id":null,'
+ '"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social Research","p":1.0}],"contents":[{'
+ '"id":"b9d6fdb95ae2402dbb8e8673be382f04","id_code":"m:b9d6fdb95ae2402dbb8e8673be382f04","loi":954,"payout":166,'
+ '"source":"m"},{"id":"x94r9bg","id_code":"o:x94r9bg","loi":71,"payout":132,"source":"o"},{"id":"ejqjbv4",'
+ '"id_code":"o:ejqjbv4","loi":650,"payout":128,"source":"o"},{"id":"yxqdnb9","id_code":"o:yxqdnb9","loi":624,'
+ '"payout":113,"source":"o"},{"id":"vyjrv0v","id_code":"o:vyjrv0v","loi":719,"payout":124,"source":"o"}],'
+ '"currency":"USD","description":"","duration":{"max":954,"min":72,"q1":625,"q2":650,"q3":719},'
+ '"id":"c190231a7d494012a2f641a89a85e6a6","name":"","payout":{"max":166,"min":113,"q1":124,"q2":128,"q3":132},'
+ '"quality_score":1.0,"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120'
+ '/?i=c190231a7d494012a2f641a89a85e6a6&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=9dbbf8c","x":0,"y":0},'
+ '{"category":[{"adwords_id":null,"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social '
+ 'Research","p":0.5},{"adwords_id":"5000","adwords_label":"World Localities",'
+ '"id":"cd3f9374ba5d4e5692ee5691320ecc8b","label":"World Localities","p":0.25},{"adwords_id":"14",'
+ '"adwords_label":"People & Society","id":"c8642a1b86d9460cbe8f7e8ae6e56ee4","label":"People & Society",'
+ '"p":0.25}],"contents":[{"id":"ejqa3kw","id_code":"o:ejqa3kw","loi":143,"payout":68,"source":"o"},'
+ '{"id":"g6xkrbm","id_code":"o:g6xkrbm","loi":536,"payout":68,"source":"o"},{"id":"yr5od0g","id_code":"o:yr5od0g",'
+ '"loi":457,"payout":68,"source":"o"}],"currency":"USD","description":"","duration":{"max":537,"min":144,"q1":301,'
+ '"q2":457,"q3":497},"id":"ee12be565f744ef2b194703f3d32f8cd","name":"","payout":{"max":68,"min":68,"q1":68,'
+ '"q2":68,"q3":68},"quality_score":0.01,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=ee12be565f744ef2b194703f3d32f8cd&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=9dbbf8c","x":1,"y":0},'
+ '{"category":[{"adwords_id":null,"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social '
+ 'Research","p":1.0}],"contents":[{"id":"a019660a8bc0411dba19a3e5c5df5b6c",'
+ '"id_code":"m:a019660a8bc0411dba19a3e5c5df5b6c","loi":1180,"payout":24,"source":"m"},{"id":"ejqa3kw",'
+ '"id_code":"o:ejqa3kw","loi":143,"payout":68,"source":"o"},{"id":"8c54725047cc4e0590665a034d37e7f5",'
+ '"id_code":"m:8c54725047cc4e0590665a034d37e7f5","loi":1128,"payout":14,"source":"m"}],"currency":"USD",'
+ '"description":"","duration":{"max":1180,"min":144,"q1":636,"q2":1128,"q3":1154},'
+ '"id":"dc2551eb48d84b329fdcb5b2bd60ed71","name":"","payout":{"max":68,"min":14,"q1":19,"q2":24,"q3":46},'
+ '"quality_score":0.06662835156312356,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=dc2551eb48d84b329fdcb5b2bd60ed71&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=9dbbf8c","x":2,"y":0}],'
+ '"id":"7cb9eb4c1a5b41a38cfebd13c9c338cb"}}'
+)
+
+# This is a blocked user. Otherwise, the format is identical to RESPONSE_b145b803
+# https://fsb.generalresearch.com/00ff1d9b71b94bf4b20d22cd56774120/offerwall/1e5f0af8/?bpuid=00051c62-a872-4832-a008
+# -c37ec51d33d3&duration=1200&format=json&country_iso=us
+RESPONSE_d48cce47 = (
+ '{"info":{"success":true},"offerwall":{"availability_count":0,"buckets":[],'
+ '"id":"168680387a7f4c8c9cc8e7ab63f502ff","payout_format":"${payout/100:.2f}"}}'
+)
+
+# https://fsb.generalresearch.com/00ff1d9b71b94bf4b20d22cd56774120/offerwall/1e5f0af8/?bpuid=379fb74f-05b2-42dc-b283
+# -47e1c8678b04&duration=1200&format=json&country_iso=us
+RESPONSE_1e5f0af8 = (
+ '{"info":{"success":true},"offerwall":{"availability_count":9,"buckets":[{"category":[{"adwords_id":null,'
+ '"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social Research","p":1.0}],"contents":[{'
+ '"id":"x94r9bg","id_code":"o:x94r9bg","loi":71,"payout":132,"source":"o"},{"id":"yxqdnb9","id_code":"o:yxqdnb9",'
+ '"loi":604,"payout":113,"source":"o"},{"id":"ejqjbv4","id_code":"o:ejqjbv4","loi":473,"payout":128,"source":"o"},'
+ '{"id":"vyjrv0v","id_code":"o:vyjrv0v","loi":719,"payout":124,"source":"o"}],"currency":"USD","description":"",'
+ '"duration":{"max":719,"min":72,"q1":373,"q2":539,"q3":633},"id":"2a4a897a76464af2b85703b72a125da0",'
+ '"is_recontact":false,"name":"","payout":{"max":132,"min":113,"q1":121,"q2":126,"q3":129},"quality_score":1.0,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=2a4a897a76464af2b85703b72a125da0&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=82fe142","x":0,"y":0},'
+ '{"category":[{"adwords_id":null,"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social '
+ 'Research","p":0.5},{"adwords_id":"5000","adwords_label":"World Localities",'
+ '"id":"cd3f9374ba5d4e5692ee5691320ecc8b","label":"World Localities","p":0.25},{"adwords_id":"14",'
+ '"adwords_label":"People & Society","id":"c8642a1b86d9460cbe8f7e8ae6e56ee4","label":"People & Society",'
+ '"p":0.25}],"contents":[{"id":"775ed98f65604dac91b3a60814438829","id_code":"m:775ed98f65604dac91b3a60814438829",'
+ '"loi":1121,"payout":32,"source":"m"},{"id":"ejqa3kw","id_code":"o:ejqa3kw","loi":143,"payout":68,"source":"o"},'
+ '{"id":"yr5od0g","id_code":"o:yr5od0g","loi":457,"payout":68,"source":"o"}],"currency":"USD","description":"",'
+ '"duration":{"max":1121,"min":144,"q1":301,"q2":457,"q3":789},"id":"0aa83eb711c042e28bb9284e604398ac",'
+ '"is_recontact":false,"name":"","payout":{"max":68,"min":32,"q1":50,"q2":68,"q3":68},"quality_score":0.01,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=0aa83eb711c042e28bb9284e604398ac&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=82fe142","x":1,"y":0},'
+ '{"category":[{"adwords_id":null,"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social '
+ 'Research","p":1.0}],"contents":[{"id":"775ed98f65604dac91b3a60814438829",'
+ '"id_code":"m:775ed98f65604dac91b3a60814438829","loi":1121,"payout":32,"source":"m"},'
+ '{"id":"8c54725047cc4e0590665a034d37e7f5","id_code":"m:8c54725047cc4e0590665a034d37e7f5","loi":1128,"payout":14,'
+ '"source":"m"},{"id":"a019660a8bc0411dba19a3e5c5df5b6c","id_code":"m:a019660a8bc0411dba19a3e5c5df5b6c",'
+ '"loi":1180,"payout":24,"source":"m"}],"currency":"USD","description":"","duration":{"max":1180,"min":1121,'
+ '"q1":1125,"q2":1128,"q3":1154},"id":"d3a87b2bb6cf4428a55916bdf65e775e","is_recontact":false,"name":"",'
+ '"payout":{"max":32,"min":14,"q1":19,"q2":24,"q3":28},"quality_score":0.0825688889614345,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=d3a87b2bb6cf4428a55916bdf65e775e&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=82fe142","x":2,"y":0}],'
+ '"id":"391a54bbe84c4dbfa50b40841201a606"}}'
+)
+
+# https://fsb.generalresearch.com/00ff1d9b71b94bf4b20d22cd56774120/offerwall/5fl8bpv5/?bpuid=379fb74f-05b2-42dc-b283
+# -47e1c8678b04&duration=1200&format=json&country_iso=us
+RESPONSE_5fl8bpv5 = (
+ '{"info":{"success":true},"offerwall":{"availability_count":9,"buckets":[{'
+ '"id":"a1097b20f2ae472a9a9ad2987ba3bf95",'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=a1097b20f2ae472a9a9ad2987ba3bf95&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=e7baf5e"}],'
+ '"id":"fddaa544d7ff428a8ccccd0667fdc249","payout_format":"${payout/100:.2f}"}}'
+)
+
+# https://fsb.generalresearch.com/00ff1d9b71b94bf4b20d22cd56774120/offerwall/37d1da64/?bpuid=379fb74f-05b2-42dc-b283
+# -47e1c8678b04&duration=1200&format=json&country_iso=us
+RESPONSE_37d1da64 = (
+ '{"info":{"success":true},"offerwall":{"availability_count":18,"buckets":[{"category":[{"adwords_id":null,'
+ '"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social Research","p":1.0}],"contents":[{'
+ '"id":"0qvwx4z","id_code":"o:0qvwx4z","loi":700,"payout":106,"source":"o"},{"id":"x94r9bg","id_code":"o:x94r9bg",'
+ '"loi":72,"payout":132,"source":"o"},{"id":"yxqdnb9","id_code":"o:yxqdnb9","loi":605,"payout":113,"source":"o"},'
+ '{"id":"ejqjbv4","id_code":"o:ejqjbv4","loi":474,"payout":128,"source":"o"}],"eligibility":"conditional",'
+ '"id":"2cfc47e8d8c4417cb1f499dbf7e9afb8","loi":700,"missing_questions":["7ca8b59f4c864f80a1a7c7287adfc637"],'
+ '"payout":106,"uri":null},{"category":[{"adwords_id":null,"adwords_label":null,'
+ '"id":"c82cf98c578a43218334544ab376b00e","label":"Social Research","p":1.0}],"contents":[{"id":"yxqdnb9",'
+ '"id_code":"o:yxqdnb9","loi":605,"payout":113,"source":"o"},{"id":"x94r9bg","id_code":"o:x94r9bg","loi":72,'
+ '"payout":132,"source":"o"},{"id":"ejqjbv4","id_code":"o:ejqjbv4","loi":474,"payout":128,"source":"o"}],'
+ '"eligibility":"unconditional","id":"8964a87ebbe9433cae0ddce1b34a637a","loi":605,"payout":113,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=8964a87ebbe9433cae0ddce1b34a637a&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=ec8aa9a"},{"category":[{'
+ '"adwords_id":null,"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social Research",'
+ '"p":1.0}],"contents":[{"id":"a019660a8bc0411dba19a3e5c5df5b6c","id_code":"m:a019660a8bc0411dba19a3e5c5df5b6c",'
+ '"loi":1180,"payout":24,"source":"m"},{"id":"x94r9bg","id_code":"o:x94r9bg","loi":72,"payout":132,"source":"o"},'
+ '{"id":"yxqdnb9","id_code":"o:yxqdnb9","loi":605,"payout":113,"source":"o"},'
+ '{"id":"775ed98f65604dac91b3a60814438829","id_code":"m:775ed98f65604dac91b3a60814438829","loi":1121,"payout":32,'
+ '"source":"m"},{"id":"ejqa3kw","id_code":"o:ejqa3kw","loi":144,"payout":68,"source":"o"},{"id":"ejqjbv4",'
+ '"id_code":"o:ejqjbv4","loi":474,"payout":128,"source":"o"},{"id":"yr5od0g","id_code":"o:yr5od0g","loi":457,'
+ '"payout":68,"source":"o"},{"id":"vyjrv0v","id_code":"o:vyjrv0v","loi":719,"payout":124,"source":"o"}],'
+ '"eligibility":"unconditional","id":"ba98df6041344e818f02873534ae09a3","loi":1180,"payout":24,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=ba98df6041344e818f02873534ae09a3&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=ec8aa9a"},{"category":[{'
+ '"adwords_id":null,"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social Research",'
+ '"p":1.0}],"contents":[{"id":"775ed98f65604dac91b3a60814438829","id_code":"m:775ed98f65604dac91b3a60814438829",'
+ '"loi":1121,"payout":32,"source":"m"},{"id":"x94r9bg","id_code":"o:x94r9bg","loi":72,"payout":132,"source":"o"},'
+ '{"id":"yxqdnb9","id_code":"o:yxqdnb9","loi":605,"payout":113,"source":"o"},{"id":"ejqa3kw",'
+ '"id_code":"o:ejqa3kw","loi":144,"payout":68,"source":"o"},{"id":"ejqjbv4","id_code":"o:ejqjbv4","loi":474,'
+ '"payout":128,"source":"o"},{"id":"yr5od0g","id_code":"o:yr5od0g","loi":457,"payout":68,"source":"o"},'
+ '{"id":"vyjrv0v","id_code":"o:vyjrv0v","loi":719,"payout":124,"source":"o"}],"eligibility":"unconditional",'
+ '"id":"6456abe0f2584be8a30d9e0e93bef496","loi":1121,"payout":32,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=6456abe0f2584be8a30d9e0e93bef496&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=ec8aa9a"},{"category":[{'
+ '"adwords_id":null,"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social Research",'
+ '"p":1.0}],"contents":[{"id":"ejqa3kw","id_code":"o:ejqa3kw","loi":144,"payout":68,"source":"o"},{"id":"x94r9bg",'
+ '"id_code":"o:x94r9bg","loi":72,"payout":132,"source":"o"},{"id":"x94r9bg","id_code":"o:x94r9bg","loi":72,'
+ '"payout":132,"source":"o"}],"eligibility":"unconditional","id":"e0a0fb27bf174040be7971795a967ce5","loi":144,'
+ '"payout":68,"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=e0a0fb27bf174040be7971795a967ce5&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=ec8aa9a"},{"category":[{'
+ '"adwords_id":null,"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social Research",'
+ '"p":1.0}],"contents":[{"id":"ejqjbv4","id_code":"o:ejqjbv4","loi":474,"payout":128,"source":"o"},'
+ '{"id":"x94r9bg","id_code":"o:x94r9bg","loi":72,"payout":132,"source":"o"},{"id":"x94r9bg","id_code":"o:x94r9bg",'
+ '"loi":72,"payout":132,"source":"o"}],"eligibility":"unconditional","id":"99e3f8cfd367411b822cf11c3b54b558",'
+ '"loi":474,"payout":128,"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709'
+ "/00ff1d9b71b94bf4b20d22cd56774120/?i=99e3f8cfd367411b822cf11c3b54b558&b=379fb74f-05b2-42dc-b283-47e1c8678b04"
+ '&66482fb=ec8aa9a"},{"category":[{"adwords_id":null,"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e",'
+ '"label":"Social Research","p":1.0}],"contents":[{"id":"8c54725047cc4e0590665a034d37e7f5",'
+ '"id_code":"m:8c54725047cc4e0590665a034d37e7f5","loi":1128,"payout":14,"source":"m"},{"id":"x94r9bg",'
+ '"id_code":"o:x94r9bg","loi":72,"payout":132,"source":"o"},{"id":"yxqdnb9","id_code":"o:yxqdnb9","loi":605,'
+ '"payout":113,"source":"o"},{"id":"775ed98f65604dac91b3a60814438829",'
+ '"id_code":"m:775ed98f65604dac91b3a60814438829","loi":1121,"payout":32,"source":"m"},{"id":"ejqa3kw",'
+ '"id_code":"o:ejqa3kw","loi":144,"payout":68,"source":"o"},{"id":"ejqjbv4","id_code":"o:ejqjbv4","loi":474,'
+ '"payout":128,"source":"o"},{"id":"yr5od0g","id_code":"o:yr5od0g","loi":457,"payout":68,"source":"o"},'
+ '{"id":"vyjrv0v","id_code":"o:vyjrv0v","loi":719,"payout":124,"source":"o"}],"eligibility":"unconditional",'
+ '"id":"db7bc77afbb6443e8f35e9f06764fd06","loi":1128,"payout":14,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=db7bc77afbb6443e8f35e9f06764fd06&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=ec8aa9a"},{"category":[{'
+ '"adwords_id":null,"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social Research",'
+ '"p":0.5},{"adwords_id":"5000","adwords_label":"World Localities","id":"cd3f9374ba5d4e5692ee5691320ecc8b",'
+ '"label":"World Localities","p":0.25},{"adwords_id":"14","adwords_label":"People & Society",'
+ '"id":"c8642a1b86d9460cbe8f7e8ae6e56ee4","label":"People & Society","p":0.25}],"contents":[{"id":"yr5od0g",'
+ '"id_code":"o:yr5od0g","loi":457,"payout":68,"source":"o"},{"id":"x94r9bg","id_code":"o:x94r9bg","loi":72,'
+ '"payout":132,"source":"o"},{"id":"ejqa3kw","id_code":"o:ejqa3kw","loi":144,"payout":68,"source":"o"}],'
+ '"eligibility":"unconditional","id":"d5af3c782a354d6a8616e47531eb15e7","loi":457,"payout":68,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=d5af3c782a354d6a8616e47531eb15e7&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=ec8aa9a"},{"category":[{'
+ '"adwords_id":null,"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social Research",'
+ '"p":1.0}],"contents":[{"id":"vyjrv0v","id_code":"o:vyjrv0v","loi":719,"payout":124,"source":"o"},'
+ '{"id":"x94r9bg","id_code":"o:x94r9bg","loi":72,"payout":132,"source":"o"},{"id":"ejqjbv4","id_code":"o:ejqjbv4",'
+ '"loi":474,"payout":128,"source":"o"}],"eligibility":"unconditional","id":"ceafb146bb7042889b85630a44338a75",'
+ '"loi":719,"payout":124,"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709'
+ "/00ff1d9b71b94bf4b20d22cd56774120/?i=ceafb146bb7042889b85630a44338a75&b=379fb74f-05b2-42dc-b283-47e1c8678b04"
+ '&66482fb=ec8aa9a"}],"id":"37ed0858e0f64329812e2070fb658eb3","question_info":{'
+ '"7ca8b59f4c864f80a1a7c7287adfc637":{"choices":[{"choice_id":"0","choice_text":"Single, never married",'
+ '"order":0},{"choice_id":"1","choice_text":"Living with a Partner","order":1},{"choice_id":"2",'
+ '"choice_text":"Civil Union / Domestic Partnership","order":2},{"choice_id":"3","choice_text":"Married",'
+ '"order":3},{"choice_id":"4","choice_text":"Separated","order":4},{"choice_id":"5","choice_text":"Divorced",'
+ '"order":5},{"choice_id":"6","choice_text":"Widowed","order":6}],"country_iso":"us","language_iso":"eng",'
+ '"question_id":"7ca8b59f4c864f80a1a7c7287adfc637","question_text":"What is your relationship status?",'
+ '"question_type":"MC","selector":"SA"}}}}'
+)
+
+# https://fsb.generalresearch.com/00ff1d9b71b94bf4b20d22cd56774120/offerwall/5fa23085/?bpuid=379fb74f-05b2-42dc-b283
+# -47e1c8678b04&duration=1200&format=json&country_iso=us
+RESPONSE_5fa23085 = (
+ '{"info":{"success":true},"offerwall":{"availability_count":7,"buckets":[{"category":[{"adwords_id":null,'
+ '"adwords_label":null,"id":"c82cf98c578a43218334544ab376b00e","label":"Social Research","p":0.6666666666666666},'
+ '{"adwords_id":"5000","adwords_label":"World Localities","id":"cd3f9374ba5d4e5692ee5691320ecc8b","label":"World '
+ 'Localities","p":0.16666666666666666},{"adwords_id":"14","adwords_label":"People & Society",'
+ '"id":"c8642a1b86d9460cbe8f7e8ae6e56ee4","label":"People & Society","p":0.16666666666666666}],"contents":[{'
+ '"id":"x94r9bg","id_code":"o:x94r9bg","loi":72,"payout":132,"source":"o"},{"id":"yxqdnb9","id_code":"o:yxqdnb9",'
+ '"loi":605,"payout":113,"source":"o"},{"id":"ejqjbv4","id_code":"o:ejqjbv4","loi":640,"payout":128,"source":"o"},'
+ '{"id":"yr5od0g","id_code":"o:yr5od0g","loi":457,"payout":68,"source":"o"},{"id":"vyjrv0v","id_code":"o:vyjrv0v",'
+ '"loi":719,"payout":124,"source":"o"}],"duration":{"max":719,"min":72,"q1":457,"q2":605,"q3":640},'
+ '"id":"30049c28363b4f689c84fbacf1cc57e2","payout":{"max":132,"min":68,"q1":113,"q2":124,"q3":128},'
+ '"source":"pollfish","uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120'
+ '/?i=30049c28363b4f689c84fbacf1cc57e2&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=e3c1af1"}],'
+ '"id":"1c33756fd7ca49fa84ee48e42145e68c"}}'
+)
+
+# https://fsb.generalresearch.com/00ff1d9b71b94bf4b20d22cd56774120/offerwall/1705e4f8/?bpuid=379fb74f-05b2-42dc-b283
+# -47e1c8678b04&duration=1200&format=json&country_iso=us
+RESPONSE_1705e4f8 = (
+ '{"info":{"success":true},"offerwall":{"availability_count":7,"buckets":[{"currency":"USD","duration":719,'
+ '"id":"d06abd6ac75b453a93d0e85e4e391c00","min_payout":124,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=d06abd6ac75b453a93d0e85e4e391c00&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=e09baa2"},{"currency":"USD",'
+ '"duration":705,"id":"bcf5ca85a4044e9abed163f72039c7d1","min_payout":123,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=bcf5ca85a4044e9abed163f72039c7d1&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=e09baa2"},{"currency":"USD",'
+ '"duration":679,"id":"b85034bd59e04a64ac1be7686a4c906d","min_payout":121,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=b85034bd59e04a64ac1be7686a4c906d&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=e09baa2"},{"currency":"USD",'
+ '"duration":1180,"id":"324cf03b9ba24ef19284683bf9b62afb","min_payout":24,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=324cf03b9ba24ef19284683bf9b62afb&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=e09baa2"},{"currency":"USD",'
+ '"duration":1062,"id":"6bbbef3157c346009e677e556ecea7e7","min_payout":17,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=6bbbef3157c346009e677e556ecea7e7&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=e09baa2"},{"currency":"USD",'
+ '"duration":1128,"id":"bc12ba86b5024cf5b8ade997415190f7","min_payout":14,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=bc12ba86b5024cf5b8ade997415190f7&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=e09baa2"},{"currency":"USD",'
+ '"duration":1126,"id":"e574e715dcc744dbacf84a8426a0cd37","min_payout":10,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=e574e715dcc744dbacf84a8426a0cd37&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=e09baa2"},{"currency":"USD",'
+ '"duration":1302,"id":"a6f562e3371347d19d281d40b3ca317d","min_payout":10,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=a6f562e3371347d19d281d40b3ca317d&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=e09baa2"},{"currency":"USD",'
+ '"duration":953,"id":"462c8e1fbdad475792a360097c8e740f","min_payout":4,'
+ '"uri":"https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i'
+ '=462c8e1fbdad475792a360097c8e740f&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=e09baa2"}],'
+ '"id":"7f5158dc25174ada89f54e2b26a61b20"}}'
+)
+
+# Blocked user
+# https://fsb.generalresearch.com/00ff1d9b71b94bf4b20d22cd56774120/offerwall/0af0f7ec/?bpuid=00051c62-a872-4832-a008
+# -c37ec51d33d3&duration=1200&format=json&country_iso=us
+RESPONSE_0af0f7ec = (
+ '{"info":{"success":true},"offerwall":{"availability_count":0,"buckets":[],'
+ '"id":"34e71f4ccdec47b3b0991f5cfda60238","payout_format":"${payout/100:.2f}"}}'
+)
diff --git a/tests/models/legacy/test_offerwall_parse_response.py b/tests/models/legacy/test_offerwall_parse_response.py
new file mode 100644
index 0000000..7fb5315
--- /dev/null
+++ b/tests/models/legacy/test_offerwall_parse_response.py
@@ -0,0 +1,186 @@
+import json
+
+from generalresearch.models import Source
+from generalresearch.models.legacy.bucket import (
+ TopNPlusBucket,
+ SurveyEligibilityCriterion,
+ DurationSummary,
+ PayoutSummary,
+ BucketTask,
+)
+
+
+class TestOfferwallTopNAndStarwall:
+ def test_45b7228a7(self):
+ from generalresearch.models.legacy.offerwall import (
+ TopNOfferWall,
+ TopNOfferWallResponse,
+ StarwallOfferWallResponse,
+ )
+ from tests.models.legacy.data import (
+ RESPONSE_45b7228a7,
+ )
+
+ res = json.loads(RESPONSE_45b7228a7)
+ assert TopNOfferWallResponse.model_validate(res)
+ offerwall = TopNOfferWall.model_validate(res["offerwall"])
+ assert offerwall
+ offerwall.censor()
+ # Format is identical to starwall
+ assert StarwallOfferWallResponse.model_validate(res)
+
+ def test_b145b803(self):
+ from generalresearch.models.legacy.offerwall import (
+ TopNPlusOfferWallResponse,
+ StarwallPlusOfferWallResponse,
+ )
+ from tests.models.legacy.data import (
+ RESPONSE_b145b803,
+ )
+
+ res = json.loads(RESPONSE_b145b803)
+ assert TopNPlusOfferWallResponse.model_validate(res)
+ assert StarwallPlusOfferWallResponse.model_validate(res)
+
+ def test_d48cce47(self):
+ from generalresearch.models.legacy.offerwall import (
+ TopNPlusBlockOfferWallResponse,
+ StarwallPlusBlockOfferWallResponse,
+ )
+ from tests.models.legacy.data import (
+ RESPONSE_b145b803,
+ RESPONSE_d48cce47,
+ )
+
+ res = json.loads(RESPONSE_d48cce47) # this is a blocked user's response
+ assert TopNPlusBlockOfferWallResponse.model_validate(res)
+ assert StarwallPlusBlockOfferWallResponse.model_validate(res)
+ # otherwise it is identical to the plus's response
+ res = json.loads(RESPONSE_b145b803)
+ assert TopNPlusBlockOfferWallResponse.model_validate(res)
+ assert StarwallPlusBlockOfferWallResponse.model_validate(res)
+
+ def test_1e5f0af8(self):
+ from generalresearch.models.legacy.offerwall import (
+ TopNPlusBlockRecontactOfferWallResponse,
+ StarwallPlusBlockRecontactOfferWallResponse,
+ )
+ from tests.models.legacy.data import (
+ RESPONSE_d48cce47,
+ RESPONSE_1e5f0af8,
+ )
+
+ res = json.loads(RESPONSE_1e5f0af8)
+ assert TopNPlusBlockRecontactOfferWallResponse.model_validate(res)
+ assert StarwallPlusBlockRecontactOfferWallResponse.model_validate(res)
+
+ res = json.loads(RESPONSE_d48cce47) # this is a blocked user's response
+ assert TopNPlusBlockRecontactOfferWallResponse.model_validate(res)
+ assert StarwallPlusBlockRecontactOfferWallResponse.model_validate(res)
+
+ def test_eligibility_criteria(self):
+ b = TopNPlusBucket(
+ id="c82cf98c578a43218334544ab376b00e",
+ contents=[
+ BucketTask(
+ id="12345",
+ payout=10,
+ source=Source.TESTING,
+ id_code="t:12345",
+ loi=120,
+ )
+ ],
+ duration=DurationSummary(max=1, min=1, q1=1, q2=1, q3=1),
+ quality_score=1,
+ payout=PayoutSummary(max=1, min=1, q1=1, q2=1, q3=1),
+ uri="https://task.generalresearch.com/api/v1/52d3f63b2709/00ff1d9b71b94bf4b20d22cd56774120/?i=2a4a897a76464af2b85703b72a125da0&b=379fb74f-05b2-42dc-b283-47e1c8678b04&66482fb=82fe142",
+ eligibility_criteria=(
+ SurveyEligibilityCriterion(
+ question_id="71a367fb71b243dc89f0012e0ec91749",
+ question_text="what is something",
+ qualifying_answer=("1",),
+ qualifying_answer_label=("abc",),
+ property_code="t:123",
+ ),
+ SurveyEligibilityCriterion(
+ question_id="81a367fb71b243dc89f0012e0ec91749",
+ question_text="what is something 2",
+ qualifying_answer=("2",),
+ qualifying_answer_label=("ddd",),
+ property_code="t:124",
+ ),
+ ),
+ )
+ assert b.eligibility_criteria[0].rank == 0
+ assert b.eligibility_criteria[1].rank == 1
+ print(b.model_dump_json())
+ b.censor()
+ print(b.model_dump_json())
+
+
+class TestOfferwallSingle:
+ def test_5fl8bpv5(self):
+ from generalresearch.models.legacy.offerwall import (
+ SingleEntryOfferWallResponse,
+ )
+ from tests.models.legacy.data import (
+ RESPONSE_5fl8bpv5,
+ )
+
+ res = json.loads(RESPONSE_5fl8bpv5)
+ assert SingleEntryOfferWallResponse.model_validate(res)
+
+
+class TestOfferwallSoftPair:
+ def test_37d1da64(self):
+ from generalresearch.models.legacy.offerwall import (
+ SoftPairOfferwallResponse,
+ )
+ from tests.models.legacy.data import (
+ RESPONSE_37d1da64,
+ )
+
+ res = json.loads(RESPONSE_37d1da64)
+ assert SoftPairOfferwallResponse.model_validate(res)
+
+
+class TestMarketplace:
+ def test_5fa23085(self):
+ from generalresearch.models.legacy.offerwall import (
+ MarketplaceOfferwallResponse,
+ )
+
+ from tests.models.legacy.data import (
+ RESPONSE_5fa23085,
+ )
+
+ res = json.loads(RESPONSE_5fa23085)
+ assert MarketplaceOfferwallResponse.model_validate(res)
+
+
+class TestTimebucks:
+ def test_1705e4f8(self):
+ from generalresearch.models.legacy.offerwall import (
+ TimeBucksOfferwallResponse,
+ )
+ from tests.models.legacy.data import (
+ RESPONSE_1705e4f8,
+ )
+
+ res = json.loads(RESPONSE_1705e4f8)
+ assert TimeBucksOfferwallResponse.model_validate(res)
+
+ def test_0af0f7ec(self):
+ from generalresearch.models.legacy.offerwall import (
+ TimeBucksBlockOfferwallResponse,
+ )
+ from tests.models.legacy.data import (
+ RESPONSE_1705e4f8,
+ RESPONSE_0af0f7ec,
+ )
+
+ res = json.loads(RESPONSE_0af0f7ec)
+ assert TimeBucksBlockOfferwallResponse.model_validate(res)
+
+ res = json.loads(RESPONSE_1705e4f8)
+ assert TimeBucksBlockOfferwallResponse.model_validate(res)
diff --git a/tests/models/legacy/test_profiling_questions.py b/tests/models/legacy/test_profiling_questions.py
new file mode 100644
index 0000000..1afaa6b
--- /dev/null
+++ b/tests/models/legacy/test_profiling_questions.py
@@ -0,0 +1,81 @@
+class TestUpkQuestionResponse:
+
+ def test_init(self):
+ from generalresearch.models.legacy.questions import UpkQuestionResponse
+
+ s = (
+ '{"status": "success", "count": 7, "questions": [{"selector": "SL", "validation": {"patterns": [{'
+ '"message": "Must input a value between 13 and 120", "pattern": "^(1[01][0-9]|120|1[3-9]|[2-9]['
+ '0-9])$"}]}, "country_iso": "us", "question_id": "c5a4ef644c374f8994ecb3226b84263e", "language_iso": '
+ '"eng", "configuration": {"max_length": 3, "type": "TE"}, "question_text": "What is your age (in '
+ 'years)?", "question_type": "TE", "task_score": 20.28987136651298, "task_count": 21131, "p": 1.0}, '
+ '{"choices": [{"order": 0, "choice_id": "0", "choice_text": "Male"}, {"order": 1, "choice_id": "1", '
+ '"choice_text": "Female"}, {"order": 2, "choice_id": "2", "choice_text": "Other"}], "selector": "SA", '
+ '"country_iso": "us", "question_id": "5d6d9f3c03bb40bf9d0a24f306387d7c", "language_iso": "eng", '
+ '"question_text": "What is your gender?", "question_type": "MC", "task_score": 16.598347505339095, '
+ '"task_count": 4842, "p": 0.8180607558081178}, {"choices": [{"order": 0, "choice_id": "ara", '
+ '"choice_text": "Arabic"}, {"order": 1, "choice_id": "zho", "choice_text": "Chinese - Mandarin"}, '
+ '{"order": 2, "choice_id": "dut", "choice_text": "Dutch"}, {"order": 3, "choice_id": "eng", '
+ '"choice_text": "English"}, {"order": 4, "choice_id": "fre", "choice_text": "French"}, {"order": 5, '
+ '"choice_id": "ger", "choice_text": "German"}, {"order": 6, "choice_id": "hat", "choice_text": "Haitian '
+ 'Creole"}, {"order": 7, "choice_id": "hin", "choice_text": "Hindi"}, {"order": 8, "choice_id": "ind", '
+ '"choice_text": "Indonesian"}, {"order": 9, "choice_id": "ita", "choice_text": "Italian"}, {"order": 10, '
+ '"choice_id": "jpn", "choice_text": "Japanese"}, {"order": 11, "choice_id": "kor", "choice_text": '
+ '"Korean"}, {"order": 12, "choice_id": "may", "choice_text": "Malay"}, {"order": 13, "choice_id": "pol", '
+ '"choice_text": "Polish"}, {"order": 14, "choice_id": "por", "choice_text": "Portuguese"}, {"order": 15, '
+ '"choice_id": "pan", "choice_text": "Punjabi"}, {"order": 16, "choice_id": "rus", "choice_text": '
+ '"Russian"}, {"order": 17, "choice_id": "spa", "choice_text": "Spanish"}, {"order": 18, "choice_id": '
+ '"tgl", "choice_text": "Tagalog/Filipino"}, {"order": 19, "choice_id": "tur", "choice_text": "Turkish"}, '
+ '{"order": 20, "choice_id": "vie", "choice_text": "Vietnamese"}, {"order": 21, "choice_id": "zul", '
+ '"choice_text": "Zulu"}, {"order": 22, "choice_id": "xxx", "choice_text": "Other"}], "selector": "MA", '
+ '"country_iso": "us", "question_id": "f15663d012244d5fa43f5784f7bd1898", "language_iso": "eng", '
+ '"question_text": "Which language(s) do you speak fluently at home? (Select all that apply)", '
+ '"question_type": "MC", "task_score": 15.835933296975051, "task_count": 147, "p": 0.780484657143325}, '
+ '{"selector": "SL", "validation": {"patterns": [{"message": "Must enter a valid zip code: XXXXX", '
+ '"pattern": "^[0-9]{5}$"}]}, "country_iso": "us", "question_id": "543de254e9ca4d9faded4377edab82a9", '
+ '"language_iso": "eng", "configuration": {"max_length": 5, "min_length": 5, "type": "TE"}, '
+ '"question_text": "What is your zip code?", "question_type": "TE", "task_score": 3.9114103408096685, '
+ '"task_count": 4116, "p": 0.19277649769949645}, {"selector": "SL", "validation": {"patterns": [{'
+ '"message": "Must input digits only (range between 1 and 999999)", "pattern": "^[0-9]{1,6}$"}]}, '
+ '"country_iso": "us", "question_id": "9ffacedc92584215912062a9d75338fa", "language_iso": "eng", '
+ '"configuration": {"max_length": 6, "type": "TE"}, "question_text": "What is your current annual '
+ 'household income before taxes (in USD)?", "question_type": "TE", "task_score": 2.4630414657197686, '
+ '"task_count": 3267, "p": 0.12139266046727369}, {"choices": [{"order": 0, "choice_id": "0", '
+ '"choice_text": "Employed full-time"}, {"order": 1, "choice_id": "1", "choice_text": "Employed '
+ 'part-time"}, {"order": 2, "choice_id": "2", "choice_text": "Self-employed full-time"}, {"order": 3, '
+ '"choice_id": "3", "choice_text": "Self-employed part-time"}, {"order": 4, "choice_id": "4", '
+ '"choice_text": "Active military"}, {"order": 5, "choice_id": "5", "choice_text": "Inactive '
+ 'military/Veteran"}, {"order": 6, "choice_id": "6", "choice_text": "Temporarily unemployed"}, '
+ '{"order": 7, "choice_id": "7", "choice_text": "Full-time homemaker"}, {"order": 8, "choice_id": "8", '
+ '"choice_text": "Retired"}, {"order": 9, "choice_id": "9", "choice_text": "Student"}, {"order": 10, '
+ '"choice_id": "10", "choice_text": "Disabled"}], "selector": "SA", "country_iso": "us", "question_id": '
+ '"b546d26651f040c9a6900ffb126e7d69", "language_iso": "eng", "question_text": "What is your current '
+ 'employment status?", "question_type": "MC", "task_score": 1.6940674222375414, "task_count": 1134, '
+ '"p": 0.0834932559027201}, {"choices": [{"order": 0, "choice_id": "0", "choice_text": "No"}, '
+ '{"order": 1, "choice_id": "1", "choice_text": "Yes, Mexican"}, {"order": 2, "choice_id": "2", '
+ '"choice_text": "Yes, Puerto Rican"}, {"order": 3, "choice_id": "3", "choice_text": "Yes, Cuban"}, '
+ '{"order": 4, "choice_id": "4", "choice_text": "Yes, Salvadoran"}, {"order": 5, "choice_id": "5", '
+ '"choice_text": "Yes, Dominican"}, {"order": 6, "choice_id": "6", "choice_text": "Yes, Guatemalan"}, '
+ '{"order": 7, "choice_id": "7", "choice_text": "Yes, Colombian"}, {"order": 8, "choice_id": "8", '
+ '"choice_text": "Yes, Honduran"}, {"order": 9, "choice_id": "9", "choice_text": "Yes, Ecuadorian"}, '
+ '{"order": 10, "choice_id": "10", "choice_text": "Yes, Argentinian"}, {"order": 11, "choice_id": "11", '
+ '"choice_text": "Yes, Peruvian"}, {"order": 12, "choice_id": "12", "choice_text": "Yes, Nicaraguan"}, '
+ '{"order": 13, "choice_id": "13", "choice_text": "Yes, Spaniard"}, {"order": 14, "choice_id": "14", '
+ '"choice_text": "Yes, Venezuelan"}, {"order": 15, "choice_id": "15", "choice_text": "Yes, Panamanian"}, '
+ '{"order": 16, "choice_id": "16", "choice_text": "Yes, Other"}], "selector": "SA", "country_iso": "us", '
+ '"question_id": "7d452b8069c24a1aacbccbf767910345", "language_iso": "eng", "question_text": "Are you of '
+ 'Hispanic, Latino, or Spanish origin?", "question_type": "MC", "task_score": 1.6342156553005878, '
+ '"task_count": 2516, "p": 0.08054342118687588}], "special_questions": [{"selector": "HIDDEN", '
+ '"country_iso": "xx", "question_id": "1d1e2e8380ac474b87fb4e4c569b48df", "language_iso": "xxx", '
+ '"question_type": "HIDDEN"}, {"selector": "HIDDEN", "country_iso": "xx", "question_id": '
+ '"2fbedb2b9f7647b09ff5e52fa119cc5e", "language_iso": "xxx", "question_type": "HIDDEN"}, {"selector": '
+ '"HIDDEN", "country_iso": "xx", "question_id": "4030c52371b04e80b64e058d9c5b82e9", "language_iso": '
+ '"xxx", "question_type": "HIDDEN"}, {"selector": "HIDDEN", "country_iso": "xx", "question_id": '
+ '"a91cb1dea814480dba12d9b7b48696dd", "language_iso": "xxx", "question_type": "HIDDEN"}, {"selector": '
+ '"HIDDEN", "task_count": 40.0, "task_score": 0.45961204566189584, "country_iso": "us", "question_id": '
+ '"59f39a785f154752b6435c260cbce3c6", "language_iso": "eng", "question_text": "Core-Based Statistical '
+ 'Area (2020)", "question_type": "HIDDEN"}], "consent_questions": []}'
+ )
+ instance = UpkQuestionResponse.model_validate_json(s)
+
+ assert isinstance(instance, UpkQuestionResponse)
diff --git a/tests/models/legacy/test_user_question_answer_in.py b/tests/models/legacy/test_user_question_answer_in.py
new file mode 100644
index 0000000..253c46e
--- /dev/null
+++ b/tests/models/legacy/test_user_question_answer_in.py
@@ -0,0 +1,304 @@
+import json
+from decimal import Decimal
+from uuid import uuid4
+
+import pytest
+
+
+class TestUserQuestionAnswers:
+ """This is for the GRS POST submission that may contain multiple
+ Question+Answer(s) combinations for a single GRS Survey. It is
+ responsible for making sure the same question isn't submitted
+ more than once per submission, and other "list validation"
+ checks that aren't possible on an individual level.
+ """
+
+ def test_json_init(
+ self,
+ product_manager,
+ user_manager,
+ session_manager,
+ wall_manager,
+ user_factory,
+ product,
+ session_factory,
+ utc_hour_ago,
+ ):
+ from generalresearch.models.thl.session import Session, Wall
+ from generalresearch.models.thl.user import User
+ from generalresearch.models import Source
+ from generalresearch.models.legacy.questions import (
+ UserQuestionAnswers,
+ )
+
+ u: User = user_factory(product=product)
+
+ s1: Session = session_factory(
+ user=u,
+ wall_count=1,
+ started=utc_hour_ago,
+ wall_req_cpi=Decimal("0.00"),
+ wall_source=Source.GRS,
+ )
+ assert isinstance(s1, Session)
+ w1 = s1.wall_events[0]
+ assert isinstance(w1, Wall)
+
+ instance = UserQuestionAnswers.model_validate_json(
+ json.dumps(
+ {
+ "product_id": product.uuid,
+ "product_user_id": u.product_user_id,
+ "session_id": w1.uuid,
+ "answers": [
+ {"question_id": uuid4().hex, "answer": ["a", "b"]},
+ {"question_id": uuid4().hex, "answer": ["a", "b"]},
+ ],
+ }
+ )
+ )
+ assert isinstance(instance, UserQuestionAnswers)
+
+ def test_simple_validation_errors(
+ self, product_manager, user_manager, session_manager, wall_manager
+ ):
+ from generalresearch.models.legacy.questions import (
+ UserQuestionAnswers,
+ )
+
+ with pytest.raises(ValueError):
+ UserQuestionAnswers.model_validate(
+ {
+ "product_user_id": f"test-user-{uuid4().hex[:6]}",
+ "session_id": uuid4().hex,
+ "answers": [{"question_id": uuid4().hex, "answer": ["a", "b"]}],
+ }
+ )
+
+ with pytest.raises(ValueError):
+ UserQuestionAnswers.model_validate(
+ {
+ "product_id": uuid4().hex,
+ "session_id": uuid4().hex,
+ "answers": [{"question_id": uuid4().hex, "answer": ["a", "b"]}],
+ }
+ )
+
+ # user is validated only if a session_id is passed
+ UserQuestionAnswers.model_validate(
+ {
+ "product_id": uuid4().hex,
+ "product_user_id": f"test-user-{uuid4().hex[:6]}",
+ "answers": [{"question_id": uuid4().hex, "answer": ["a", "b"]}],
+ }
+ )
+
+ with pytest.raises(ValueError):
+ UserQuestionAnswers.model_validate(
+ {
+ "product_id": uuid4().hex,
+ "product_user_id": f"test-user-{uuid4().hex[:6]}",
+ "session_id": uuid4().hex,
+ }
+ )
+
+ with pytest.raises(ValueError):
+ UserQuestionAnswers.model_validate(
+ {
+ "product_id": uuid4().hex,
+ "product_user_id": f"test-user-{uuid4().hex[:6]}",
+ "session_id": uuid4().hex,
+ "answers": [],
+ }
+ )
+
+ with pytest.raises(ValueError):
+ answers = [
+ {"question_id": uuid4().hex, "answer": ["a"]} for i in range(101)
+ ]
+ UserQuestionAnswers.model_validate(
+ {
+ "product_id": uuid4().hex,
+ "product_user_id": f"test-user-{uuid4().hex[:6]}",
+ "session_id": uuid4().hex,
+ "answers": answers,
+ }
+ )
+
+ with pytest.raises(ValueError):
+ UserQuestionAnswers.model_validate(
+ {
+ "product_id": uuid4().hex,
+ "product_user_id": f"test-user-{uuid4().hex[:6]}",
+ "session_id": uuid4().hex,
+ "answers": "aaa",
+ }
+ )
+
+ def test_no_duplicate_questions(self):
+ # TODO: depending on if or how many of these types of errors actually
+ # occur, we could get fancy and just drop one of them. I don't
+ # think this is worth exploring yet unless we see if it's a problem.
+ from generalresearch.models.legacy.questions import (
+ UserQuestionAnswers,
+ )
+
+ consistent_qid = uuid4().hex
+ with pytest.raises(ValueError) as cm:
+ UserQuestionAnswers.model_validate(
+ {
+ "product_id": uuid4().hex,
+ "product_user_id": f"test-user-{uuid4().hex[:6]}",
+ "session_id": uuid4().hex,
+ "answers": [
+ {"question_id": consistent_qid, "answer": ["aaa"]},
+ {"question_id": consistent_qid, "answer": ["bbb"]},
+ ],
+ }
+ )
+
+ assert "Don't provide answers to duplicate questions" in str(cm.value)
+
+ def test_allow_answer_failures_silent(
+ self,
+ product_manager,
+ user_manager,
+ session_manager,
+ wall_manager,
+ product,
+ user_factory,
+ utc_hour_ago,
+ session_factory,
+ ):
+ """
+ There are many instances where suppliers may be submitting answers
+ manually, and they're just totally broken. We want to silently remove
+ that one QuestionAnswerIn without "loosing" any of the other
+ QuestionAnswerIn items that they provided.
+ """
+ from generalresearch.models.thl.session import Session, Wall
+ from generalresearch.models.thl.user import User
+ from generalresearch.models.legacy.questions import (
+ UserQuestionAnswers,
+ )
+
+ u: User = user_factory(product=product)
+
+ s1: Session = session_factory(user=u, wall_count=1, started=utc_hour_ago)
+ assert isinstance(s1, Session)
+ w1 = s1.wall_events[0]
+ assert isinstance(w1, Wall)
+
+ data = {
+ "product_id": product.uuid,
+ "product_user_id": u.product_user_id,
+ "session_id": w1.uuid,
+ "answers": [
+ {"question_id": uuid4().hex, "answer": ["aaa"]},
+ {"question_id": f"broken-{uuid4().hex[:6]}", "answer": ["bbb"]},
+ ],
+ }
+ # load via .model_validate()
+ instance = UserQuestionAnswers.model_validate(data)
+ assert isinstance(instance, UserQuestionAnswers)
+
+ # One of the QuestionAnswerIn items was invalid, so it was dropped
+ assert 1 == len(instance.answers)
+
+ # Confirm that this also works via model_validate_json
+ json_data = json.dumps(data)
+ instance = UserQuestionAnswers.model_validate_json(json_data)
+ assert isinstance(instance, UserQuestionAnswers)
+
+ # One of the QuestionAnswerIn items was invalid, so it was dropped
+ assert 1 == len(instance.answers)
+
+ assert instance.user is None
+ instance.prefetch_user(um=user_manager)
+ assert isinstance(instance.user, User)
+
+
+class TestUserQuestionAnswerIn:
+ """This is for the individual Question+Answer(s) that may come back from
+ a GRS POST.
+ """
+
+ def test_simple_validation_errors(self):
+ from generalresearch.models.legacy.questions import (
+ UserQuestionAnswerIn,
+ )
+
+ with pytest.raises(ValueError):
+ UserQuestionAnswerIn.model_validate(
+ {"question_id": f"test-{uuid4().hex[:6]}", "answer": ["123"]}
+ )
+
+ with pytest.raises(ValueError):
+ UserQuestionAnswerIn.model_validate({"answer": ["123"]})
+
+ with pytest.raises(ValueError):
+ UserQuestionAnswerIn.model_validate(
+ {"question_id": uuid4().hex, "answer": [123]}
+ )
+
+ with pytest.raises(ValueError):
+ UserQuestionAnswerIn.model_validate(
+ {"question_id": uuid4().hex, "answer": [""]}
+ )
+
+ with pytest.raises(ValueError):
+ UserQuestionAnswerIn.model_validate(
+ {"question_id": uuid4().hex, "answer": [" "]}
+ )
+
+ with pytest.raises(ValueError):
+ UserQuestionAnswerIn.model_validate(
+ {"question_id": uuid4().hex, "answer": ["a" * 5_001]}
+ )
+
+ with pytest.raises(ValueError):
+ UserQuestionAnswerIn.model_validate(
+ {"question_id": uuid4().hex, "answer": []}
+ )
+
+ def test_only_single_answers(self):
+ from generalresearch.models.legacy.questions import (
+ UserQuestionAnswerIn,
+ )
+
+ for qid in {
+ "2fbedb2b9f7647b09ff5e52fa119cc5e",
+ "4030c52371b04e80b64e058d9c5b82e9",
+ "a91cb1dea814480dba12d9b7b48696dd",
+ "1d1e2e8380ac474b87fb4e4c569b48df",
+ }:
+ # This is the UserAgent question which only allows a single answer
+ with pytest.raises(ValueError) as cm:
+ UserQuestionAnswerIn.model_validate(
+ {"question_id": qid, "answer": ["a", "b"]}
+ )
+
+ assert "Too many answer values provided" in str(cm.value)
+
+ def test_answer_item_limit(self):
+ from generalresearch.models.legacy.questions import (
+ UserQuestionAnswerIn,
+ )
+
+ answer = [uuid4().hex[:6] for i in range(11)]
+ with pytest.raises(ValueError) as cm:
+ UserQuestionAnswerIn.model_validate(
+ {"question_id": uuid4().hex, "answer": answer}
+ )
+ assert "List should have at most 10 items after validation" in str(cm.value)
+
+ def test_disallow_duplicate_answer_values(self):
+ from generalresearch.models.legacy.questions import (
+ UserQuestionAnswerIn,
+ )
+
+ answer = ["aaa" for i in range(5)]
+ with pytest.raises(ValueError) as cm:
+ UserQuestionAnswerIn.model_validate(
+ {"question_id": uuid4().hex, "answer": answer}
+ )