boarddata 6.7.1__tar.gz → 6.7.2__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.
- {boarddata-6.7.1 → boarddata-6.7.2}/PKG-INFO +1 -1
- {boarddata-6.7.1 → boarddata-6.7.2}/__init__.py +1 -1
- {boarddata-6.7.1 → boarddata-6.7.2}/_assemblies.py +19 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/boarddata.egg-info/PKG-INFO +1 -1
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/apply.py +24 -9
- {boarddata-6.7.1 → boarddata-6.7.2}/pyproject.toml +1 -1
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_assemblies.py +26 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/assemblies.py +12 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/CLAUDE.md +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/README.md +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_auditors.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_base.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_comex.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_committees.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_companies.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_criteria.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_directors.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_documents.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_esg.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_indexes.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_persons.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_risks.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_sentinel.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/_utilities.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/boarddata.egg-info/SOURCES.txt +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/boarddata.egg-info/dependency_links.txt +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/boarddata.egg-info/requires.txt +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/boarddata.egg-info/top_level.txt +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/cache.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/client.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/__init__.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/adjudicate.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/context.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/db.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/domains.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/models.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/oracle.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/registry.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/report.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/rules/__init__.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/rules/cross_company.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/rules/lexical.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/s3.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/dq/verify_s3.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/errors.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/py.typed +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/scripts/__init__.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/scripts/dq_apply.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/scripts/dq_batch.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/scripts/dq_plan.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/scripts/dq_revert.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/scripts/dq_scan.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/scripts/dq_verify_s3.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/setup.cfg +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/__init__.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/conftest.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_auditors.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_base.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_build_payload.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_cache.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_comex.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_committees.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_companies.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_config.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_criteria.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_directors.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_documents.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_esg.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_exports.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_indexes.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_persons.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_risks.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_sentinel.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_utilities.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/__init__.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/auditors.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/comex.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/committees.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/companies.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/core.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/criteria.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/directors.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/documents.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/esg.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/indexes.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/persons.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/risks.py +0 -0
- {boarddata-6.7.1 → boarddata-6.7.2}/types/sentinel.py +0 -0
|
@@ -6,6 +6,7 @@ from typing import Any
|
|
|
6
6
|
|
|
7
7
|
from .types.assemblies import (
|
|
8
8
|
AssemblyDetail,
|
|
9
|
+
AssemblyDocumentLink,
|
|
9
10
|
AssemblyExportItem,
|
|
10
11
|
CreatePresentationPayload,
|
|
11
12
|
CreateQuestionPayload,
|
|
@@ -456,6 +457,24 @@ class AssemblyMixin:
|
|
|
456
457
|
"""Delete a question."""
|
|
457
458
|
self._delete(f"assemblies/{assembly_uid}/questions/{pk}/") # type: ignore[attr-defined]
|
|
458
459
|
|
|
460
|
+
# --- Assembly document links ---
|
|
461
|
+
def link_assembly_document(
|
|
462
|
+
self, assembly_uid: str, document_id: str, category: str, doc_type: str
|
|
463
|
+
) -> AssemblyDocumentLink:
|
|
464
|
+
"""Link a document to an assembly (idempotent; returns the link row).
|
|
465
|
+
|
|
466
|
+
The backend refuses cross-company links: ``document`` must belong to the
|
|
467
|
+
assembly's company.
|
|
468
|
+
"""
|
|
469
|
+
return self._post( # type: ignore[attr-defined]
|
|
470
|
+
f"assemblies/{assembly_uid}/documents/",
|
|
471
|
+
{"document_id": document_id, "category": category, "doc_type": doc_type},
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
def unlink_assembly_document(self, assembly_uid: str, link_id: str) -> None:
|
|
475
|
+
"""Delete an assembly-document link (the Document itself is untouched)."""
|
|
476
|
+
self._delete(f"assemblies/{assembly_uid}/documents/{link_id}/") # type: ignore[attr-defined]
|
|
477
|
+
|
|
459
478
|
# -- Resolutions --
|
|
460
479
|
|
|
461
480
|
def list_resolutions(self, **params: Any) -> PaginatedResponse:
|
|
@@ -7,10 +7,10 @@ the pghistory audit trail. There is no direct DB write anywhere here.
|
|
|
7
7
|
|
|
8
8
|
Reversibility, by action:
|
|
9
9
|
* ``repoint`` — full. Restore the document's previous ISIN.
|
|
10
|
-
* ``delete`` —
|
|
11
|
-
snapshot,
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
* ``delete`` — full. The document row is recreated from a full pre-delete
|
|
11
|
+
snapshot, and its assembly↔document link(s) are re-created via
|
|
12
|
+
``link_assembly_document``. A link that fails to restore (e.g. a
|
|
13
|
+
cross-company refusal) is recorded rather than aborting the revert.
|
|
14
14
|
* ``merge`` — refused. Destructive and not yet revertible; ``apply`` raises
|
|
15
15
|
rather than perform an irreversible operation.
|
|
16
16
|
"""
|
|
@@ -54,6 +54,9 @@ class FixClient(Protocol):
|
|
|
54
54
|
def update_document(self, uid: str, **kwargs: Any) -> dict[str, Any]: ...
|
|
55
55
|
def delete_document(self, uid: str, *, cascade_field_sources: bool = False) -> None: ...
|
|
56
56
|
def merge_document(self, uid: str, *, into: str) -> dict[str, Any]: ...
|
|
57
|
+
def link_assembly_document(
|
|
58
|
+
self, assembly_uid: str, document_id: str, category: str, doc_type: str
|
|
59
|
+
) -> dict[str, Any]: ...
|
|
57
60
|
|
|
58
61
|
|
|
59
62
|
def _snapshot(client: FixClient, uid: str) -> dict[str, Any]:
|
|
@@ -78,9 +81,9 @@ def apply_action(action: FixAction, client: FixClient) -> dict[str, Any] | None:
|
|
|
78
81
|
"action": "delete",
|
|
79
82
|
"document_id": doc_id,
|
|
80
83
|
"before": before,
|
|
81
|
-
# links are gone (backend cascade)
|
|
84
|
+
# links are gone (backend cascade); recorded so revert can re-create them
|
|
82
85
|
"assembly_links": list(action.before_state.get("assembly_links", [])),
|
|
83
|
-
"reversible": "
|
|
86
|
+
"reversible": "full",
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
if action.action == "repoint":
|
|
@@ -155,12 +158,24 @@ def revert_entry(entry: dict[str, Any], client: FixClient) -> dict[str, Any]:
|
|
|
155
158
|
url=before.get("url"),
|
|
156
159
|
original_url=before.get("original_url"),
|
|
157
160
|
)
|
|
161
|
+
new_id = new.get("id")
|
|
162
|
+
restored: list[dict[str, Any]] = []
|
|
163
|
+
unrestored: list[dict[str, Any]] = []
|
|
164
|
+
for link in entry.get("assembly_links", []):
|
|
165
|
+
try:
|
|
166
|
+
client.link_assembly_document(
|
|
167
|
+
link["assembly_id"], new_id, link.get("category", ""), link.get("doc_type", "")
|
|
168
|
+
)
|
|
169
|
+
restored.append(link)
|
|
170
|
+
except Exception as exc: # noqa: BLE001 — best-effort restore; record, never abort the revert
|
|
171
|
+
unrestored.append({**link, "error": str(exc)})
|
|
158
172
|
return {
|
|
159
173
|
"document_id": doc_id,
|
|
160
174
|
"reverted": "delete",
|
|
161
|
-
"status": "
|
|
162
|
-
"new_document_id":
|
|
163
|
-
"
|
|
175
|
+
"status": "ok" if not unrestored else "partial",
|
|
176
|
+
"new_document_id": new_id,
|
|
177
|
+
"restored_assembly_links": restored,
|
|
178
|
+
"unrestored_assembly_links": unrestored,
|
|
164
179
|
}
|
|
165
180
|
|
|
166
181
|
raise ApplyError(f"cannot revert action {act!r} for document {doc_id}")
|
|
@@ -161,3 +161,29 @@ class TestListPublishedAssemblies:
|
|
|
161
161
|
c.list_published_assemblies(page=2, page_size=500)
|
|
162
162
|
_, kwargs = c._request.call_args
|
|
163
163
|
assert kwargs["params"] == {"page": 2, "page_size": 500}
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
class TestLinkAssemblyDocument:
|
|
167
|
+
def test_posts_link_payload(self):
|
|
168
|
+
c = make_client()
|
|
169
|
+
c._request.return_value = {
|
|
170
|
+
"id": "link1",
|
|
171
|
+
"document_id": "doc1",
|
|
172
|
+
"url": "k.pdf",
|
|
173
|
+
"title": "Notice",
|
|
174
|
+
"document_type": "notice_of_gm",
|
|
175
|
+
"category": "ODJ",
|
|
176
|
+
"doc_type": "NOTICE",
|
|
177
|
+
}
|
|
178
|
+
out = c.link_assembly_document("asm1", "doc1", "ODJ", "NOTICE")
|
|
179
|
+
args, kwargs = c._request.call_args
|
|
180
|
+
assert args == ("POST", "assemblies/asm1/documents/")
|
|
181
|
+
assert kwargs["json"] == {"document_id": "doc1", "category": "ODJ", "doc_type": "NOTICE"}
|
|
182
|
+
assert out["id"] == "link1"
|
|
183
|
+
|
|
184
|
+
def test_unlink_issues_delete(self):
|
|
185
|
+
c = make_client()
|
|
186
|
+
c._request.return_value = None
|
|
187
|
+
c.unlink_assembly_document("asm1", "link1")
|
|
188
|
+
args, _ = c._request.call_args
|
|
189
|
+
assert args == ("DELETE", "assemblies/asm1/documents/link1/")
|
|
@@ -248,6 +248,18 @@ class _AssemblyDocumentInline(TypedDict):
|
|
|
248
248
|
doc_type: str
|
|
249
249
|
|
|
250
250
|
|
|
251
|
+
class AssemblyDocumentLink(TypedDict):
|
|
252
|
+
"""A document↔assembly link row (the join shown on an AGM page)."""
|
|
253
|
+
|
|
254
|
+
id: str
|
|
255
|
+
document_id: str
|
|
256
|
+
url: str
|
|
257
|
+
title: str
|
|
258
|
+
document_type: str
|
|
259
|
+
category: str
|
|
260
|
+
doc_type: str
|
|
261
|
+
|
|
262
|
+
|
|
251
263
|
# -- Assemblies --
|
|
252
264
|
|
|
253
265
|
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|