pingintel-api 1.4.7__tar.gz → 1.4.9__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.
Files changed (59) hide show
  1. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/PKG-INFO +1 -1
  2. pingintel_api-1.4.9/examples/close_submissions.py +129 -0
  3. pingintel_api-1.4.9/src/pingintel_api/__about__.py +1 -0
  4. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/pingvision/pingvision_api_client.py +1 -1
  5. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/sov_fixer/sov_fixer_api_client.py +5 -0
  6. pingintel_api-1.4.7/src/pingintel_api/__about__.py +0 -1
  7. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/.claude/settings.json +0 -0
  8. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/.claude/settings.local.json +0 -0
  9. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/.gitignore +0 -0
  10. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/.pre-commit-config.yaml +0 -0
  11. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/LICENSE +0 -0
  12. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/README.md +0 -0
  13. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/SOV10 2026-02_Acc-STG.txt +0 -0
  14. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/Untitled-9.json5 +0 -0
  15. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/assignment_queue.xlsx +0 -0
  16. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/completed_submissions.xlsx +0 -0
  17. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/data/demo_email.eml +0 -0
  18. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/data/parse_sov_testfile.xlsx +0 -0
  19. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/find-amwins-missing-account-numbers.py +0 -0
  20. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/ping-data/enhance_multiple_locations.py +0 -0
  21. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/ping-data/enhance_one_location.py +0 -0
  22. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/ping-data/pingdata_enhance.py +0 -0
  23. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/ping-extraction/parse_sov.py +0 -0
  24. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/ping-extraction/sovfixer_fix_sov.py +0 -0
  25. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/ping-extraction/sovfixer_list_activity.py +0 -0
  26. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/ping-extraction/sovfixer_reoutput_sov.py +0 -0
  27. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/ping-vision/clear_and_triage_api.py +0 -0
  28. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/ping-vision/pingvision_activity.py +0 -0
  29. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/ping-vision/pingvision_clear_and_triage.py +0 -0
  30. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/ping-vision/send_and_track_submission.py +0 -0
  31. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/pingvision_assignment.py +0 -0
  32. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/pingvision_declination_flow.py +0 -0
  33. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/sovfixer_list_history.py +0 -0
  34. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/sovfixer_list_history_ping_None_1_run2.csv +0 -0
  35. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/sovfixerapi +0 -0
  36. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/test_reoutput_locations.csv +0 -0
  37. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/examples/test_sov.xlsx +0 -0
  38. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/generate_docs.py +0 -0
  39. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/justfile +0 -0
  40. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/output-data-f432c75e-b40e-11f0-9e68-3c58c2f91432-1.json +0 -0
  41. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/pyproject.toml +0 -0
  42. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/requirements.txt +0 -0
  43. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/__init__.py +0 -0
  44. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/api_client_base.py +0 -0
  45. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/common_types.py +0 -0
  46. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/constants.py +0 -0
  47. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/pingdata/pingdata_api_client.py +0 -0
  48. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/pingdata/types.py +0 -0
  49. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/pingdataapi_cmd.py +0 -0
  50. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/pingmaps/pingmaps_api_client.py +0 -0
  51. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/pingmaps/types.py +0 -0
  52. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/pingmapsapi_cmd.py +0 -0
  53. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/pingvision/types.py +0 -0
  54. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/pingvisionapi_cmd.py +0 -0
  55. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/sov_fixer/types.py +0 -0
  56. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/sovfixerapi_cmd.py +0 -0
  57. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/url_signature_factory.py +0 -0
  58. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/src/pingintel_api/utils.py +0 -0
  59. {pingintel_api-1.4.7 → pingintel_api-1.4.9}/tests/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pingintel-api
