1
|
|
|
""" |
2
|
|
|
Usage: |
3
|
|
|
rapidpro-pull --flow-runs --api-token=<api-token> [--address=<address>] |
4
|
|
|
[--before=<before> --after=<after>] |
5
|
|
|
[--with-contacts --with-flows] |
6
|
|
|
[--cache=<database-url>] |
7
|
|
|
rapidpro-pull --flows --api-token=<api-token> [--address=<address>] |
8
|
|
|
[--before=<before> --after=<after>] |
9
|
|
|
[--uuid=<uuid> ...] |
10
|
|
|
[--cache=<database-url>] |
11
|
|
|
rapidpro-pull --contacts --api-token=<api-token> [--address=<address>] |
12
|
|
|
[--before=<before> --after=<after>] |
13
|
|
|
[--uuid=<uuid> ...] |
14
|
|
|
[--cache=<database-url>] |
15
|
|
|
rapidpro-pull --help |
16
|
|
|
|
17
|
|
|
Options: |
18
|
|
|
--flow-runs download flow runs |
19
|
|
|
--flows download flows |
20
|
|
|
--contacts download contacts |
21
|
|
|
-a, --address=<address> a RapidPro server [default: rapidpro.io] |
22
|
|
|
-t, --api-token=<api-token> a RapidPro API token |
23
|
|
|
|
24
|
|
|
-h, --help display this help and exit |
25
|
|
|
|
26
|
|
|
--before=<before> download all older than ISO 8601 date/time |
27
|
|
|
--after=<after> download all newer than ISO 8601 date/time |
28
|
|
|
|
29
|
|
|
--uuid=<uuid> fetch objects matching UUID(s) (repeatable) |
30
|
|
|
|
31
|
|
|
--with-flows download associated flows, too |
32
|
|
|
--with-contacts download associated contacts, too |
33
|
|
|
|
34
|
|
|
--cache=<database-url> use database-url as cache (store retrieved |
35
|
|
|
objects in cache; retrieve objects from |
36
|
|
|
cache instead of downloading from RapidPro |
37
|
|
|
when possible) |
38
|
|
|
""" |
39
|
|
|
from __future__ import print_function |
40
|
|
|
import sys |
41
|
|
|
import json |
42
|
|
|
|
43
|
|
|
import temba_client.v1 |
44
|
|
|
import temba_client.exceptions |
45
|
|
|
import docopt |
46
|
|
|
|
47
|
|
|
import rapidpropull.download |
48
|
|
|
|
49
|
|
|
__author__ = 'Tomasz J. Kotarba <[email protected]>' |
50
|
|
|
__copyright__ = 'Copyright (c) 2016, Tomasz J. Kotarba. All rights reserved.' |
51
|
|
|
__maintainer__ = 'Tomasz J. Kotarba' |
52
|
|
|
__email__ = '[email protected]' |
53
|
|
|
__version__ = 'rapidpro-pull 1.0.0' |
54
|
|
|
|
55
|
|
|
|
56
|
|
|
class ArgumentProcessor(object): |
57
|
|
|
""" |
58
|
|
|
A problem domain specific processor of command-line arguments for the |
59
|
|
|
rapidpro-pull program. Sanitises arguments and can be queried to provide |
60
|
|
|
domain relevant information to clients (e.g. to instances of the |
61
|
|
|
DownloadTask class). |
62
|
|
|
It depends on a definition in a format compatible with docopt and specified |
63
|
|
|
in __doc__. |
64
|
|
|
""" |
65
|
|
|
ENDPOINT_SELECTORS = [ |
66
|
|
|
'--flow-runs', |
67
|
|
|
'--flows', |
68
|
|
|
'--contacts' |
69
|
|
|
] |
70
|
|
|
|
71
|
|
|
def __init__(self, argv=None): |
72
|
|
|
""" |
73
|
|
|
Initialise and interact with docopt to process arguments given in argv |
74
|
|
|
(or sys.argv if argv not provided). |
75
|
|
|
""" |
76
|
|
|
if argv is None: |
77
|
|
|
argv = sys.argv[1:] |
78
|
|
|
self.arguments = docopt.docopt(__doc__, argv=argv, version=__version__) |
79
|
|
|
|
80
|
|
|
def get_address(self): |
81
|
|
|
"""Return the address of a RapidPro service to be used.""" |
82
|
|
|
return self.arguments['--address'] |
83
|
|
|
|
84
|
|
|
def get_api_token(self): |
85
|
|
|
"""Return a RapidPro API token provided by the user.""" |
86
|
|
|
return self.arguments['--api-token'] |
87
|
|
|
|
88
|
|
|
def get_endpoint_selector(self): |
89
|
|
|
""" |
90
|
|
|
Return an endpoint selector (see: the RapidPro API). The endpoint |
91
|
|
|
selector determines which of the endpoints publicly exposed by the |
92
|
|
|
RapidPro web API will be used to pull data. |
93
|
|
|
""" |
94
|
|
|
filtered = filter(lambda s: self.arguments.get(s), |
95
|
|
|
self.ENDPOINT_SELECTORS) |
96
|
|
|
if filtered: |
97
|
|
|
return filtered[0] |
98
|
|
|
else: |
99
|
|
|
return None |
100
|
|
|
|
101
|
|
|
def get_endpoint_kwargs(self): |
102
|
|
|
""" |
103
|
|
|
Return a dictionary of optional arguments the user has provided to |
104
|
|
|
modify a request to the user-selected endpoint. |
105
|
|
|
""" |
106
|
|
|
kwargs = {} |
107
|
|
|
if self.arguments['--before'] is not None: |
108
|
|
|
kwargs['before'] = self.arguments['--before'] |
109
|
|
|
if self.arguments['--after'] is not None: |
110
|
|
|
kwargs['after'] = self.arguments['--after'] |
111
|
|
|
if self.arguments['--uuid']: |
112
|
|
|
kwargs['uuids'] = self.arguments['--uuid'] |
113
|
|
|
return kwargs |
114
|
|
|
|
115
|
|
|
def get_selectors_of_requested_associations(self): |
116
|
|
|
""" |
117
|
|
|
Return optional endpoint selector[s] to be used to query RapidPro for |
118
|
|
|
objects associated to the objects downloaded as a result of a request |
119
|
|
|
sent to the main endpoint selector. Only the endpoint selectors |
120
|
|
|
explicitly requested by the user will be returned. |
121
|
|
|
""" |
122
|
|
|
selectors = [] |
123
|
|
|
if self.arguments['--with-contacts']: |
124
|
|
|
selectors.append('--contacts') |
125
|
|
|
if self.arguments['--with-flows']: |
126
|
|
|
selectors.append('--flows') |
127
|
|
|
return tuple(selectors) |
128
|
|
|
|
129
|
|
|
def get_cache_url(self): |
130
|
|
|
"""Return a URL to a database to be used as cache (if provided).""" |
131
|
|
|
return self.arguments['--cache'] |
132
|
|
|
|
133
|
|
|
|
134
|
|
|
def main(argv=None): |
135
|
|
|
""" |
136
|
|
|
This is an entry point used to automatically generate the rapidpro-pull |
137
|
|
|
command. |
138
|
|
|
""" |
139
|
|
|
arguments = ArgumentProcessor(argv) |
140
|
|
|
downloader = rapidpropull.download.DownloadTask(arguments) |
141
|
|
|
try: |
142
|
|
|
downloader.download() |
143
|
|
|
except temba_client.exceptions.TembaConnectionError: |
144
|
|
|
print('Unable to connect to host', file=sys.stderr) |
145
|
|
|
sys.exit(1) |
146
|
|
|
except temba_client.exceptions.TembaTokenError: |
147
|
|
|
print('Authentication with provided token failed', file=sys.stderr) |
148
|
|
|
sys.exit(1) |
149
|
|
|
else: |
150
|
|
|
print(json.dumps(downloader.get_downloaded_json_structure())) |
151
|
|
|
|