Skip to content

Commit ff2f317

Browse files
Merge pull request #61 from dynata/dev
templates and quota cells
2 parents eb589cf + 0ac59b1 commit ff2f317

27 files changed

+848
-17
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# IDE files
2+
.idea
3+
.vscode
4+
15
# Byte-compiled / optimized / DLL files
26
__pycache__/
37
*.py[cod]

CONTRIBUTING.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ This is an InnerSource python project. It is the work of someone who thought it
66

77
This repository is maintained by
88

9-
1. [Ridley Larsen](@RidleyLarsen)
10-
2. [Bradley Wogsland](@wogsland)
11-
3. [Nathan Workman](@nathanworkman)
9+
1. [Ridley Larsen](https://github.com/RidleyLarsen)
10+
1. [Uthayakumar Kumarasamy](https://github.com/ukumark)
11+
1. [Bradley Wogsland](https://github.com/wogsland)
12+
1. [Nathan Workman](https://github.com/nathanworkman)
1213

1314
### Community Guidelines
1415

README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Links to the Demand API documentation are included for each function.
6262
[Get Project Detailed Report](https://developers.dynata.com/demand-api-reference/core-resources/projects/get-project-detailed-report): get_project_detailed_report(project_id)
6363
[Get Pricing & Feasibility](https://developers.dynata.com/demand-api-reference/core-resources/pricing-feasibility/get-pricing-feasibility): get_feasibility(project_id)
6464
[Get Invoice PDF](https://developers.dynata.com/demand-api-reference/billing_invoicing/invoicing/get-invoices): get_invoice(project_id)
65+
[Get Invoices Summary PDF](https://developers.dynata.com): get_invoices_summary(\*\*kwargs)
6566

6667
### Line Item Functions
6768

@@ -72,7 +73,9 @@ Links to the Demand API documentation are included for each function.
7273
[Launch Line Item](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/post-lineitem-launch): launch_line_item(project_id, line_item_id)
7374
[Pause Line Item](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/post-lineitem-pause): pause_line_item(project_id, line_item_id)
7475
[Update Line Item](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/post-lineitem): update_line_item(project_id, line_item_id, line_item_data)
75-
[Get Line Item Detailed Report](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/get-detailed-line-item): get_line_item_detailed_report(project_id, line_item_id)
76+
[Get Line Item Detailed Report](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/get-detailed-line-item): get_line_item_detailed_report(project_id, line_item_id)
77+
[Launch Quota cell](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/post-quota-cell-launch): set_quotacell_status(project_id, line_item_id, quota_cell_id, launch)
78+
[Pause Quota cell](https://developers.dynata.com/demand-api-reference/core-resources/lineitems/post-quota-cell-pause): set_quotacell_status(project_id, line_item_id, quota_cell_id, pause)
7679

7780
### Misc Functions
7881

dynatademand/api.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,23 @@ def _api_get(self, uri, query_params=None):
7272
return response.content
7373
return response.json()
7474

75+
def _api_delete(self, uri):
76+
# Send an authenticated DELETE request to an API endpoint.
77+
self._check_authentication()
78+
url = '{}{}'.format(self.base_url, uri)
79+
request_headers = {
80+
'Authorization': 'Bearer {}'.format(self._access_token),
81+
'Content-Type': "application/json",
82+
}
83+
response = requests.delete(url=url, headers=request_headers)
84+
if response.status_code > 399:
85+
raise DemandAPIError('Demand API request to {} failed with status {}. Response: {}'.format(
86+
url, response.status_code, response.content
87+
))
88+
if response.headers['content-type'] == 'application/pdf':
89+
return response.content
90+
return response.json()
91+
7592
def authenticate(self):
7693
# Sends the authentication data to the access token endpoint.
7794
url = '{}/token/password'.format(self.auth_base_url)
@@ -158,6 +175,9 @@ def get_countries(self, **kwargs):
158175
)
159176
return self._api_get('/countries', kwargs)
160177

178+
def get_study_metadata(self):
179+
return self._api_get('/studyMetadata')
180+
161181
def get_event(self, event_id):
162182
self.validator.validate_request(
163183
'get_event',
@@ -352,6 +372,27 @@ def pause_line_item(self, project_id, line_item_id):
352372
)
353373
return response_data
354374

375+
def set_quotacell_status(self, project_id, line_item_id, quota_cell_id, action):
376+
# Stops traffic to a line item.
377+
self.validator.validate_request(
378+
'set_quotacell_status',
379+
path_data={
380+
'extProjectId': '{}'.format(project_id),
381+
'extLineItemId': '{}'.format(line_item_id),
382+
'quotaCellId': '{}'.format(quota_cell_id),
383+
'action': '{}'.format(action),
384+
},
385+
)
386+
response_data = self._api_post('/projects/{}/lineItems/{}/quotaCells/{}/{}'.format(
387+
project_id, line_item_id, quota_cell_id, action), {})
388+
if response_data.get('status').get('message') != 'success':
389+
raise DemandAPIError(
390+
"Could not {} quotacell. Demand API responded with: {}".format(
391+
action, response_data
392+
)
393+
)
394+
return response_data
395+
355396
def get_line_item(self, project_id, line_item_id):
356397
self.validator.validate_request(
357398
'get_line_item',
@@ -425,6 +466,13 @@ def get_sources(self):
425466
)
426467
return self._api_get('/sources')
427468

469+
def get_invoices_summary(self, **kwargs):
470+
self.validator.validate_request(
471+
'get_invoices_summary',
472+
query_params=kwargs
473+
)
474+
return self._api_get('/projects/invoices/summary', kwargs)
475+
428476
def reconcile_project(self, project_id, file, message):
429477
'''
430478
Sends a reconciliation request
@@ -454,3 +502,38 @@ def reconcile_project(self, project_id, file, message):
454502
url, response.status_code, response.content
455503
))
456504
return response.json()
505+
506+
def create_template(self, template):
507+
# TODO: Waiting on a valid path and request body schema.
508+
# self.validator.validate_request(
509+
# 'create_template',
510+
# request_body=template,
511+
# )
512+
return self._api_post('/templates/quotaplan', template)
513+
514+
def update_template(self, id, template):
515+
# TODO: Waiting on a valid path and request body schema.
516+
# self.validator.validate_request(
517+
# 'update_template',
518+
# path_data={'id': '{}'.format(id)},
519+
# request_body=template,
520+
# )
521+
return self._api_post('/templates/quotaplan/{}'.format(id), template)
522+
523+
def delete_template(self, id):
524+
self.validator.validate_request(
525+
'delete_template',
526+
path_data={'id': '{}'.format(id)},
527+
)
528+
return self._api_delete('/templates/quotaplan/{}'.format(id))
529+
530+
def get_templates(self, country, lang, **kwargs):
531+
self.validator.validate_request(
532+
'get_templates',
533+
path_data={
534+
'countryCode': '{}'.format(country),
535+
'languageCode': '{}'.format(lang)
536+
},
537+
query_params=kwargs,
538+
)
539+
return self._api_get('/templates/quotaplan/{}/{}'.format(country, lang), kwargs)

dynatademand/schemas/request/body/create_line_item.json

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,7 @@
4545
"deliveryType": {
4646
"type": "string",
4747
"default": "BALANCED",
48-
"description": "The plan on how responses will flow into the survey.",
49-
"enum": [
50-
"SLOW",
51-
"BALANCED",
52-
"FAST"
53-
]
48+
"description": "The plan on how responses will flow into the survey."
5449
},
5550
"sources": {
5651
"type": "array",

dynatademand/schemas/request/body/create_project.json

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,20 @@
5555
"type": "string",
5656
"minLength": 1
5757
}
58+
},
59+
"studyType": {
60+
"type": "array",
61+
"description": "List of possible types of survey.",
62+
"items": {
63+
"type": "string"
64+
}
65+
},
66+
"surveyRequirements": {
67+
"type": "array",
68+
"description": "List of possible requirements for a survey.",
69+
"items": {
70+
"type": "string"
71+
}
5872
}
5973
}
6074
},
@@ -118,12 +132,7 @@
118132
"deliveryType": {
119133
"type": "string",
120134
"default": "BALANCED",
121-
"description": "The plan on how responses will flow into the survey.",
122-
"enum": [
123-
"SLOW",
124-
"BALANCED",
125-
"FAST"
126-
]
135+
"description": "The plan on how responses will flow into the survey."
127136
},
128137
"sources": {
129138
"type": "array",
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
{
2+
"type": "object",
3+
"title": "New Template",
4+
"properties": {
5+
"name": {
6+
"type": "string",
7+
"description": "Name of the template"
8+
},
9+
"description": {
10+
"type": "string",
11+
"description": "template description"
12+
},
13+
"countryISOCode": {
14+
"type": "string",
15+
"description": "2 letter ISO Country Code",
16+
"minLength": 2,
17+
"maxLength": 2
18+
},
19+
"languageISOCode": {
20+
"type": "string",
21+
"description": "2 letter ISO Language Code",
22+
"minLength": 2,
23+
"maxLength": 2
24+
},
25+
"tags": {
26+
"type": "array",
27+
"description": "keyword tags"
28+
},
29+
"quotaPlan": {
30+
"type": "object",
31+
"title": "Quota Plan",
32+
"description": "Defines the type of respondents you want to invite for the survey",
33+
"properties": {
34+
"filters": {
35+
"type": "array",
36+
"description": "Filters are minimum set of targeting that every respondent must have in order to qualify for the study. Only attributes that have `isAllowedInFilters = true` is allowed to be used in `filters`",
37+
"items": {
38+
"type": "object",
39+
"properties": {
40+
"attributeId": {
41+
"type": "string",
42+
"description": "The attribute you want to target respondents on"
43+
},
44+
"options": {
45+
"type": "array",
46+
"description": "The options of the attribute you want to target respondents on",
47+
"uniqueItems": true,
48+
"items": {
49+
"type": "string"
50+
}
51+
},
52+
"operator": {
53+
"type": "string",
54+
"enum": [
55+
"exclude",
56+
"include"
57+
],
58+
"default": "include",
59+
"description": "The operator to use for the attribute options."
60+
}
61+
}
62+
}
63+
},
64+
"quotaGroups": {
65+
"type": "array",
66+
"description": "Quota groups define the allocated targeting attributes for panelists within this line item. Only attributes that have `isAllowedInQuotas = true` is allowed in `quotaGroups`.",
67+
"items": {
68+
"type": "object",
69+
"properties": {
70+
"name": {
71+
"type": "string",
72+
"description": "A quota group name of your choosing"
73+
},
74+
"quotaCells": {
75+
"type": "array",
76+
"description": "Quota Cells define the percentage allocation for the required targeting. A quota cell is made up of a collection of quota Nodes",
77+
"items": {
78+
"type": "object",
79+
"properties": {
80+
"quotaNodes": {
81+
"type": "array",
82+
"description": "Quota Nodes define the collection of attributes and options being targeted.",
83+
"items": {
84+
"type": "object",
85+
"properties": {
86+
"attributeId": {
87+
"type": "string",
88+
"description": "The attribute you want to target respondents on"
89+
},
90+
"options": {
91+
"type": "array",
92+
"description": "The options of the attribute you want to target respondents on",
93+
"uniqueItems": true,
94+
"items": {
95+
"type": "string"
96+
}
97+
},
98+
"operator": {
99+
"type": "string",
100+
"enum": [
101+
"exclude",
102+
"include"
103+
],
104+
"default": "include",
105+
"description": "**Deprecated field** The operator to use for the attribute options."
106+
}
107+
}
108+
}
109+
},
110+
"count": {
111+
"type": "integer",
112+
"description": "The count of respondents you want to qualify for the defined quota cell"
113+
}
114+
}
115+
}
116+
}
117+
}
118+
}
119+
}
120+
}
121+
}
122+
},
123+
"required": [
124+
"name",
125+
"countryISOCode",
126+
"languageISOCode",
127+
"description",
128+
"quotaPlan"
129+
]
130+
}
131+

dynatademand/schemas/request/body/update_project.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,18 @@
2525
"items": {
2626
"type": "string"
2727
}
28+
},
29+
"studyType": {
30+
"type": "array",
31+
"items": {
32+
"type": "string"
33+
}
34+
},
35+
"studyRequirements": {
36+
"type": "array",
37+
"items": {
38+
"type": "string"
39+
}
2840
}
2941
}
3042
},

0 commit comments

Comments
 (0)