This repository has been archived by the owner on Feb 24, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathbackup.py
executable file
·158 lines (128 loc) · 4.21 KB
/
backup.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
#!/usr/bin/env python
"""Create backup of a Close.io organization and store it on AWS S3."""
import io
import json
import os
import tarfile
import tempfile
import time
import boto3
import requests
import slumber
from raven import Client
from requests.adapters import HTTPAdapter
from slumber.exceptions import HttpServerError, HttpClientError
AWS_S3_BUCKET = os.getenv('AWS_S3_BUCKET')
CLOSEIO_API_KEY = os.getenv('CLOSEIO_API_KEY')
SENTRY_DSN = os.getenv('SENTRY_DSN')
def get_closeio_api():
"""Return slumber API client for Close.io API."""
_session = requests.Session()
_session.auth = (CLOSEIO_API_KEY, "")
_session.verify = True
_session.mount('http://', HTTPAdapter(max_retries=5))
_session.mount('https://', HTTPAdapter(max_retries=5))
_api_cache = slumber.API(
'https://app.close.io/api/v1/',
session=_session
)
return _api_cache
def _data_iter(func, *args, **kwargs):
"""Handle Close.io API pagination."""
skip = 0
limit = 100
retries = 0
while True:
kwargs['_skip'] = skip
kwargs['_limit'] = limit
try:
response = func(*args, **kwargs)
except HttpServerError:
if retries < 10:
time.sleep(min(600, 2 ** retries))
retries += 1
continue
raise
except HttpClientError:
skip += limit
continue
else:
retries = 0
for item in response['data']:
yield item
if not response['has_more']:
break
else:
skip += limit
def backup(tarball: tarfile.TarFile, filename, fn):
"""Create JSON dump and add to tarball."""
with io.BytesIO() as output_file:
tar_info = tarfile.TarInfo(filename)
print(f"Adding {filename} to archive...", end=' ')
output_file.write(b"[\n")
for item, count in enumerate(_data_iter(fn)):
if count != 0:
output_file.write(b",")
output_file.write(
json.dumps(
item,
ensure_ascii=False,
).encode()
)
output_file.write(b"\n]\n")
output_file.seek(0)
tar_info.size = output_file.tell()
tarball.addfile(tar_info, output_file)
print("Done!")
def main():
"""Create backup and upload to AWS S3."""
tmp_dir = tempfile.mkdtemp()
filename = time.strftime('%Y-%m-%d.tar.gz')
archive_path = os.path.join(tmp_dir, filename)
api = get_closeio_api()
with tarfile.open(archive_path, "w:gz") as tarball:
backup(tarball, 'lead.json', api.lead.get)
backup(tarball, 'contact.json', api.contact.get)
backup(tarball, 'activity.json', api.activity.get)
backup(tarball, 'activity_note.json', api.activity.note.get)
backup(tarball, 'activity_email.json', api.activity.email.get)
backup(
tarball,
'activity_emailthread.json',
api.activity.emailthread.get
)
backup(
tarball,
'activity_statuschange_lead.json',
api.activity.status_change.lead.get,
)
backup(
tarball,
'activity_statuschange_opportunity.json',
api.activity.status_change.opportunity.get,
)
backup(
tarball,
'activity_call.json', api.activity.call.get)
backup(
tarball,
'opportunity.json', api.opportunity.get)
backup(tarball, 'task.json', api.task.get)
backup(tarball, 'status_lead.json', api.status.lead.get)
backup(tarball, 'status_opportunity.json', api.status.opportunity.get)
backup(tarball, 'email_template.json', api.email_template.get)
print("Uploading to AWS S3 bucket...", end=' ')
s3 = boto3.resource('s3')
with open(archive_path) as zf:
s3.Bucket(AWS_S3_BUCKET).put_object(Key=filename, Body=zf)
print("Done!")
print("Cleaning up filesystem...", end=' ')
os.remove(archive_path)
print("Done!")
if __name__ == '__main__':
sentry_client = Client(SENTRY_DSN)
try:
main()
except BaseException:
sentry_client.captureException()
raise