3
- Version: 1.4.7
3
+ Version: 1.4.9
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
@@ -0,0 +1,129 @@
1
+ """
2
+ Set one or more PingVision submissions to a given workflow status.
3
+
4
+ Accepts pingids (e.g. p-ob-amw-agtdgc) or client_refs. Identifiers starting
5
+ with "p-" are treated as pingids; all others are looked up as client_refs.
6
+
7
+ Usage:
8
+ python close_submissions.py <id1> [id2 ...] --status <name> [options]
9
+
10
+ python close_submissions.py p-ob-amw-abc123 my-client-ref-456 --status Closed
11
+ python close_submissions.py p-ob-amw-abc123 --status Declined --env staging --dry-run
12
+ """
13
+
14
+ import argparse
15
+ import sys
16
+
17
+ from pingintel_api import PingVisionAPIClient
18
+
19
+
20
+ def resolve_pingids(client: PingVisionAPIClient, identifiers: list[str]) -> dict[str, str | None]:
21
+ """Return {identifier: pingid} for all identifiers. pingid is None if not found."""
22
+ result = {}
23
+ for ident in identifiers:
24
+ if ident.startswith("p-"):
25
+ result[ident] = ident
26
+ else:
27
+ activity = client.list_submission_activity(client_ref=ident, page_size=10)
28
+ submissions = activity.get("results", [])
29
+ if not submissions:
30
+ print(f" WARNING: No submission found for client_ref={ident!r}", file=sys.stderr)
31
+ result[ident] = None
32
+ elif len(submissions) > 1:
33
+ pingids = [s["id"] for s in submissions]
34
+ print(
35
+ f" WARNING: Multiple submissions found for client_ref={ident!r}: {pingids}. Skipping.",
36
+ file=sys.stderr,
37
+ )
38
+ result[ident] = None
39
+ else:
40
+ result[ident] = submissions[0]["id"]
41
+ return result
42
+
43
+
44
+ def find_status_uuid(client: PingVisionAPIClient, division_uuid: str, status_name: str) -> str:
45
+ statuses = client.list_submission_statuses(division=division_uuid)
46
+ matched = [s for s in statuses if s["name"].lower() == status_name.lower()]
47
+ if not matched:
48
+ available = [s["name"] for s in statuses]
49
+ raise RuntimeError(f"Status {status_name!r} not found. Available: {available}")
50
+ return matched[0]["uuid"]
51
+
52
+
53
+ def find_division_uuid(client: PingVisionAPIClient, team_name: str | None, division: str | None) -> str:
54
+ teams = client.list_teams()
55
+ if not teams:
56
+ raise RuntimeError("No teams found for this API token.")
57
+ if division:
58
+ matched = [
59
+ t for t in teams
60
+ if t["division_uuid"] == division
61
+ or t.get("division_name", "").lower() == division.lower()
62
+ or t.get("division_short_name", "").lower() == division.lower()
63
+ ]
64
+ if not matched:
65
+ available = [(t.get("division_short_name") or t["division_name"], t["division_uuid"]) for t in teams]
66
+ raise RuntimeError(f"Division {division!r} not found. Available: {available}")
67
+ return matched[0]["division_uuid"]
68
+ if team_name:
69
+ matched = [t for t in teams if t["team_name"] == team_name]
70
+ if not matched:
71
+ available = [t["team_name"] for t in teams]
72
+ raise RuntimeError(f"Team {team_name!r} not found. Available: {available}")
73
+ return matched[0]["division_uuid"]
74
+ if len(teams) == 1:
75
+ return teams[0]["division_uuid"]
76
+ names = [t["team_name"] for t in teams]
77
+ raise RuntimeError(f"Multiple teams found: {names}. Specify one with --team or --division.")
78
+
79
+
80
+ def main():
81
+ parser = argparse.ArgumentParser(description="Set PingVision submission workflow status.")
82
+ parser.add_argument("identifiers", nargs="+", help="pingids or client_refs to update")
83
+ parser.add_argument("--status", required=True, help="Target workflow status name (e.g. Closed, Declined)")
84
+ parser.add_argument("--env", default="prod", help="API environment (prod, staging, local). Default: prod")
85
+ parser.add_argument("--api-url", help="Override API URL (e.g. http://localhost:8002)")
86
+ parser.add_argument("--team", help="Team name for status lookup (required if multiple teams)")
87
+ parser.add_argument("--division", help="Division UUID (alternative to --team)")
88
+ parser.add_argument("--dry-run", action="store_true", help="Print what would happen without making changes")
89
+ args = parser.parse_args()
90
+
91
+ kwargs = {}
92
+ if args.api_url:
93
+ kwargs["api_url"] = args.api_url
94
+ else:
95
+ kwargs["environment"] = args.env
96
+
97
+ client = PingVisionAPIClient(**kwargs)
98
+
99
+ division_uuid = find_division_uuid(client, args.team, args.division)
100
+ status_uuid = find_status_uuid(client, division_uuid, args.status)
101
+ print(f"Status {args.status!r} UUID: {status_uuid}")
102
+
103
+ id_map = resolve_pingids(client, args.identifiers)
104
+
105
+ success, skipped, failed = 0, 0, 0
106
+ for ident, pingid in id_map.items():
107
+ if pingid is None:
108
+ skipped += 1
109
+ continue
110
+ label = pingid if ident == pingid else f"{pingid} (client_ref={ident!r})"
111
+ if args.dry_run:
112
+ print(f" DRY RUN: would set {label} -> {args.status!r}")
113
+ success += 1
114
+ continue
115
+ try:
116
+ client.change_status(pingid=pingid, workflow_status_id=status_uuid)
117
+ print(f" Updated {label} -> {args.status!r}")
118
+ success += 1
119
+ except Exception as e:
120
+ print(f" ERROR updating {label}: {e}", file=sys.stderr)
121
+ failed += 1
122
+
123
+ print(f"\nDone. {success} updated, {skipped} skipped, {failed} failed.")
124
+ if failed:
125
+ sys.exit(1)
126
+
127
+
128
+ if __name__ == "__main__":
129
+ main()
@@ -0,0 +1 @@
1
+ __version__ = "1.4.9"
@@ -182,7 +182,7 @@ class PingVisionAPIClient(APIClientBase):
182
182
  else:
