pingintel-api 1.4.10__tar.gz → 1.4.11__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.
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/.gitignore +2 -1
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/PKG-INFO +16 -11
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/README.md +14 -10
- pingintel_api-1.4.11/requirements.txt +3 -0
- pingintel_api-1.4.11/src/pingintel_api/__about__.py +1 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/pingdata/pingdata_api_client.py +47 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/pingdata/types.py +42 -2
- pingintel_api-1.4.11/src/pingintel_api/pingdataapi_cmd.py +449 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/pingvision/pingvision_api_client.py +85 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/pingvisionapi_cmd.py +56 -0
- pingintel_api-1.4.10/requirements.txt +0 -2
- pingintel_api-1.4.10/src/pingintel_api/__about__.py +0 -1
- pingintel_api-1.4.10/src/pingintel_api/pingdataapi_cmd.py +0 -263
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/.pre-commit-config.yaml +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/LICENSE +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/data/corrections.csv +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/data/demo_email.eml +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/data/parse_sov_testfile.xlsx +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-data/enhance_multiple_locations.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-data/enhance_one_location.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-data/pingdata_enhance.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/build_sov_pipeline.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/get_or_create_sov_output.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/monitor_sov_activity.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/parse_sov.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/sovfixer_fix_sov.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/sovfixer_list_activity.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/sovfixer_reoutput_sov.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/update_sov.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-vision/clear_and_triage_api.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-vision/pingvision_activity.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-vision/send_and_track_submission.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/pingvision_assignment.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/test_reoutput_locations.csv +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/test_sov.xlsx +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/generate_docs.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/justfile +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/pyproject.toml +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/__init__.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/api_client_base.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/common_types.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/constants.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/pingmaps/pingmaps_api_client.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/pingmaps/types.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/pingmapsapi_cmd.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/pingvision/types.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/sov_fixer/sov_fixer_api_client.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/sov_fixer/types.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/sovfixerapi_cmd.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/url_signature_factory.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/utils.py +0 -0
- {pingintel_api-1.4.10 → pingintel_api-1.4.11}/tests/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pingintel-api
|
|
3
|
-
Version: 1.4.
|
|
3
|
+
Version: 1.4.11
|
|
4
4
|
Summary: Python-based client to Ping Data Intelligence APIs
|
|
5
5
|
Project-URL: Homepage, https://github.com/pingintel/pingintel-api
|
|
6
6
|
Project-URL: Issues, https://github.com/pingintel/pingintel-api/issues
|
|
@@ -12,6 +12,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
12
12
|
Requires-Python: >=3.11
|
|
13
13
|
Requires-Dist: click
|
|
14
14
|
Requires-Dist: requests
|
|
15
|
+
Requires-Dist: typing-extensions>=4.13
|
|
15
16
|
Description-Content-Type: text/markdown
|
|
16
17
|
|
|
17
18
|
# pingintel-api
|
|
@@ -90,7 +91,9 @@ Commands:
|
|
|
90
91
|
activity List submission activity.
|
|
91
92
|
create Create new submission from file(s).
|
|
92
93
|
download-document Download document by document URL.
|
|
94
|
+
events List submission events.
|
|
93
95
|
get Get submission detail.
|
|
96
|
+
get-output Fetch or generate an output from a previous...
|
|
94
97
|
list-submission-statuses List submission statuses.
|
|
95
98
|
list-teams List teams.
|
|
96
99
|
|
|
@@ -112,11 +115,14 @@ Options:
|
|
|
112
115
|
--help Show this message and exit.
|
|
113
116
|
|
|
114
117
|
Commands:
|
|
115
|
-
activity
|
|
116
|
-
check-progress
|
|
117
|
-
fix
|
|
118
|
-
get-output
|
|
118
|
+
activity List submission activity.
|
|
119
|
+
check-progress Check the progress of a submission.
|
|
120
|
+
fix Extract insurance information from file(s).
|
|
121
|
+
get-output Fetch or generate an output from a previous...
|
|
122
|
+
history List submission activity.
|
|
123
|
+
list-output-formats List available output formats.
|
|
119
124
|
serverinfo
|
|
125
|
+
update Update an existing SOV with new location data.
|
|
120
126
|
|
|
121
127
|
```
|
|
122
128
|
|
|
@@ -140,8 +146,11 @@ Options:
|
|
|
140
146
|
--help Show this message and exit.
|
|
141
147
|
|
|
142
148
|
Commands:
|
|
143
|
-
bulk-enhance
|
|
144
|
-
enhance
|
|
149
|
+
bulk-enhance Request data about multiple addresses using async API.
|
|
150
|
+
enhance Request data synchronously about a single address.
|
|
151
|
+
get-datasource Get the configuration for a single datasource by its...
|
|
152
|
+
list-datasources List the datasource configurations the authenticated...
|
|
153
|
+
usage Get API credit usage over a time range, broken down...
|
|
145
154
|
|
|
146
155
|
```
|
|
147
156
|
|
|
@@ -160,10 +169,6 @@ Options:
|
|
|
160
169
|
PINGDATA_AUTH_TOKEN environment variable.
|
|
161
170
|
-v, --verbose Can be used multiple times. -v for INFO, -vv
|
|
162
171
|
for DEBUG, -vvv for very DEBUG.
|
|
163
|
-
-D, --delegate-to ORG_SHORT_NAME
|
|
164
|
-
Delegate to another organization. Provide
|
|
165
|
-
the 'short name' of the desired delegatee.
|
|
166
|
-
Requires the `delegate` permission.
|
|
167
172
|
--help Show this message and exit.
|
|
168
173
|
|
|
169
174
|
Commands:
|
|
@@ -74,7 +74,9 @@ Commands:
|
|
|
74
74
|
activity List submission activity.
|
|
75
75
|
create Create new submission from file(s).
|
|
76
76
|
download-document Download document by document URL.
|
|
77
|
+
events List submission events.
|
|
77
78
|
get Get submission detail.
|
|
79
|
+
get-output Fetch or generate an output from a previous...
|
|
78
80
|
list-submission-statuses List submission statuses.
|
|
79
81
|
list-teams List teams.
|
|
80
82
|
|
|
@@ -96,11 +98,14 @@ Options:
|
|
|
96
98
|
--help Show this message and exit.
|
|
97
99
|
|
|
98
100
|
Commands:
|
|
99
|
-
activity
|
|
100
|
-
check-progress
|
|
101
|
-
fix
|
|
102
|
-
get-output
|
|
101
|
+
activity List submission activity.
|
|
102
|
+
check-progress Check the progress of a submission.
|
|
103
|
+
fix Extract insurance information from file(s).
|
|
104
|
+
get-output Fetch or generate an output from a previous...
|
|
105
|
+
history List submission activity.
|
|
106
|
+
list-output-formats List available output formats.
|
|
103
107
|
serverinfo
|
|
108
|
+
update Update an existing SOV with new location data.
|
|
104
109
|
|
|
105
110
|
```
|
|
106
111
|
|
|
@@ -124,8 +129,11 @@ Options:
|
|
|
124
129
|
--help Show this message and exit.
|
|
125
130
|
|
|
126
131
|
Commands:
|
|
127
|
-
bulk-enhance
|
|
128
|
-
enhance
|
|
132
|
+
bulk-enhance Request data about multiple addresses using async API.
|
|
133
|
+
enhance Request data synchronously about a single address.
|
|
134
|
+
get-datasource Get the configuration for a single datasource by its...
|
|
135
|
+
list-datasources List the datasource configurations the authenticated...
|
|
136
|
+
usage Get API credit usage over a time range, broken down...
|
|
129
137
|
|
|
130
138
|
```
|
|
131
139
|
|
|
@@ -144,10 +152,6 @@ Options:
|
|
|
144
152
|
PINGDATA_AUTH_TOKEN environment variable.
|
|
145
153
|
-v, --verbose Can be used multiple times. -v for INFO, -vv
|
|
146
154
|
for DEBUG, -vvv for very DEBUG.
|
|
147
|
-
-D, --delegate-to ORG_SHORT_NAME
|
|
148
|
-
Delegate to another organization. Provide
|
|
149
|
-
the 'short name' of the desired delegatee.
|
|
150
|
-
Requires the `delegate` permission.
|
|
151
155
|
--help Show this message and exit.
|
|
152
156
|
|
|
153
157
|
Commands:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.4.11"
|
{pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/pingdata/pingdata_api_client.py
RENAMED
|
@@ -340,6 +340,53 @@ class PingDataAPIClient(APIClientBase):
|
|
|
340
340
|
raise_for_status(response)
|
|
341
341
|
return response.json()
|
|
342
342
|
|
|
343
|
+
def list_datasources(
|
|
344
|
+
self,
|
|
345
|
+
*,
|
|
346
|
+
delegate_to: str | None = None,
|
|
347
|
+
) -> t.ListDatasourcesResponse:
|
|
348
|
+
"""
|
|
349
|
+
List the datasource configurations the authenticated user has access to.
|
|
350
|
+
|
|
351
|
+
:param delegate_to: Optional delegate to use for the request.
|
|
352
|
+
|
|
353
|
+
:return: Dict with a 'configs' key mapping each source code (e.g. 'PG', 'PH') to its
|
|
354
|
+
datasource configuration (required/optional attributes, supported regions,
|
|
355
|
+
output fields, etc.).
|
|
356
|
+
"""
|
|
357
|
+
url = self.api_url + "/api/v1/datasources"
|
|
358
|
+
params: dict = {}
|
|
359
|
+
if delegate_to:
|
|
360
|
+
params["delegate_to"] = delegate_to
|
|
361
|
+
|
|
362
|
+
response = self.get(url, params=params)
|
|
363
|
+
raise_for_status(response)
|
|
364
|
+
return response.json()
|
|
365
|
+
|
|
366
|
+
def get_datasource(
|
|
367
|
+
self,
|
|
368
|
+
*,
|
|
369
|
+
code: str,
|
|
370
|
+
delegate_to: str | None = None,
|
|
371
|
+
) -> t.GetDatasourceResponse:
|
|
372
|
+
"""
|
|
373
|
+
Get the configuration for a single datasource by its source code.
|
|
374
|
+
|
|
375
|
+
:param code: Datasource source code (e.g. 'PG', 'PH', 'DTC'). Case-insensitive.
|
|
376
|
+
:param delegate_to: Optional delegate to use for the request.
|
|
377
|
+
|
|
378
|
+
:return: Dict with a 'config' key containing the datasource configuration
|
|
379
|
+
(required/optional attributes, supported regions, output fields, etc.).
|
|
380
|
+
"""
|
|
381
|
+
url = self.api_url + f"/api/v1/datasources/{code}"
|
|
382
|
+
params: dict = {}
|
|
383
|
+
if delegate_to:
|
|
384
|
+
params["delegate_to"] = delegate_to
|
|
385
|
+
|
|
386
|
+
response = self.get(url, params=params)
|
|
387
|
+
raise_for_status(response)
|
|
388
|
+
return response.json()
|
|
389
|
+
|
|
343
390
|
def fetch_bulk_enhance_output(self, request_id: str, filename: str, output_path: str | None = None) -> bytes:
|
|
344
391
|
"""
|
|
345
392
|
Download a result file from a completed bulk enhance job.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
# Copyright 2021-2024 Ping Data Intelligence
|
|
2
|
-
from typing import Any, Dict, List,
|
|
2
|
+
from typing import Any, Dict, List, Optional, Literal
|
|
3
|
+
from typing_extensions import NotRequired, TypedDict
|
|
3
4
|
from datetime import datetime
|
|
4
5
|
import enum
|
|
5
6
|
import functools
|
|
@@ -166,9 +167,16 @@ class BatchLocation(SingleLocation):
|
|
|
166
167
|
datasource_config: NotRequired[dict | None]
|
|
167
168
|
|
|
168
169
|
|
|
170
|
+
class EnhanceResponseItem(TypedDict, extra_items=Any):
|
|
171
|
+
is_success: bool
|
|
172
|
+
status_code: int
|
|
173
|
+
fetch_time: float
|
|
174
|
+
error_message: NotRequired[str | None]
|
|
175
|
+
|
|
176
|
+
|
|
169
177
|
class EnhanceResponse(TypedDict):
|
|
170
178
|
id: str
|
|
171
|
-
location_data: dict[SOURCES,
|
|
179
|
+
location_data: dict[SOURCES, EnhanceResponseItem]
|
|
172
180
|
|
|
173
181
|
|
|
174
182
|
class BulkEnhanceResponseCheckProgressResultOutputFile(TypedDict):
|
|
@@ -235,3 +243,35 @@ class UsageResponse(TypedDict):
|
|
|
235
243
|
end: str
|
|
236
244
|
buckets: list[UsageBucket]
|
|
237
245
|
totals: dict[str, int]
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
class DatasourceOutputField(TypedDict, extra_items=Any):
|
|
249
|
+
type: str
|
|
250
|
+
description: NotRequired[str]
|
|
251
|
+
display_type: NotRequired[str]
|
|
252
|
+
return_values: NotRequired[Any]
|
|
253
|
+
category: NotRequired[list[str]]
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class DatasourceConfig(TypedDict):
|
|
257
|
+
source_name: str
|
|
258
|
+
source_code: str
|
|
259
|
+
required_attrs: list[str]
|
|
260
|
+
optional_attrs: list[str]
|
|
261
|
+
requires_credentials: bool
|
|
262
|
+
non_supported_input_values: dict[str, list[str]] | None
|
|
263
|
+
supported_countries: list[str] | None
|
|
264
|
+
supported_us_states: list[str] | None
|
|
265
|
+
excluded_us_states: list[str] | None
|
|
266
|
+
geocode_confidence_required: int | None
|
|
267
|
+
geocode_precision_required: int | None
|
|
268
|
+
output_fields: dict[str, DatasourceOutputField]
|
|
269
|
+
base_output_fields: dict[str, DatasourceOutputField]
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
class ListDatasourcesResponse(TypedDict):
|
|
273
|
+
configs: dict[str, DatasourceConfig]
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class GetDatasourceResponse(TypedDict):
|
|
277
|
+
config: DatasourceConfig
|
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
|
|
3
|
+
# Copyright 2021-2024 Ping Data Intelligence
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import pprint
|
|
8
|
+
import time
|
|
9
|
+
from timeit import default_timer as timer
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from pingintel_api import PingDataAPIClient
|
|
13
|
+
|
|
14
|
+
from pingintel_api.api_client_base import AuthTokenNotFound
|
|
15
|
+
from pingintel_api.pingdata.types import Location
|
|
16
|
+
from pingintel_api.utils import set_verbosity
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
global start_time
|
|
22
|
+
start_time = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
pingdataapi.py
|
|
27
|
+
|
|
28
|
+
Example Python commandline script for using the Ping Data Technologies Data API to enhance locations with additional data.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@click.group()
|
|
33
|
+
@click.option(
|
|
34
|
+
"-e",
|
|
35
|
+
"--environment",
|
|
36
|
+
type=click.Choice(
|
|
37
|
+
[
|
|
38
|
+
"prod",
|
|
39
|
+
"prodeu",
|
|
40
|
+
"staging",
|
|
41
|
+
"dev",
|
|
42
|
+
],
|
|
43
|
+
case_sensitive=False,
|
|
44
|
+
),
|
|
45
|
+
)
|
|
46
|
+
@click.option(
|
|
47
|
+
"-u",
|
|
48
|
+
"--api-url",
|
|
49
|
+
help="Provide base url (instead of environment, primarily for debugging)",
|
|
50
|
+
)
|
|
51
|
+
@click.option(
|
|
52
|
+
"--auth-token",
|
|
53
|
+
help="Provide auth token via --auth-token or PINGDATA_AUTH_TOKEN environment variable.",
|
|
54
|
+
)
|
|
55
|
+
@click.option(
|
|
56
|
+
"-v", "--verbose", count=True, help="Can be used multiple times. -v for INFO, -vv for DEBUG, -vvv for very DEBUG."
|
|
57
|
+
)
|
|
58
|
+
@click.option(
|
|
59
|
+
"-D",
|
|
60
|
+
"--delegate-to",
|
|
61
|
+
metavar="ORG_SHORT_NAME",
|
|
62
|
+
help="Delegate to another organization. Provide the 'short name' of the desired delegatee. Requires the `delegate` permission.",
|
|
63
|
+
)
|
|
64
|
+
@click.pass_context
|
|
65
|
+
def cli(ctx, environment, api_url, auth_token, verbose, delegate_to):
|
|
66
|
+
ctx.ensure_object(dict)
|
|
67
|
+
ctx.obj["environment"] = environment
|
|
68
|
+
ctx.obj["auth_token"] = auth_token
|
|
69
|
+
ctx.obj["api_url"] = api_url
|
|
70
|
+
ctx.obj["delegate_to"] = delegate_to
|
|
71
|
+
set_verbosity(verbose)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def get_client(ctx) -> PingDataAPIClient:
|
|
75
|
+
environment = ctx.obj["environment"]
|
|
76
|
+
auth_token = ctx.obj["auth_token"]
|
|
77
|
+
api_url = ctx.obj["api_url"]
|
|
78
|
+
try:
|
|
79
|
+
client = PingDataAPIClient(environment=environment, auth_token=auth_token, api_url=api_url)
|
|
80
|
+
except AuthTokenNotFound as e:
|
|
81
|
+
click.echo(e)
|
|
82
|
+
raise click.Abort()
|
|
83
|
+
|
|
84
|
+
return client
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def parse_extra_kwargs(extra: tuple[str, ...]) -> dict:
|
|
88
|
+
"""Parse key=value pairs into a dictionary, converting numeric values."""
|
|
89
|
+
result = {}
|
|
90
|
+
for item in extra:
|
|
91
|
+
if "=" not in item:
|
|
92
|
+
raise click.BadParameter(f"Invalid format '{item}'. Expected key=value")
|
|
93
|
+
key, value = item.split("=", 1)
|
|
94
|
+
# Try to convert to int, then float, otherwise keep as string
|
|
95
|
+
try:
|
|
96
|
+
result[key] = int(value)
|
|
97
|
+
except ValueError:
|
|
98
|
+
try:
|
|
99
|
+
result[key] = float(value)
|
|
100
|
+
except ValueError:
|
|
101
|
+
result[key] = value
|
|
102
|
+
return result
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@cli.command()
|
|
106
|
+
@click.pass_context
|
|
107
|
+
@click.option("-a", "--address", multiple=True)
|
|
108
|
+
@click.option(
|
|
109
|
+
"-s",
|
|
110
|
+
"--sources",
|
|
111
|
+
multiple=True,
|
|
112
|
+
required=True,
|
|
113
|
+
)
|
|
114
|
+
@click.option("-c", "--country", type=str, default=None, help="Optional. Provides a country hint to the geocoders.")
|
|
115
|
+
@click.option("--latitude", type=float, default=None, help="Optional. Specify latitude.")
|
|
116
|
+
@click.option("--longitude", type=float, default=None, help="Optional. Specify longitude.")
|
|
117
|
+
@click.option("--timeout", type=float, default=None, help="Optional. Maximum time to wait for response in seconds.")
|
|
118
|
+
@click.option("-r", "--include-raw-response", is_flag=True, help="Optional. Include raw response from all sources.")
|
|
119
|
+
@click.option("--nocache", is_flag=True, help="If set, do not use cache.")
|
|
120
|
+
@click.option(
|
|
121
|
+
"-x",
|
|
122
|
+
"--extra",
|
|
123
|
+
multiple=True,
|
|
124
|
+
metavar="KEY=VALUE",
|
|
125
|
+
help="Extra location kwargs (e.g., -x const__code_rms=RC -x const__num_stories=3). Can be used multiple times.",
|
|
126
|
+
)
|
|
127
|
+
def enhance(
|
|
128
|
+
ctx: click.Context,
|
|
129
|
+
address: str,
|
|
130
|
+
sources: list[str],
|
|
131
|
+
country: str | None,
|
|
132
|
+
latitude: float | None,
|
|
133
|
+
longitude: float | None,
|
|
134
|
+
timeout: float | None = None,
|
|
135
|
+
include_raw_response: bool = False,
|
|
136
|
+
nocache: bool = False,
|
|
137
|
+
extra: tuple[str, ...] = (),
|
|
138
|
+
):
|
|
139
|
+
"""Request data synchronously about a single address."""
|
|
140
|
+
|
|
141
|
+
client = get_client(ctx)
|
|
142
|
+
|
|
143
|
+
extra_location_kwargs = parse_extra_kwargs(extra)
|
|
144
|
+
|
|
145
|
+
response_data = client.enhance(
|
|
146
|
+
address=address,
|
|
147
|
+
country=country,
|
|
148
|
+
latitude=latitude,
|
|
149
|
+
longitude=longitude,
|
|
150
|
+
sources=sources,
|
|
151
|
+
timeout=timeout,
|
|
152
|
+
include_raw_response=include_raw_response,
|
|
153
|
+
nocache=nocache,
|
|
154
|
+
delegate_to=ctx.obj["delegate_to"],
|
|
155
|
+
**extra_location_kwargs,
|
|
156
|
+
)
|
|
157
|
+
click.echo(f"+ Finished querying with result:\n{pprint.pformat(response_data)}")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
@cli.command()
|
|
161
|
+
@click.pass_context
|
|
162
|
+
@click.option("-a", "--address", multiple=True)
|
|
163
|
+
@click.option("-f", "--file", type=click.File("r"), help="File containing location data, one per line.")
|
|
164
|
+
@click.option(
|
|
165
|
+
"-s",
|
|
166
|
+
"--sources",
|
|
167
|
+
multiple=True,
|
|
168
|
+
# required=True,
|
|
169
|
+
)
|
|
170
|
+
@click.option("--timeout", type=float, default=None, help="Optional. Maximum time to wait for response in seconds.")
|
|
171
|
+
@click.option("-r", "--include-raw-response", is_flag=True, help="Optional. Include raw response from all sources.")
|
|
172
|
+
@click.option("--nocache", is_flag=True, help="If set, do not use cache.")
|
|
173
|
+
@click.option("--fetch-outputs/--no-fetch-outputs", is_flag=True, default=True)
|
|
174
|
+
@click.option("-v", "--verbose", help="Enable verbose output. Can be used up to 3 times.", count=True)
|
|
175
|
+
def bulk_enhance(
|
|
176
|
+
ctx: click.Context,
|
|
177
|
+
address: list[str],
|
|
178
|
+
file: click.File,
|
|
179
|
+
sources: list[str],
|
|
180
|
+
timeout: float | None,
|
|
181
|
+
include_raw_response: bool,
|
|
182
|
+
nocache: bool,
|
|
183
|
+
fetch_outputs: bool,
|
|
184
|
+
verbose: int,
|
|
185
|
+
):
|
|
186
|
+
"""Request data about multiple addresses using async API."""
|
|
187
|
+
client = get_client(ctx)
|
|
188
|
+
|
|
189
|
+
locations = []
|
|
190
|
+
address_id_ctr = 0
|
|
191
|
+
if address:
|
|
192
|
+
for addr in address:
|
|
193
|
+
address_id_ctr += 1
|
|
194
|
+
locations.append(Location(address=addr, id=f"id_{address_id_ctr:03d}"))
|
|
195
|
+
|
|
196
|
+
if file:
|
|
197
|
+
for line in file:
|
|
198
|
+
address_id_ctr += 1
|
|
199
|
+
locations.append(Location(address=line.strip(), id=f"id_{address_id_ctr:03d}"))
|
|
200
|
+
|
|
201
|
+
response_data = client.bulk_enhance(
|
|
202
|
+
locations=locations,
|
|
203
|
+
sources=sources,
|
|
204
|
+
timeout=timeout,
|
|
205
|
+
include_raw_response=include_raw_response,
|
|
206
|
+
nocache=nocache,
|
|
207
|
+
fetch_outputs=fetch_outputs,
|
|
208
|
+
verbose=verbose,
|
|
209
|
+
delegate_to=ctx.obj["delegate_to"],
|
|
210
|
+
)
|
|
211
|
+
click.echo(f"+ Finished querying with result:\n{pprint.pformat(response_data)}")
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@cli.command()
|
|
215
|
+
@click.pass_context
|
|
216
|
+
@click.option(
|
|
217
|
+
"--start",
|
|
218
|
+
type=str,
|
|
219
|
+
default=None,
|
|
220
|
+
help="Start of the time range. Relative offset (-5m, -1h, -30d) or ISO 8601 UTC timestamp. Defaults to -30d.",
|
|
221
|
+
)
|
|
222
|
+
@click.option(
|
|
223
|
+
"--end",
|
|
224
|
+
type=str,
|
|
225
|
+
default=None,
|
|
226
|
+
help="End of the time range. Relative offset or ISO 8601 UTC timestamp. Defaults to now.",
|
|
227
|
+
)
|
|
228
|
+
@click.option(
|
|
229
|
+
"--username",
|
|
230
|
+
type=str,
|
|
231
|
+
default=None,
|
|
232
|
+
help="Filter by username. Defaults to the requesting user. Staff only when querying another user.",
|
|
233
|
+
)
|
|
234
|
+
@click.option(
|
|
235
|
+
"--org-short-name",
|
|
236
|
+
type=str,
|
|
237
|
+
default=None,
|
|
238
|
+
help="Filter by organization short name (e.g. 'AMWNS'). Staff only. Mutually exclusive with --username.",
|
|
239
|
+
)
|
|
240
|
+
def usage(
|
|
241
|
+
ctx: click.Context,
|
|
242
|
+
start: str | None,
|
|
243
|
+
end: str | None,
|
|
244
|
+
username: str | None,
|
|
245
|
+
org_short_name: str | None,
|
|
246
|
+
):
|
|
247
|
+
"""Get API credit usage over a time range, broken down by data source and time bucket."""
|
|
248
|
+
client = get_client(ctx)
|
|
249
|
+
response_data = client.get_usage(
|
|
250
|
+
start=start,
|
|
251
|
+
end=end,
|
|
252
|
+
username=username,
|
|
253
|
+
org_short_name=org_short_name,
|
|
254
|
+
delegate_to=ctx.obj["delegate_to"],
|
|
255
|
+
)
|
|
256
|
+
click.echo(pprint.pformat(response_data))
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
@cli.command()
|
|
260
|
+
@click.pass_context
|
|
261
|
+
def list_datasources(ctx: click.Context):
|
|
262
|
+
"""List the datasource configurations the authenticated user has access to."""
|
|
263
|
+
client = get_client(ctx)
|
|
264
|
+
response_data = client.list_datasources(delegate_to=ctx.obj["delegate_to"])
|
|
265
|
+
click.echo(pprint.pformat(response_data))
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def _field_description(info: dict) -> str:
|
|
269
|
+
"""Extract the human-readable description from an output-field entry.
|
|
270
|
+
|
|
271
|
+
Field metadata (description, display_type, etc.) is spread flat alongside `type`.
|
|
272
|
+
"""
|
|
273
|
+
return str((info or {}).get("description", "") or "")
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _render_datasource_markdown(config: dict) -> str:
|
|
277
|
+
"""Render a single datasource config dict as human-readable Markdown."""
|
|
278
|
+
lines: list[str] = []
|
|
279
|
+
|
|
280
|
+
name = config.get("source_name") or config.get("source_code") or "Datasource"
|
|
281
|
+
code = config.get("source_code") or ""
|
|
282
|
+
lines.append(f"# {name} ({code})" if code else f"# {name}")
|
|
283
|
+
lines.append("")
|
|
284
|
+
|
|
285
|
+
conf = config.get("geocode_confidence_required")
|
|
286
|
+
prec = config.get("geocode_precision_required")
|
|
287
|
+
lines.append(f"- **Source code:** {code or 'None'}")
|
|
288
|
+
lines.append(f"- **Requires credentials:** {'Yes' if config.get('requires_credentials') else 'No'}")
|
|
289
|
+
lines.append(f"- **Geocode confidence required:** {conf if conf is not None else 'None'}")
|
|
290
|
+
lines.append(f"- **Geocode precision required:** {prec if prec is not None else 'None'}")
|
|
291
|
+
lines.append("")
|
|
292
|
+
|
|
293
|
+
required = config.get("required_attrs") or []
|
|
294
|
+
optional = config.get("optional_attrs") or []
|
|
295
|
+
lines.append("## Input attributes")
|
|
296
|
+
lines.append("")
|
|
297
|
+
lines.append(f"- **Required:** {', '.join(required) if required else 'None'}")
|
|
298
|
+
lines.append(f"- **Optional:** {', '.join(optional) if optional else 'None'}")
|
|
299
|
+
lines.append("")
|
|
300
|
+
|
|
301
|
+
countries = config.get("supported_countries")
|
|
302
|
+
states = config.get("supported_us_states")
|
|
303
|
+
excluded = config.get("excluded_us_states")
|
|
304
|
+
if countries or states or excluded:
|
|
305
|
+
lines.append("## Supported regions")
|
|
306
|
+
lines.append("")
|
|
307
|
+
if countries:
|
|
308
|
+
lines.append(f"- **Countries:** {', '.join(countries)}")
|
|
309
|
+
if states:
|
|
310
|
+
lines.append(f"- **US states:** {', '.join(states)}")
|
|
311
|
+
if excluded:
|
|
312
|
+
lines.append(f"- **Excluded US states:** {', '.join(excluded)}")
|
|
313
|
+
lines.append("")
|
|
314
|
+
|
|
315
|
+
non_supported = config.get("non_supported_input_values")
|
|
316
|
+
if non_supported:
|
|
317
|
+
lines.append("## Non-supported input values")
|
|
318
|
+
lines.append("")
|
|
319
|
+
for field, values in non_supported.items():
|
|
320
|
+
lines.append(f"- **{field}:** {', '.join(values) if values else 'None'}")
|
|
321
|
+
lines.append("")
|
|
322
|
+
|
|
323
|
+
def _append_field_table(title: str, fields: dict) -> None:
|
|
324
|
+
if not fields:
|
|
325
|
+
return
|
|
326
|
+
lines.append(f"## {title}")
|
|
327
|
+
lines.append("")
|
|
328
|
+
lines.append("| Field | Type | Description |")
|
|
329
|
+
lines.append("| --- | --- | --- |")
|
|
330
|
+
for field_name, info in fields.items():
|
|
331
|
+
field_type = str((info or {}).get("type", "")).replace("|", "\\|")
|
|
332
|
+
description = _field_description(info).replace("|", "\\|").replace("\n", " ")
|
|
333
|
+
lines.append(f"| {field_name} | {field_type} | {description} |")
|
|
334
|
+
# Nested object fields declare their sub-attributes under `fields`.
|
|
335
|
+
for sub_name, sub_info in ((info or {}).get("fields") or {}).items():
|
|
336
|
+
sub_type = str((sub_info or {}).get("type", "")).replace("|", "\\|")
|
|
337
|
+
sub_desc = _field_description(sub_info).replace("|", "\\|").replace("\n", " ")
|
|
338
|
+
lines.append(f"| {field_name}.{sub_name} | {sub_type} | {sub_desc} |")
|
|
339
|
+
lines.append("")
|
|
340
|
+
|
|
341
|
+
_append_field_table("Output fields", config.get("output_fields") or {})
|
|
342
|
+
_append_field_table("Base output fields", config.get("base_output_fields") or {})
|
|
343
|
+
|
|
344
|
+
return "\n".join(lines).rstrip() + "\n"
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
def _render_datasource_pretty(config: dict) -> str:
|
|
348
|
+
"""Render a single datasource config dict as plain readable text (no Markdown tables)."""
|
|
349
|
+
lines: list[str] = []
|
|
350
|
+
|
|
351
|
+
name = config.get("source_name") or config.get("source_code") or "Datasource"
|
|
352
|
+
code = config.get("source_code") or ""
|
|
353
|
+
heading = f"{name} ({code})" if code else name
|
|
354
|
+
lines.append(heading)
|
|
355
|
+
lines.append("=" * len(heading))
|
|
356
|
+
lines.append("")
|
|
357
|
+
|
|
358
|
+
conf = config.get("geocode_confidence_required")
|
|
359
|
+
prec = config.get("geocode_precision_required")
|
|
360
|
+
lines.append(f"Source code: {code or 'None'}")
|
|
361
|
+
lines.append(f"Requires credentials: {'Yes' if config.get('requires_credentials') else 'No'}")
|
|
362
|
+
lines.append(f"Geocode confidence required: {conf if conf is not None else 'None'}")
|
|
363
|
+
lines.append(f"Geocode precision required: {prec if prec is not None else 'None'}")
|
|
364
|
+
lines.append("")
|
|
365
|
+
|
|
366
|
+
required = config.get("required_attrs") or []
|
|
367
|
+
optional = config.get("optional_attrs") or []
|
|
368
|
+
lines.append("Input attributes:")
|
|
369
|
+
lines.append(f" Required: {', '.join(required) if required else 'None'}")
|
|
370
|
+
lines.append(f" Optional: {', '.join(optional) if optional else 'None'}")
|
|
371
|
+
lines.append("")
|
|
372
|
+
|
|
373
|
+
countries = config.get("supported_countries")
|
|
374
|
+
states = config.get("supported_us_states")
|
|
375
|
+
excluded = config.get("excluded_us_states")
|
|
376
|
+
if countries or states or excluded:
|
|
377
|
+
lines.append("Supported regions:")
|
|
378
|
+
if countries:
|
|
379
|
+
lines.append(f" Countries: {', '.join(countries)}")
|
|
380
|
+
if states:
|
|
381
|
+
lines.append(f" US states: {', '.join(states)}")
|
|
382
|
+
if excluded:
|
|
383
|
+
lines.append(f" Excluded US states: {', '.join(excluded)}")
|
|
384
|
+
lines.append("")
|
|
385
|
+
|
|
386
|
+
non_supported = config.get("non_supported_input_values")
|
|
387
|
+
if non_supported:
|
|
388
|
+
lines.append("Non-supported input values:")
|
|
389
|
+
for field, values in non_supported.items():
|
|
390
|
+
lines.append(f" {field}: {', '.join(values) if values else 'None'}")
|
|
391
|
+
lines.append("")
|
|
392
|
+
|
|
393
|
+
def _append_field_section(title: str, fields: dict) -> None:
|
|
394
|
+
if not fields:
|
|
395
|
+
return
|
|
396
|
+
lines.append(f"{title}:")
|
|
397
|
+
for field_name, info in fields.items():
|
|
398
|
+
field_type = str((info or {}).get("type", ""))
|
|
399
|
+
lines.append(f" {field_name} ({field_type})" if field_type else f" {field_name}")
|
|
400
|
+
description = _field_description(info).strip()
|
|
401
|
+
for desc_line in description.splitlines():
|
|
402
|
+
lines.append(f" {desc_line}")
|
|
403
|
+
# Nested object fields declare their sub-attributes under `fields`.
|
|
404
|
+
for sub_name, sub_info in ((info or {}).get("fields") or {}).items():
|
|
405
|
+
sub_type = str((sub_info or {}).get("type", ""))
|
|
406
|
+
dotted = f"{field_name}.{sub_name}"
|
|
407
|
+
lines.append(f" {dotted} ({sub_type})" if sub_type else f" {dotted}")
|
|
408
|
+
for desc_line in _field_description(sub_info).strip().splitlines():
|
|
409
|
+
lines.append(f" {desc_line}")
|
|
410
|
+
lines.append("")
|
|
411
|
+
|
|
412
|
+
_append_field_section("Output fields", config.get("output_fields") or {})
|
|
413
|
+
_append_field_section("Base output fields", config.get("base_output_fields") or {})
|
|
414
|
+
|
|
415
|
+
return "\n".join(lines).rstrip() + "\n"
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
@cli.command()
|
|
419
|
+
@click.pass_context
|
|
420
|
+
@click.argument("code", type=str)
|
|
421
|
+
@click.option(
|
|
422
|
+
"--format",
|
|
423
|
+
"output_format",
|
|
424
|
+
type=click.Choice(["json", "markdown", "pretty"], case_sensitive=False),
|
|
425
|
+
default="markdown",
|
|
426
|
+
show_default=True,
|
|
427
|
+
help="Output format for the datasource config.",
|
|
428
|
+
)
|
|
429
|
+
def get_datasource(ctx: click.Context, code: str, output_format: str):
|
|
430
|
+
"""Get the configuration for a single datasource by its source code (e.g. PG, PH, DTC)."""
|
|
431
|
+
client = get_client(ctx)
|
|
432
|
+
response_data = client.get_datasource(code=code, delegate_to=ctx.obj["delegate_to"])
|
|
433
|
+
output_format = output_format.lower()
|
|
434
|
+
if output_format == "json":
|
|
435
|
+
click.echo(json.dumps(response_data, indent=2))
|
|
436
|
+
return
|
|
437
|
+
config = response_data.get("config", response_data)
|
|
438
|
+
if output_format == "pretty":
|
|
439
|
+
click.echo(_render_datasource_pretty(config))
|
|
440
|
+
else:
|
|
441
|
+
click.echo(_render_datasource_markdown(config))
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def main():
|
|
445
|
+
cli()
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
if __name__ == "__main__":
|
|
449
|
+
main()
|
{pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/pingvision/pingvision_api_client.py
RENAMED
|
@@ -373,3 +373,88 @@ class PingVisionAPIClient(APIClientBase):
|
|
|
373
373
|
url = self.api_url + f"/api/v1/submission/{pingid}/add_data_items"
|
|
374
374
|
response = self.post(url, json={"items": items, "action": action})
|
|
375
375
|
raise_for_status(response)
|
|
376
|
+
|
|
377
|
+
def export_modeling_options(self, pingid: str):
|
|
378
|
+
url = self.api_url + f"/api/v1/submission/{pingid}/cat/modeling-options/export"
|
|
379
|
+
|
|
380
|
+
response = self.get(url)
|
|
381
|
+
|
|
382
|
+
raise_for_status(response)
|
|
383
|
+
|
|
384
|
+
response_data = response.json()
|
|
385
|
+
return response_data
|
|
386
|
+
|
|
387
|
+
def generate_acc_loc_files_async_start(
|
|
388
|
+
self,
|
|
389
|
+
pingid: str,
|
|
390
|
+
modeling_option_uuids: list[str] = None,
|
|
391
|
+
cat_model_type: str = None,
|
|
392
|
+
use_secondary_modifiers: bool = None,
|
|
393
|
+
use_ping_geocoding: bool = None,
|
|
394
|
+
layer_output: str = None,
|
|
395
|
+
):
|
|
396
|
+
url = self.api_url + f"/api/v1/submission/{pingid}/cat/acc-loc-files"
|
|
397
|
+
|
|
398
|
+
data = {}
|
|
399
|
+
if modeling_option_uuids:
|
|
400
|
+
data["modeling_option_uuids"] = modeling_option_uuids
|
|
401
|
+
if cat_model_type:
|
|
402
|
+
data["cat_model_type"] = cat_model_type
|
|
403
|
+
if use_secondary_modifiers:
|
|
404
|
+
data["use_secondary_modifiers"] = use_secondary_modifiers
|
|
405
|
+
if use_ping_geocoding:
|
|
406
|
+
data["use_ping_geocoding"] = use_ping_geocoding
|
|
407
|
+
if layer_output:
|
|
408
|
+
data["layer_output"] = layer_output
|
|
409
|
+
|
|
410
|
+
response = self.post(url, json=data)
|
|
411
|
+
if 200 <= response.status_code < 300:
|
|
412
|
+
pass
|
|
413
|
+
# pprint.pprint(response.json())
|
|
414
|
+
else:
|
|
415
|
+
self.logger.warning(f"Error starting Generate Acc/Loc file request:\n{pprint.pformat(response.text)}")
|
|
416
|
+
|
|
417
|
+
raise_for_status(response)
|
|
418
|
+
|
|
419
|
+
response_data = response.json()
|
|
420
|
+
return response_data
|
|
421
|
+
|
|
422
|
+
def generate_acc_loc_files_async_check_progress(self, pingid, uuid):
|
|
423
|
+
status_url = self.api_url + f"/api/v1/submission/{pingid}/cat/acc-loc-files/{uuid}"
|
|
424
|
+
|
|
425
|
+
response = self.get(status_url)
|
|
426
|
+
# pprint.pprint(response.json())
|
|
427
|
+
raise_for_status(response)
|
|
428
|
+
|
|
429
|
+
response_data = response.json()
|
|
430
|
+
return response_data
|
|
431
|
+
|
|
432
|
+
def generate_acc_loc_files(
|
|
433
|
+
self,
|
|
434
|
+
pingid: str,
|
|
435
|
+
modeling_option_uuids: list[str] = None,
|
|
436
|
+
cat_model_type: str = None,
|
|
437
|
+
use_secondary_modifiers: bool = None,
|
|
438
|
+
use_ping_geocoding: bool = None,
|
|
439
|
+
layer_output: str = None,
|
|
440
|
+
):
|
|
441
|
+
client = self
|
|
442
|
+
response = client.generate_acc_loc_files_async_start(pingid, modeling_option_uuids, cat_model_type, use_secondary_modifiers, use_ping_geocoding, layer_output)
|
|
443
|
+
uuid = response.get("uuid")
|
|
444
|
+
assert uuid
|
|
445
|
+
|
|
446
|
+
response_data = None
|
|
447
|
+
while 1:
|
|
448
|
+
response_data = client.generate_acc_loc_files_async_check_progress(pingid, uuid)
|
|
449
|
+
status = response_data.get("status")
|
|
450
|
+
progress = response_data.get("processing_pct_complete")
|
|
451
|
+
POLL_SECS = 2.5
|
|
452
|
+
if status == "N" or status == "I":
|
|
453
|
+
self.logger.info(f" - Still in progress: {status} ({progress}%)")
|
|
454
|
+
time.sleep(POLL_SECS)
|
|
455
|
+
else:
|
|
456
|
+
break
|
|
457
|
+
|
|
458
|
+
return response_data
|
|
459
|
+
|
|
460
|
+
|
|
@@ -315,6 +315,62 @@ def list_submission_statuses(ctx, division):
|
|
|
315
315
|
return
|
|
316
316
|
pprint.pprint(results)
|
|
317
317
|
|
|
318
|
+
@cli.command()
|
|
319
|
+
@click.pass_context
|
|
320
|
+
@click.argument("pingid", type=str)
|
|
321
|
+
def modeling_options(ctx, pingid):
|
|
322
|
+
"""Export modeling options for a submission."""
|
|
323
|
+
client = get_client(ctx)
|
|
324
|
+
|
|
325
|
+
ret = client.export_modeling_options(pingid=pingid)
|
|
326
|
+
pprint.pprint(ret)
|
|
327
|
+
|
|
328
|
+
@cli.command()
|
|
329
|
+
@click.pass_context
|
|
330
|
+
@click.argument("pingid", type=str)
|
|
331
|
+
@click.option("-m", "--modeling-option-uuids", type=str, multiple=True, help="Generate files for specific modeling options (all if not provided)")
|
|
332
|
+
@click.option("--cat-model-type",type=click.Choice(
|
|
333
|
+
[
|
|
334
|
+
"air",
|
|
335
|
+
"rms",
|
|
336
|
+
],
|
|
337
|
+
case_sensitive=False,
|
|
338
|
+
), help="Cat models to generate files for (all if not provided)")
|
|
339
|
+
@click.option("--use-secondary-modifiers",type=bool)
|
|
340
|
+
@click.option("--use-ping-geocoding",type=bool)
|
|
341
|
+
@click.option("--layer-output",type=click.Choice(
|
|
342
|
+
[
|
|
343
|
+
"C",
|
|
344
|
+
"P",
|
|
345
|
+
],
|
|
346
|
+
case_sensitive=False,
|
|
347
|
+
),help="C (Combined) or P (Per Layer)",)
|
|
348
|
+
@click.option(
|
|
349
|
+
"--write/--no-write",
|
|
350
|
+
is_flag=True,
|
|
351
|
+
default=True,
|
|
352
|
+
help="(default) Download and write the acc/loc files to disk.",
|
|
353
|
+
)
|
|
354
|
+
def generate_acc_loc_files(ctx, pingid,modeling_option_uuids,cat_model_type,use_secondary_modifiers,use_ping_geocoding,layer_output, write):
|
|
355
|
+
"""List submission activity."""
|
|
356
|
+
client = get_client(ctx)
|
|
357
|
+
ret = client.generate_acc_loc_files(pingid, modeling_option_uuids,cat_model_type,use_secondary_modifiers,use_ping_geocoding,layer_output)
|
|
358
|
+
|
|
359
|
+
if write:
|
|
360
|
+
if ret.get("status") == "C":
|
|
361
|
+
for modeling_set in ret.get("modeling_sets", {}):
|
|
362
|
+
if modeling_set.get("status") == "C":
|
|
363
|
+
acc_file_url = modeling_set.get("acc_file_url")
|
|
364
|
+
if acc_file_url:
|
|
365
|
+
ctx.invoke(download_document, document_url=acc_file_url, output=None)
|
|
366
|
+
loc_file_url = modeling_set.get("loc_file_url")
|
|
367
|
+
if loc_file_url:
|
|
368
|
+
ctx.invoke(download_document, document_url=loc_file_url, output=None)
|
|
369
|
+
|
|
370
|
+
pprint.pprint(ret)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
|
|
318
374
|
|
|
319
375
|
@cli.command()
|
|
320
376
|
@click.pass_context
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.4.10"
|
|
@@ -1,263 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python
|
|
2
|
-
|
|
3
|
-
# Copyright 2021-2024 Ping Data Intelligence
|
|
4
|
-
|
|
5
|
-
import logging
|
|
6
|
-
import pprint
|
|
7
|
-
import time
|
|
8
|
-
from timeit import default_timer as timer
|
|
9
|
-
import click
|
|
10
|
-
|
|
11
|
-
from pingintel_api import PingDataAPIClient
|
|
12
|
-
|
|
13
|
-
from pingintel_api.api_client_base import AuthTokenNotFound
|
|
14
|
-
from pingintel_api.pingdata.types import Location
|
|
15
|
-
from pingintel_api.utils import set_verbosity
|
|
16
|
-
|
|
17
|
-
logger = logging.getLogger(__name__)
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
global start_time
|
|
21
|
-
start_time = None
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"""
|
|
25
|
-
pingdataapi.py
|
|
26
|
-
|
|
27
|
-
Example Python commandline script for using the Ping Data Technologies Data API to enhance locations with additional data.
|
|
28
|
-
"""
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@click.group()
|
|
32
|
-
@click.option(
|
|
33
|
-
"-e",
|
|
34
|
-
"--environment",
|
|
35
|
-
type=click.Choice(
|
|
36
|
-
[
|
|
37
|
-
"prod",
|
|
38
|
-
"prodeu",
|
|
39
|
-
"staging",
|
|
40
|
-
"dev",
|
|
41
|
-
],
|
|
42
|
-
case_sensitive=False,
|
|
43
|
-
),
|
|
44
|
-
)
|
|
45
|
-
@click.option(
|
|
46
|
-
"-u",
|
|
47
|
-
"--api-url",
|
|
48
|
-
help="Provide base url (instead of environment, primarily for debugging)",
|
|
49
|
-
)
|
|
50
|
-
@click.option(
|
|
51
|
-
"--auth-token",
|
|
52
|
-
help="Provide auth token via --auth-token or PINGDATA_AUTH_TOKEN environment variable.",
|
|
53
|
-
)
|
|
54
|
-
@click.option(
|
|
55
|
-
"-v", "--verbose", count=True, help="Can be used multiple times. -v for INFO, -vv for DEBUG, -vvv for very DEBUG."
|
|
56
|
-
)
|
|
57
|
-
@click.option(
|
|
58
|
-
"-D",
|
|
59
|
-
"--delegate-to",
|
|
60
|
-
metavar="ORG_SHORT_NAME",
|
|
61
|
-
help="Delegate to another organization. Provide the 'short name' of the desired delegatee. Requires the `delegate` permission.",
|
|
62
|
-
)
|
|
63
|
-
@click.pass_context
|
|
64
|
-
def cli(ctx, environment, api_url, auth_token, verbose, delegate_to):
|
|
65
|
-
ctx.ensure_object(dict)
|
|
66
|
-
ctx.obj["environment"] = environment
|
|
67
|
-
ctx.obj["auth_token"] = auth_token
|
|
68
|
-
ctx.obj["api_url"] = api_url
|
|
69
|
-
ctx.obj["delegate_to"] = delegate_to
|
|
70
|
-
set_verbosity(verbose)
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def get_client(ctx) -> PingDataAPIClient:
|
|
74
|
-
environment = ctx.obj["environment"]
|
|
75
|
-
auth_token = ctx.obj["auth_token"]
|
|
76
|
-
api_url = ctx.obj["api_url"]
|
|
77
|
-
try:
|
|
78
|
-
client = PingDataAPIClient(environment=environment, auth_token=auth_token, api_url=api_url)
|
|
79
|
-
except AuthTokenNotFound as e:
|
|
80
|
-
click.echo(e)
|
|
81
|
-
raise click.Abort()
|
|
82
|
-
|
|
83
|
-
return client
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
def parse_extra_kwargs(extra: tuple[str, ...]) -> dict:
|
|
87
|
-
"""Parse key=value pairs into a dictionary, converting numeric values."""
|
|
88
|
-
result = {}
|
|
89
|
-
for item in extra:
|
|
90
|
-
if "=" not in item:
|
|
91
|
-
raise click.BadParameter(f"Invalid format '{item}'. Expected key=value")
|
|
92
|
-
key, value = item.split("=", 1)
|
|
93
|
-
# Try to convert to int, then float, otherwise keep as string
|
|
94
|
-
try:
|
|
95
|
-
result[key] = int(value)
|
|
96
|
-
except ValueError:
|
|
97
|
-
try:
|
|
98
|
-
result[key] = float(value)
|
|
99
|
-
except ValueError:
|
|
100
|
-
result[key] = value
|
|
101
|
-
return result
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
@cli.command()
|
|
105
|
-
@click.pass_context
|
|
106
|
-
@click.option("-a", "--address", multiple=True)
|
|
107
|
-
@click.option(
|
|
108
|
-
"-s",
|
|
109
|
-
"--sources",
|
|
110
|
-
multiple=True,
|
|
111
|
-
required=True,
|
|
112
|
-
)
|
|
113
|
-
@click.option("-c", "--country", type=str, default=None, help="Optional. Provides a country hint to the geocoders.")
|
|
114
|
-
@click.option("--latitude", type=float, default=None, help="Optional. Specify latitude.")
|
|
115
|
-
@click.option("--longitude", type=float, default=None, help="Optional. Specify longitude.")
|
|
116
|
-
@click.option("--timeout", type=float, default=None, help="Optional. Maximum time to wait for response in seconds.")
|
|
117
|
-
@click.option("-r", "--include-raw-response", is_flag=True, help="Optional. Include raw response from all sources.")
|
|
118
|
-
@click.option("--nocache", is_flag=True, help="If set, do not use cache.")
|
|
119
|
-
@click.option(
|
|
120
|
-
"-x",
|
|
121
|
-
"--extra",
|
|
122
|
-
multiple=True,
|
|
123
|
-
metavar="KEY=VALUE",
|
|
124
|
-
help="Extra location kwargs (e.g., -x const__code_rms=RC -x const__num_stories=3). Can be used multiple times.",
|
|
125
|
-
)
|
|
126
|
-
def enhance(
|
|
127
|
-
ctx: click.Context,
|
|
128
|
-
address: str,
|
|
129
|
-
sources: list[str],
|
|
130
|
-
country: str | None,
|
|
131
|
-
latitude: float | None,
|
|
132
|
-
longitude: float | None,
|
|
133
|
-
timeout: float | None = None,
|
|
134
|
-
include_raw_response: bool = False,
|
|
135
|
-
nocache: bool = False,
|
|
136
|
-
extra: tuple[str, ...] = (),
|
|
137
|
-
):
|
|
138
|
-
"""Request data synchronously about a single address."""
|
|
139
|
-
|
|
140
|
-
client = get_client(ctx)
|
|
141
|
-
|
|
142
|
-
extra_location_kwargs = parse_extra_kwargs(extra)
|
|
143
|
-
|
|
144
|
-
response_data = client.enhance(
|
|
145
|
-
address=address,
|
|
146
|
-
country=country,
|
|
147
|
-
latitude=latitude,
|
|
148
|
-
longitude=longitude,
|
|
149
|
-
sources=sources,
|
|
150
|
-
timeout=timeout,
|
|
151
|
-
include_raw_response=include_raw_response,
|
|
152
|
-
nocache=nocache,
|
|
153
|
-
delegate_to=ctx.obj["delegate_to"],
|
|
154
|
-
**extra_location_kwargs,
|
|
155
|
-
)
|
|
156
|
-
click.echo(f"+ Finished querying with result:\n{pprint.pformat(response_data)}")
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
@cli.command()
|
|
160
|
-
@click.pass_context
|
|
161
|
-
@click.option("-a", "--address", multiple=True)
|
|
162
|
-
@click.option("-f", "--file", type=click.File("r"), help="File containing location data, one per line.")
|
|
163
|
-
@click.option(
|
|
164
|
-
"-s",
|
|
165
|
-
"--sources",
|
|
166
|
-
multiple=True,
|
|
167
|
-
# required=True,
|
|
168
|
-
)
|
|
169
|
-
@click.option("--timeout", type=float, default=None, help="Optional. Maximum time to wait for response in seconds.")
|
|
170
|
-
@click.option("-r", "--include-raw-response", is_flag=True, help="Optional. Include raw response from all sources.")
|
|
171
|
-
@click.option("--nocache", is_flag=True, help="If set, do not use cache.")
|
|
172
|
-
@click.option("--fetch-outputs/--no-fetch-outputs", is_flag=True, default=True)
|
|
173
|
-
@click.option("-v", "--verbose", help="Enable verbose output. Can be used up to 3 times.", count=True)
|
|
174
|
-
def bulk_enhance(
|
|
175
|
-
ctx: click.Context,
|
|
176
|
-
address: list[str],
|
|
177
|
-
file: click.File,
|
|
178
|
-
sources: list[str],
|
|
179
|
-
timeout: float | None,
|
|
180
|
-
include_raw_response: bool,
|
|
181
|
-
nocache: bool,
|
|
182
|
-
fetch_outputs: bool,
|
|
183
|
-
verbose: int,
|
|
184
|
-
):
|
|
185
|
-
"""Request data about multiple addresses using async API."""
|
|
186
|
-
client = get_client(ctx)
|
|
187
|
-
|
|
188
|
-
locations = []
|
|
189
|
-
address_id_ctr = 0
|
|
190
|
-
if address:
|
|
191
|
-
for addr in address:
|
|
192
|
-
address_id_ctr += 1
|
|
193
|
-
locations.append(Location(address=addr, id=f"id_{address_id_ctr:03d}"))
|
|
194
|
-
|
|
195
|
-
if file:
|
|
196
|
-
for line in file:
|
|
197
|
-
address_id_ctr += 1
|
|
198
|
-
locations.append(Location(address=line.strip(), id=f"id_{address_id_ctr:03d}"))
|
|
199
|
-
|
|
200
|
-
response_data = client.bulk_enhance(
|
|
201
|
-
locations=locations,
|
|
202
|
-
sources=sources,
|
|
203
|
-
timeout=timeout,
|
|
204
|
-
include_raw_response=include_raw_response,
|
|
205
|
-
nocache=nocache,
|
|
206
|
-
fetch_outputs=fetch_outputs,
|
|
207
|
-
verbose=verbose,
|
|
208
|
-
delegate_to=ctx.obj["delegate_to"],
|
|
209
|
-
)
|
|
210
|
-
click.echo(f"+ Finished querying with result:\n{pprint.pformat(response_data)}")
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
@cli.command()
|
|
214
|
-
@click.pass_context
|
|
215
|
-
@click.option(
|
|
216
|
-
"--start",
|
|
217
|
-
type=str,
|
|
218
|
-
default=None,
|
|
219
|
-
help="Start of the time range. Relative offset (-5m, -1h, -30d) or ISO 8601 UTC timestamp. Defaults to -30d.",
|
|
220
|
-
)
|
|
221
|
-
@click.option(
|
|
222
|
-
"--end",
|
|
223
|
-
type=str,
|
|
224
|
-
default=None,
|
|
225
|
-
help="End of the time range. Relative offset or ISO 8601 UTC timestamp. Defaults to now.",
|
|
226
|
-
)
|
|
227
|
-
@click.option(
|
|
228
|
-
"--username",
|
|
229
|
-
type=str,
|
|
230
|
-
default=None,
|
|
231
|
-
help="Filter by username. Defaults to the requesting user. Staff only when querying another user.",
|
|
232
|
-
)
|
|
233
|
-
@click.option(
|
|
234
|
-
"--org-short-name",
|
|
235
|
-
type=str,
|
|
236
|
-
default=None,
|
|
237
|
-
help="Filter by organization short name (e.g. 'AMWNS'). Staff only. Mutually exclusive with --username.",
|
|
238
|
-
)
|
|
239
|
-
def usage(
|
|
240
|
-
ctx: click.Context,
|
|
241
|
-
start: str | None,
|
|
242
|
-
end: str | None,
|
|
243
|
-
username: str | None,
|
|
244
|
-
org_short_name: str | None,
|
|
245
|
-
):
|
|
246
|
-
"""Get API credit usage over a time range, broken down by data source and time bucket."""
|
|
247
|
-
client = get_client(ctx)
|
|
248
|
-
response_data = client.get_usage(
|
|
249
|
-
start=start,
|
|
250
|
-
end=end,
|
|
251
|
-
username=username,
|
|
252
|
-
org_short_name=org_short_name,
|
|
253
|
-
delegate_to=ctx.obj["delegate_to"],
|
|
254
|
-
)
|
|
255
|
-
click.echo(pprint.pformat(response_data))
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
def main():
|
|
259
|
-
cli()
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
if __name__ == "__main__":
|
|
263
|
-
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-data/enhance_multiple_locations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/build_sov_pipeline.py
RENAMED
|
File without changes
|
{pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/get_or_create_sov_output.py
RENAMED
|
File without changes
|
{pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/monitor_sov_activity.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/sovfixer_list_activity.py
RENAMED
|
File without changes
|
{pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-extraction/sovfixer_reoutput_sov.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pingintel_api-1.4.10 → pingintel_api-1.4.11}/examples/ping-vision/send_and_track_submission.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/pingmaps/pingmaps_api_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pingintel_api-1.4.10 → pingintel_api-1.4.11}/src/pingintel_api/sov_fixer/sov_fixer_api_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|