Skip to content
This repository was archived by the owner on Nov 1, 2024. It is now read-only.

Commit d99924e

Browse files
authored
Merge pull request #60 from SafetyCulture/SKB-3314
SKB-3314 Update to use new reporting endpoints
2 parents 1c68cd5 + 589f880 commit d99924e

File tree

7 files changed

+97
-157
lines changed

7 files changed

+97
-157
lines changed

README.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,8 @@ Things you can configure:
207207
| Setting | Description |
208208
|---|---|
209209
| export_path | absolute or relative path to the directory where to save exported data to |
210-
| timezone | an Olson timezone to be used in generated audit reports. If invalid or missing, reports will use the timezone local to the computer running the export tool |
211210
| filename | an audit item ID whose response is going to be used to name the files of exported audit reports. Can only be an item with a response type of `text` from the header section of the audit such as Audit Title, Document No., Client / Site, Prepared By, Personnel, or any custom header item which has a 'text' type response (doesn't apply when exporting as CSV) |
212-
| export_profiles | to apply an export profile transformation to particular templates, give here a list of export profile ids
211+
| preferences | to apply a preference transformation to particular templates, give here a list of preference ids
213212
| sync_delay_in_seconds | time in seconds to wait after completing one export run, before running again
214213
| export_inactive_items | This setting only applies when exporting to CSV. Valid values are true (export all items) or false (do not export inactive items). Items that are nested under [Smart Field](https://support.safetyculture.com/templates/smart-fields/) will be 'inactive' if the smart field condition is not satisfied for these items.
215214
| media_sync_offset_in_seconds | time in seconds since an audit has been modified before it will by synced
@@ -221,11 +220,10 @@ API:
221220
token: YOUR_IAUDITOR_API_TOKEN
222221
export_options:
223222
export_path: /Users/Monty/Dropbox
224-
timezone: America/Chicago
225223
filename: f3245d40-ea77-11e1-aff1-0800200c9a66
226224
csv:
227225
export_inactive_items: false
228-
export_profiles:
226+
preferences:
229227
template_3E631E46F466411B9C09AD804886A8B4:E15A6525-EFA5-4835-92F0-D11CA9F364F3
230228
template_3E631E46F466411B9C09AD804886A8B4:E50645A1-2851-4E92-B4EA-60C5CE7981BE
231229
...
@@ -234,7 +232,7 @@ sync_delay_in_seconds: 36000
234232
media_sync_offset_in_seconds: 600
235233
```
236234

237-
Note: Templates for which there is no export profile id listed in the config file will be exported without a profile applied
235+
Note: Templates for which there is no preference id listed in the config file will be exported without a preference applied
238236

239237
### Naming the exported files
240238

@@ -268,15 +266,15 @@ export_options:
268266

269267
will result in all exported files named after the `Audit Title` field.
270268

271-
### How to list available export profile IDs
272-
To list all available export profile IDs and their associated templates:
269+
### How to list available preference IDs
270+
To list all available global preference IDs and their associated templates:
273271

274272
```
275-
iauditor_exporter --list_export_profiles
273+
iauditor_exporter --list_preferences
276274
```
277-
To list export profile IDs associated with specific templates:
275+
To list global and template specific preference IDs associated with specific templates:
278276
```
279-
iauditor_exporter --list_export_profiles template_3E631E46F466411B9C09AD804886A8B4
277+
iauditor_exporter --list_preferences template_3E631E46F466411B9C09AD804886A8B4
280278
```
281279

282280
Multiple template IDs can be passed, separated by a space

examples/config.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ API:
22
token: YOUR_API_ACCESS_TOKEN
33
export_options:
44
export_path:
5-
timezone:
65
filename:
76
csv:
87
export_inactive_items: false
9-
export_profiles:
8+
preferences:
109
sync_delay_in_seconds: 36000
1110
media_sync_offset_in_seconds: 600

safetypy/safetypy.py

Lines changed: 30 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# coding=utf-8
22
# Author: SafetyCulture
33
# Copyright: © SafetyCulture 2016
4+
# pylint: disable=E1101
45

56
import collections
67
import json
@@ -15,8 +16,7 @@
1516
import requests
1617
from getpass import getpass
1718

18-
DEFAULT_EXPORT_TIMEZONE = 'Etc/UTC'
19-
DEFAULT_EXPORT_FORMAT = 'pdf'
19+
DEFAULT_EXPORT_FORMAT = 'PDF'
2020
GUID_PATTERN = '[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}$'
2121
HTTP_USER_AGENT_ID = 'safetyculture-python-sdk'
2222

@@ -212,66 +212,44 @@ def discover_templates(self, modified_after=None, modified_before=None):
212212
self.log_http_status(response.status_code, log_message)
213213
return result
214214

215-
def get_export_profile_ids(self, template_id=None):
215+
def get_preference_ids(self, template_id=None):
216216
"""
217-
Query API for all export profile IDs if no parameters are passed, else restrict to template_id passed
218-
:param template_id: template_id to obtain export profiles for
219-
:return: JSON object containing template name: export profile pairs if no errors, or None
217+
Query API for all preference IDs if no parameters are passed, else restrict to template_id passed
218+
:param template_id: template_id to obtain export preferences for
219+
:return: JSON object containing list of preference objects
220220
"""
221-
profile_search_url = self.api_url + 'export_profiles/search'
221+
preference_search_url = self.api_url + 'preferences/search'
222222
if template_id is not None:
223-
profile_search_url += '?template=' + template_id
224-
response = self.authenticated_request_get(profile_search_url)
223+
preference_search_url += '?template_id=' + template_id
224+
response = self.authenticated_request_get(preference_search_url)
225225
result = response.json() if response.status_code == requests.codes.ok else None
226226
return result
227227

228-
def get_export_profile(self, export_profile_id):
229-
"""
230-
Query API for export profile corresponding to passed profile_id
231-
232-
:param export_profile_id: Export profile ID of the profile to retrieve
233-
:return: Export profile in JSON format
234-
"""
235-
profile_id_pattern = '^template_[a-fA-F0-9]{32}:' + GUID_PATTERN
236-
profile_id_is_valid = re.match(profile_id_pattern, export_profile_id)
237-
238-
if profile_id_is_valid:
239-
export_profile_url = self.api_url + '/export_profiles/' + export_profile_id
240-
response = self.authenticated_request_get(export_profile_url)
241-
result = self.parse_json(response.content) if response.status_code == requests.codes.ok else None
242-
log_message = 'on export profile retrieval of ' + export_profile_id
243-
244-
self.log_http_status(response.status_code, log_message)
245-
return result
246-
else:
247-
self.log_critical_error(ValueError,
248-
'export_profile_id {0} does not match expected pattern'.format(export_profile_id))
249-
return None
250-
251-
def get_export_job_id(self, audit_id, timezone=DEFAULT_EXPORT_TIMEZONE, export_profile_id=None,
252-
export_format=DEFAULT_EXPORT_FORMAT):
228+
def get_export_job_id(self, audit_id, preference_id=None, export_format=DEFAULT_EXPORT_FORMAT):
253229
"""
254230
Request export job ID from API and return it
255231
256232
:param audit_id: audit_id to retrieve export_job_id for
257-
:param timezone: timezone to apply to exports
258-
:param export_profile_id: export profile to apply to exports
233+
:param preference_id: preference to apply to exports
259234
:param export_format: desired format of exported document
260235
:return: export job ID obtained from API
261236
"""
262-
export_url = self.audit_url + audit_id + '/export?format=' + export_format + '&timezone=' + timezone
237+
export_url = self.audit_url + audit_id + '/report'
238+
if export_format == 'docx': # convert old command line format
239+
export_format = 'WORD'
240+
export_data = {'format': export_format.upper()}
263241

264-
if export_profile_id is not None:
265-
profile_id_pattern = '^template_[a-fA-F0-9]{32}:' + GUID_PATTERN
266-
profile_id_is_valid = re.match(profile_id_pattern, export_profile_id)
267-
if profile_id_is_valid:
268-
export_url += '&export_profile=' + export_profile_id
242+
if preference_id is not None:
243+
preference_id_pattern = '^template_[a-fA-F0-9]{32}:' + GUID_PATTERN
244+
preference_id_is_valid = re.match(preference_id_pattern, preference_id)
245+
if preference_id_is_valid:
246+
export_data['preference_id'] = preference_id.split(':')[1]
269247
else:
270248
self.log_critical_error(ValueError,
271-
'export_profile_id {0} does not match expected pattern'.format(
272-
export_profile_id))
249+
'preference_id {0} does not match expected pattern'.format(
250+
preference_id))
273251

274-
response = self.authenticated_request_post(export_url, data=None)
252+
response = self.authenticated_request_post(export_url, data=json.dumps(export_data))
275253
result = response.json() if response.status_code == requests.codes.ok else None
276254
log_message = 'on request to ' + export_url
277255

@@ -290,27 +268,27 @@ def poll_for_export(self, audit_id, export_job_id):
290268

291269
if job_id_is_valid:
292270
delay_in_seconds = 5
293-
poll_url = self.audit_url + audit_id + '/exports/' + export_job_id
271+
poll_url = self.audit_url + audit_id + '/report/' + export_job_id
294272
export_attempts = 1
295273
poll_status = self.authenticated_request_get(poll_url)
296274
status = poll_status.json()
297275
logger = logging.getLogger('sp_logger')
298276
if 'status' in status.keys():
299-
if status['status'] == 'IN PROGRESS':
277+
if status['status'] == 'IN_PROGRESS':
300278
logger.info(str(status['status']) + ' : ' + audit_id)
301279
time.sleep(delay_in_seconds)
302280
return self.poll_for_export(audit_id, export_job_id)
303281

304282
elif status['status'] == 'SUCCESS':
305283
logger.info(str(status['status']) + ' : ' + audit_id)
306-
return status['href']
284+
return status['url']
307285

308286
else:
309287
if export_attempts < 2:
310288
export_attempts += 1
311289
logger.info('attempt # {0} exporting report for: ' + audit_id.format(str(export_attempts)))
312290
retry_id = self.get_export_job_id(audit_id)
313-
return self.poll_for_export(audit_id, retry_id['id'])
291+
return self.poll_for_export(audit_id, retry_id['messageId'])
314292
else:
315293
logger.error('export for ' + audit_id + ' failed {0} times - skipping'.format(export_attempts))
316294
else:
@@ -338,18 +316,16 @@ def download_export(self, export_href):
338316
except Exception as ex:
339317
self.log_critical_error(ex, 'Exception occurred while attempting download_export({0})'.format(export_href))
340318

341-
def get_export(self, audit_id, timezone=DEFAULT_EXPORT_TIMEZONE, export_profile_id=None,
342-
export_format=DEFAULT_EXPORT_FORMAT):
319+
def get_export(self, audit_id, preference_id=None, export_format=DEFAULT_EXPORT_FORMAT):
343320
"""
344321
Obtain exported document from API and return string representation of it
345322
346323
:param audit_id: audit_id of export to obtain
347-
:param timezone: timezone to apply to exports
348-
:param export_profile_id: ID of export profile to apply to exports
324+
:param preference_id: ID of preference to apply to exports
349325
:param export_format: desired format of exported document
350326
:return: String representation of exported document
351327
"""
352-
export_job_id = self.get_export_job_id(audit_id, timezone, export_profile_id, export_format)['id']
328+
export_job_id = self.get_export_job_id(audit_id, preference_id, export_format)['messageId']
353329
export_href = self.poll_for_export(audit_id, export_job_id)
354330

355331
export_content = self.download_export(export_href)

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from setuptools import setup
22

33
setup(name = 'safetyculture-sdk-python',
4-
version = '3.4.1',
4+
version = '4.0.0',
55
description = 'iAuditor Python SDK and integration tools',
66
url = 'https://github.com/SafetyCulture/safetyculture-sdk-python',
77
author = 'SafetyCulture',

tools/exporter/config.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ API:
22
token: YOUR_IAUDITOR_API_TOKEN
33
export_options:
44
export_path:
5-
timezone:
65
filename:
76
csv_options:
87
export_inactive_items: false
9-
export_profiles:
8+
preferences:
109
sync_delay_in_seconds:
1110
media_sync_offset_in_seconds:

0 commit comments

Comments
 (0)