183
183
  url = self.api_url + document_url
184
184
 
185
- assert url.startswith(self.api_url), f"document_url should start with {self.api_url} or / but got {url}"
185
+ # assert url.startswith(self.api_url), f"document_url should start with {self.api_url} or / but got {url}"
186
186
 
187
187
  response = self.get(url)
188
188
  raise_for_status(response)
@@ -624,6 +624,7 @@ class SOVFixerAPIClient(APIClientBase):
624
624
  revision: int = -1,
625
625
  overwrite_existing: bool = False,
626
626
  delegate_to_team: UUID | str | int | None = None,
627
+ **kwargs,
627
628
  ):
628
629
  url = self.api_url + f"/api/v1/sov/{sovid_or_sud}/get_or_create_output"
629
630
  data = {}
@@ -635,6 +636,8 @@ class SOVFixerAPIClient(APIClientBase):
635
636
  data["overwrite_existing"] = overwrite_existing
636
637
  if delegate_to_team is not None:
637
638
  data["delegate_to_team"] = delegate_to_team
639
+ if kwargs:
640
+ data.update(kwargs)
638
641
 
639
642
  response = self.post(url, data=data)
640
643
  raise_for_status(response)
@@ -654,6 +657,7 @@ class SOVFixerAPIClient(APIClientBase):
654
657
  overwrite_existing=False,
655
658
  timeout: timedelta | None = timedelta(minutes=5),
656
659
  delegate_to_team: UUID | str | int | None = None,
660
+ get_or_create_output_async_start_kwargs=None,
657
661
  ) -> t.OutputData:
658
662
  """Synchronously get or create an output from a SOV Fixer request. If it exists, it will return immediately.
659
663
  If it does not exist, it will start the generation process and poll for completion, then return it."""
@@ -665,6 +669,7 @@ class SOVFixerAPIClient(APIClientBase):
665
669
  revision,
666
670
  overwrite_existing,
667
671
  delegate_to_team,
672
+ **(get_or_create_output_async_start_kwargs or {}),
668
673
  )
669
674
 
670
675
  request_status = start_response["request"]["status"]
@@ -1 +0,0 @@
1
- __version__ = "1.4.7"
File without changes
File without changes
File without changes
File without changes