pingintel-api 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,162 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py,cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ #Pipfile.lock
96
+
97
+ # poetry
98
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
102
+ #poetry.lock
103
+
104
+ # pdm
105
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
106
+ #pdm.lock
107
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
108
+ # in version control.
109
+ # https://pdm.fming.dev/#use-with-ide
110
+ .pdm.toml
111
+
112
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
113
+ __pypackages__/
114
+
115
+ # Celery stuff
116
+ celerybeat-schedule
117
+ celerybeat.pid
118
+
119
+ # SageMath parsed files
120
+ *.sage.py
121
+
122
+ # Environments
123
+ .env
124
+ .venv
125
+ env/
126
+ venv/
127
+ ENV/
128
+ env.bak/
129
+ venv.bak/
130
+
131
+ # Spyder project settings
132
+ .spyderproject
133
+ .spyproject
134
+
135
+ # Rope project settings
136
+ .ropeproject
137
+
138
+ # mkdocs documentation
139
+ /site
140
+
141
+ # mypy
142
+ .mypy_cache/
143
+ .dmypy.json
144
+ dmypy.json
145
+
146
+ # Pyre type checker
147
+ .pyre/
148
+
149
+ # pytype static type analyzer
150
+ .pytype/
151
+
152
+ # Cython debug symbols
153
+ cython_debug/
154
+
155
+ # PyCharm
156
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
157
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
158
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
159
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
160
+ #.idea/
161
+
162
+ release.bat
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Ping Data Intelligence
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,25 @@
1
+ Metadata-Version: 2.3
2
+ Name: pingintel-api
3
+ Version: 0.1.0
4
+ Summary: Python-based API for Ping Data Intelligence APIs
5
+ Project-URL: Homepage, https://github.com/pingintel/pingintel-api
6
+ Project-URL: Issues, https://github.com/pingintel/pingintel-api
7
+ Author-email: Scott Stafford <scott@pingintel.com>
8
+ License-File: LICENSE
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Operating System :: OS Independent
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+
15
+ # pingintel-api
16
+ Python-based API for Ping Data Technology products.
17
+
18
+
19
+ Usage:
20
+
21
+ ```python
22
+ from pingintel_api import PingSOVFixerAPIClient
23
+
24
+ PingSOVFixerAPIClient(
25
+ ```
@@ -0,0 +1,11 @@
1
+ # pingintel-api
2
+ Python-based API for Ping Data Technology products.
3
+
4
+
5
+ Usage:
6
+
7
+ ```python
8
+ from pingintel_api import PingSOVFixerAPIClient
9
+
10
+ PingSOVFixerAPIClient(
11
+ ```
@@ -0,0 +1,5 @@
1
+ build:
2
+ py -m build
3
+
4
+ publish:
5
+ py -m twine upload dist/*
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "pingintel-api"
7
+ dynamic = ["version"]
8
+ authors = [
9
+ { name="Scott Stafford", email="scott@pingintel.com" },
10
+ ]
11
+ description = "Python-based API for Ping Data Intelligence APIs"
12
+ readme = "README.md"
13
+ requires-python = ">=3.8"
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ ]
19
+
20
+ [project.urls]
21
+ Homepage = "https://github.com/pingintel/pingintel-api"
22
+ Issues = "https://github.com/pingintel/pingintel-api"
23
+
24
+ [tool.hatch.version]
25
+ path = "src/pingintel_api/__about__.py"
26
+
27
+ [project.scripts]
28
+ sovfixerapi = "pingintel_api.sovfixerapi_cmd:main"
@@ -0,0 +1,2 @@
1
+ twine
2
+ requests
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,2 @@
1
+ from .sov_fixer_api_client import SOVFixerAPIClient
2
+ from .constants import SOV_RESULT_STATUS, SOV_STATUS
@@ -0,0 +1,30 @@
1
+ # Copyright 2021-2024 Ping Data Intelligence
2
+
3
+ import enum
4
+
5
+ DEFAULT_TIMEOUT_SECONDS = 5.0
6
+ WAIT_AN_EXTRA_FEW_MINUTES_FOR_AOA_TO_TIMEOUT_IF_I_HAVE_TO = 60.0 * 3
7
+
8
+
9
+ class SOV_STATUS(str, enum.Enum):
10
+ PENDING = "PENDING"
11
+ IN_PROGRESS = "IN_PROGRESS"
12
+ ENRICHING = "ENRICHING"
13
+ REENRICHING = "REENRICHING"
14
+ COMPLETE = "COMPLETE"
15
+ FAILED = "FAILED"
16
+
17
+
18
+ INCOMPLETE_STATUSES = [
19
+ SOV_STATUS.PENDING,
20
+ SOV_STATUS.IN_PROGRESS,
21
+ SOV_STATUS.ENRICHING,
22
+ SOV_STATUS.REENRICHING,
23
+ ]
24
+
25
+
26
+ class SOV_RESULT_STATUS(str, enum.Enum):
27
+ SUCCESS = "SUCCESS"
28
+ FAILED_TO_READ = "FAILED_TO_READ"
29
+ FAILED_TO_PARSE = "FAILED_TO_PARSE"
30
+ FAILED_TO_PROCESS = "FAILED_TO_PROCESS"
@@ -0,0 +1,356 @@
1
+ #!/usr/bin/env python
2
+
3
+ # Copyright 2021-2024 Ping Data Intelligence
4
+
5
+ import logging
6
+ import os
7
+ import pprint
8
+ import time
9
+ from timeit import default_timer as timer
10
+ from typing import overload, IO, TypedDict, NotRequired
11
+ import click
12
+ import requests
13
+ from requests.exceptions import HTTPError
14
+ from . import constants as c
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+
19
+ global start_time
20
+ start_time = None
21
+
22
+
23
+ def log(msg):
24
+ global start_time
25
+ if start_time is None:
26
+ start_time = timer()
27
+ elapsed = timer() - start_time
28
+ timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
29
+ click.echo(f"[{timestamp} T+{elapsed:.1f}s] {msg}")
30
+
31
+
32
+ def raise_for_status(response: requests.Response):
33
+ if response.ok:
34
+ return
35
+
36
+ error_msg = response.text
37
+ log(f"{response.status_code} {response.reason}: {error_msg}")
38
+
39
+ raise HTTPError(error_msg, response=response)
40
+
41
+
42
+ def is_fileobj(source):
43
+ return hasattr(source, "read")
44
+
45
+
46
+ class FixSOVResponseRequest(TypedDict):
47
+ status: c.SOV_STATUS
48
+ requested_at: str
49
+ progress_started_at: str
50
+ completed_at: str | None
51
+ last_health_check_time: str
52
+ last_health_status: str
53
+ pct_complete: int
54
+
55
+
56
+ class FixSOVResponseResultOutput(TypedDict):
57
+ url: str
58
+ description: str
59
+ filename: str
60
+
61
+
62
+ class FixSOVResponseResult(TypedDict):
63
+ message: str
64
+ status: c.SOV_RESULT_STATUS
65
+ outputs: list[FixSOVResponseResultOutput]
66
+
67
+
68
+ class FixSOVResponse(TypedDict):
69
+ request: FixSOVResponseRequest
70
+ result: NotRequired[FixSOVResponseResult]
71
+
72
+
73
+ class SOVFixerAPIClient:
74
+ SOV_STATUS = c.SOV_STATUS
75
+ SOV_RESULT_STATUS = c.SOV_RESULT_STATUS
76
+
77
+ @overload
78
+ def __init__(self, api_url: str, token=None) -> None: ...
79
+
80
+ @overload
81
+ def __init__(self, environment: str = "prod", token=None) -> None: ...
82
+
83
+ def __init__(
84
+ self,
85
+ api_url: str | None = None,
86
+ environment: str | None = "prod",
87
+ token=None,
88
+ ):
89
+ if api_url is None:
90
+ assert environment, "Need either api_url or environment."
91
+ if environment == "prod":
92
+ api_url = "https://api.sovfixer.com"
93
+ elif environment == "prod2":
94
+ api_url = "https://api2.sovfixer.com"
95
+ elif environment == "prodeu":
96
+ api_url = "https://api.eu.sovfixer.com"
97
+ elif environment == "local":
98
+ api_url = "http://api-local.sovfixer.com"
99
+ elif environment == "local2":
100
+ api_url = "http://localhost:8000"
101
+ else:
102
+ api_url = f"https://api-{environment}.sovfixer.com"
103
+
104
+ if token is None:
105
+ if environment in ["staging", "staging2"]:
106
+ serverspace = "stg"
107
+ elif environment in ["prod", "prod2"]:
108
+ serverspace = "prd"
109
+ elif environment in ["prodeu", "prodeu2"]:
110
+ serverspace = "prdeu"
111
+ elif environment in ["dev", "dev2"]:
112
+ serverspace = "dev"
113
+ elif environment in ["local", "local2"]:
114
+ serverspace = "local"
115
+ else:
116
+ raise ValueError("Unknown environment and missing token.")
117
+ token = os.environ.get(f"PING_{serverspace}_AUTH_TOKEN".upper())
118
+
119
+ if token is None:
120
+ token = os.environ.get("SOVFIXER_AUTH_TOKEN")
121
+ if token is None:
122
+ raise ValueError(
123
+ "Need --auth-token or SOVFIXER_AUTH_TOKEN environment variable set."
124
+ )
125
+ assert api_url
126
+ self.api_url = api_url
127
+ self.token = token
128
+ self.headers = {"Authorization": f"Token {token}"}
129
+ self.session = requests.Session()
130
+ self.session.headers = {
131
+ "Authorization": f"Token {self.token}",
132
+ "Accept-Encoding": "gzip",
133
+ }
134
+
135
+ def fix_sov_async_start(
136
+ self,
137
+ file: IO[bytes] | str,
138
+ document_type,
139
+ filename=None,
140
+ callback_url=None,
141
+ output_formats=None,
142
+ client_ref=None,
143
+ integrations=None,
144
+ delegate_to: str | None = None,
145
+ ):
146
+ url = self.api_url + "/api/v1/sov"
147
+
148
+ if is_fileobj(file):
149
+ if filename is None:
150
+ raise ValueError("Need filename if file is a file object.")
151
+
152
+ files = {"file": (filename, file)}
153
+ else:
154
+ if not os.path.exists(file):
155
+ raise click.ClickException(f"Path {file} does not exist.")
156
+
157
+ files = {"file": open(file, "rb")}
158
+
159
+ data = {}
160
+ if callback_url:
161
+ data["callback_url"] = callback_url
162
+ if document_type:
163
+ data["document_type"] = document_type
164
+ if output_formats:
165
+ data["output_formats"] = output_formats
166
+ if client_ref:
167
+ data["client_ref"] = client_ref
168
+ if integrations is not None:
169
+ data["integrations"] = integrations
170
+ if delegate_to is not None:
171
+ data["delegate_to"] = delegate_to
172
+
173
+ response = self.session.post(url, files=files, data=data)
174
+ if response.status_code == 200:
175
+ pprint.pprint(response.json())
176
+ else:
177
+ pprint.pprint(response.text)
178
+
179
+ raise_for_status(response)
180
+
181
+ response_data = response.json()
182
+ sov_id = response_data["id"]
183
+ message = response_data["message"]
184
+ status_url = self.api_url + f"/api/v1/sov/{sov_id}?include_progress=True"
185
+ log(
186
+ f"+ Dispatched {sov_id}: {message}. Now, polling for results at {status_url}."
187
+ )
188
+ return response_data
189
+
190
+ def fix_sov_async_check_progress(self, sovid_or_start_ret) -> FixSOVResponse:
191
+ if isinstance(sovid_or_start_ret, dict):
192
+ sov_id = sovid_or_start_ret["id"]
193
+ else:
194
+ sov_id = sovid_or_start_ret
195
+
196
+ status_url = self.api_url + f"/api/v1/sov/{sov_id}?include_progress=True"
197
+ # params = {"id": sov_id}
198
+
199
+ response = self.session.get(status_url)
200
+ # pprint.pprint(response.json())
201
+ raise_for_status(response)
202
+
203
+ response_data: FixSOVResponse = response.json()
204
+ # request_status = response_data["request"]["status"]
205
+ return response_data
206
+
207
+ def fix_sov_download(
208
+ self, output_ret, actually_write=False, output_path=None, environment=None
209
+ ):
210
+ return download_output(
211
+ output_ret,
212
+ session=self.session,
213
+ actually_write=actually_write,
214
+ output_path=output_path,
215
+ environment=environment,
216
+ )
217
+
218
+
219
+ def download_output(
220
+ output,
221
+ environment=None,
222
+ session=None,
223
+ auth_token=None,
224
+ actually_write=False,
225
+ output_path=None,
226
+ ):
227
+ if session is None:
228
+ session = requests.Session()
229
+ session.headers = {
230
+ "Authorization": f"Token {auth_token}",
231
+ "Accept-Encoding": "gzip",
232
+ }
233
+
234
+ output_url = output["url"]
235
+ if (
236
+ environment
237
+ and environment == "local2"
238
+ and "api-local.sovfixer.com" in output_url
239
+ ):
240
+ output_url = output_url.replace("api-local.sovfixer.com", "localhost:8000")
241
+
242
+ output_description = output["description"]
243
+ output_filename = output["filename"]
244
+ if output_path is None:
245
+ output_path = output_filename
246
+
247
+ log(f"Requesting output from {output_url}...")
248
+ with session.get(output_url, stream=True) as response:
249
+ raise_for_status(response)
250
+ filesize_mb = int(response.headers.get("content-length", 0)) / 1024 / 1024
251
+ pprint.pprint(dict(response.headers))
252
+ log(f" - Streaming {output_description} output ({filesize_mb:.2f} MB)...")
253
+
254
+ if actually_write:
255
+ with open(output_path, "wb") as fd:
256
+ for chunk in response.iter_content(chunk_size=1024 * 1024):
257
+ fd.write(chunk)
258
+ log(f" - Downloaded {output_description} output: {output_path}.")
259
+ return output_path if actually_write else None
260
+
261
+
262
+ def secure_filename(s):
263
+ return s
264
+
265
+
266
+ def fix_sov(
267
+ API_URL,
268
+ filename,
269
+ document_type,
270
+ auth_token,
271
+ environment=None,
272
+ callback_url=None,
273
+ actually_write=False,
274
+ output_formats=None,
275
+ session=None,
276
+ client_ref=None,
277
+ ):
278
+ if auth_token is None:
279
+ auth_token = os.environ.get("SOVFIXER_AUTH_TOKEN")
280
+ if auth_token is None:
281
+ raise click.ClickException(
282
+ "Need --auth-token or SOVFIXER_AUTH_TOKEN environment variable set."
283
+ )
284
+
285
+ if not os.path.exists(filename):
286
+ raise click.ClickException("Path does not exist.")
287
+
288
+ sov_fixer_client = SOVFixerAPIClient(API_URL, auth_token)
289
+ start_response = sov_fixer_client.fix_sov_async_start(
290
+ filename,
291
+ document_type=document_type,
292
+ callback_url=callback_url,
293
+ output_formats=output_formats,
294
+ client_ref=client_ref,
295
+ )
296
+
297
+ while 1:
298
+ response_data = sov_fixer_client.fix_sov_async_check_progress(start_response)
299
+ # raise_for_status(response_data)
300
+ # pprint.pprint(response_data)
301
+
302
+ request_status = response_data["request"]["status"]
303
+
304
+ POLL_SECS = 2.5
305
+ if request_status == "PENDING":
306
+ log(
307
+ f" - Has not yet been queued for processing, checking progress in {POLL_SECS}s."
308
+ )
309
+ time.sleep(POLL_SECS)
310
+ elif request_status == "IN_PROGRESS":
311
+ log(f" - Still in progress, checking progress in {POLL_SECS}s.")
312
+ time.sleep(POLL_SECS)
313
+ else:
314
+ break
315
+
316
+ result_status = response_data["result"]["status"]
317
+ result_message = response_data["result"]["message"]
318
+ log(f"+ Finished with result {result_status}: {result_message}")
319
+
320
+ if result_status == "SUCCESS":
321
+ log("Complete! Fetching outputs.")
322
+ for output in response_data["result"]["outputs"]:
323
+ output_url = output["url"]
324
+ if (
325
+ environment
326
+ and environment == "local2"
327
+ and "api-local.sovfixer.com" in output_url
328
+ ):
329
+ output_url = output_url.replace(
330
+ "api-local.sovfixer.com", "localhost:8000"
331
+ )
332
+
333
+ output_filename = output["filename"]
334
+
335
+ output_path = output_filename
336
+
337
+ if actually_write:
338
+ if os.path.exists(output_path):
339
+ yesno = input(
340
+ f"Do you want to overwrite the existing file {output_path} [y/N]? "
341
+ )
342
+ if yesno.lower() != "y":
343
+ continue
344
+
345
+ response = sov_fixer_client.fix_sov_download(
346
+ output,
347
+ actually_write=actually_write,
348
+ output_path=output_path,
349
+ environment=environment,
350
+ )
351
+ raise_for_status(response)
352
+ return True
353
+ else:
354
+ log("* Parsing failed! Raw API output:")
355
+ log(response_data)
356
+ return False
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env python
2
+
3
+ # Copyright 2021-2024 Ping Data Intelligence
4
+
5
+ import base64
6
+ import enum
7
+ import gzip
8
+ import hashlib
9
+ import io
10
+ import json
11
+ import logging
12
+ import os
13
+ import pathlib
14
+ import pprint
15
+ import random
16
+ import time
17
+ import zipfile
18
+ from timeit import default_timer as timer
19
+
20
+ import click
21
+ import requests
22
+ from requests.exceptions import HTTPError
23
+
24
+ from pingintel_api.sov_fixer_api_client import fix_sov
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ global start_time
30
+ start_time = None
31
+
32
+
33
+ """
34
+ sovfixerapi.py
35
+
36
+ Example Python commandline script for using the Ping Data Technologies sovfixer API to process SOVs.
37
+ """
38
+
39
+
40
+ def log(msg):
41
+ global start_time
42
+ if start_time is None:
43
+ start_time = timer()
44
+ elapsed = timer() - start_time
45
+ timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
46
+ click.echo(f"[{timestamp} T+{elapsed:.1f}s] {msg}")
47
+
48
+
49
+ @click.command()
50
+ @click.argument(
51
+ "filename", nargs=-1, required=True, type=click.Path(exists=True, dir_okay=False)
52
+ )
53
+ @click.option(
54
+ "-e",
55
+ "--environment",
56
+ type=click.Choice(
57
+ [
58
+ "staging",
59
+ "staging2",
60
+ "prod",
61
+ "prod2",
62
+ "prodeu",
63
+ "prodeu2",
64
+ "local",
65
+ "local2",
66
+ "dev",
67
+ "dev2",
68
+ ],
69
+ case_sensitive=False,
70
+ ),
71
+ default="staging",
72
+ )
73
+ @click.option(
74
+ "-d",
75
+ "--document-type",
76
+ type=click.Choice(
77
+ ["SOV", "PREM_BDX", "CLAIM_BDX", "SOV_BDX", "ACORD"], case_sensitive=False
78
+ ),
79
+ default="SOV",
80
+ help="Identify `filename` document type. Defaults to SOV.",
81
+ )
82
+ @click.option(
83
+ "--auth-token",
84
+ help="Provide auth token via --auth-token or SOVFIXER_AUTH_TOKEN environment variable.",
85
+ )
86
+ @click.option(
87
+ "--callback-url", help="(Optional) Provide a URL to which results should be POSTed."
88
+ )
89
+ @click.option(
90
+ "-o",
91
+ "--output-format",
92
+ multiple=True,
93
+ help="Select output format.",
94
+ )
95
+ @click.option("--client-ref")
96
+ @click.option(
97
+ "--write",
98
+ "--no-write",
99
+ is_flag=True,
100
+ default=False,
101
+ help="If set, actually write the output. Otherwise, download as a test but do not write.",
102
+ )
103
+ def main(
104
+ filename,
105
+ environment,
106
+ document_type,
107
+ auth_token,
108
+ callback_url,
109
+ output_format,
110
+ client_ref,
111
+ write,
112
+ ):
113
+ if environment == "prod":
114
+ API_URL = "https://api.sovfixer.com"
115
+ elif environment == "prod2":
116
+ API_URL = "https://api2.sovfixer.com"
117
+ elif environment == "prodeu":
118
+ API_URL = "https://api.eu.sovfixer.com"
119
+ elif environment == "local":
120
+ API_URL = "http://api-local.sovfixer.com"
121
+ elif environment == "local2":
122
+ API_URL = "http://localhost:8000"
123
+ else:
124
+ API_URL = f"https://api-{environment}.sovfixer.com"
125
+
126
+ if auth_token is None:
127
+ if environment in ["staging", "staging2"]:
128
+ serverspace = "stg"
129
+ elif environment in ["prod", "prod2"]:
130
+ serverspace = "prd"
131
+ elif environment in ["prodeu", "prodeu2"]:
132
+ serverspace = "prdeu"
133
+ elif environment in ["dev", "dev2"]:
134
+ serverspace = "dev"
135
+ elif environment in ["local", "local2"]:
136
+ serverspace = "local"
137
+ else:
138
+ raise NotImplementedError()
139
+ auth_token = os.environ.get(f"PING_{serverspace}_AUTH_TOKEN".upper())
140
+
141
+ if isinstance(filename, pathlib.PosixPath):
142
+ filename = [str(filename)]
143
+
144
+ for fn in filename:
145
+ fix_sov(
146
+ API_URL,
147
+ fn,
148
+ document_type,
149
+ auth_token,
150
+ environment,
151
+ callback_url,
152
+ actually_write=write,
153
+ output_formats=output_format,
154
+ client_ref=client_ref,
155
+ )
156
+
157
+
158
+ if __name__ == "__main__":
159
+ main()
File without changes
@@ -0,0 +1,3 @@
1
+ from pingintel_api import SOVFixerAPIClient
2
+
3
+ SOVFixerAPIClient().ping()