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.
Files changed (88) hide show
  1. {boarddata-6.7.1 → boarddata-6.7.2}/PKG-INFO +1 -1
  2. {boarddata-6.7.1 → boarddata-6.7.2}/__init__.py +1 -1
  3. {boarddata-6.7.1 → boarddata-6.7.2}/_assemblies.py +19 -0
  4. {boarddata-6.7.1 → boarddata-6.7.2}/boarddata.egg-info/PKG-INFO +1 -1
  5. {boarddata-6.7.1 → boarddata-6.7.2}/dq/apply.py +24 -9
  6. {boarddata-6.7.1 → boarddata-6.7.2}/pyproject.toml +1 -1
  7. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_assemblies.py +26 -0
  8. {boarddata-6.7.1 → boarddata-6.7.2}/types/assemblies.py +12 -0
  9. {boarddata-6.7.1 → boarddata-6.7.2}/CLAUDE.md +0 -0
  10. {boarddata-6.7.1 → boarddata-6.7.2}/README.md +0 -0
  11. {boarddata-6.7.1 → boarddata-6.7.2}/_auditors.py +0 -0
  12. {boarddata-6.7.1 → boarddata-6.7.2}/_base.py +0 -0
  13. {boarddata-6.7.1 → boarddata-6.7.2}/_comex.py +0 -0
  14. {boarddata-6.7.1 → boarddata-6.7.2}/_committees.py +0 -0
  15. {boarddata-6.7.1 → boarddata-6.7.2}/_companies.py +0 -0
  16. {boarddata-6.7.1 → boarddata-6.7.2}/_criteria.py +0 -0
  17. {boarddata-6.7.1 → boarddata-6.7.2}/_directors.py +0 -0
  18. {boarddata-6.7.1 → boarddata-6.7.2}/_documents.py +0 -0
  19. {boarddata-6.7.1 → boarddata-6.7.2}/_esg.py +0 -0
  20. {boarddata-6.7.1 → boarddata-6.7.2}/_indexes.py +0 -0
  21. {boarddata-6.7.1 → boarddata-6.7.2}/_persons.py +0 -0
  22. {boarddata-6.7.1 → boarddata-6.7.2}/_risks.py +0 -0
  23. {boarddata-6.7.1 → boarddata-6.7.2}/_sentinel.py +0 -0
  24. {boarddata-6.7.1 → boarddata-6.7.2}/_utilities.py +0 -0
  25. {boarddata-6.7.1 → boarddata-6.7.2}/boarddata.egg-info/SOURCES.txt +0 -0
  26. {boarddata-6.7.1 → boarddata-6.7.2}/boarddata.egg-info/dependency_links.txt +0 -0
  27. {boarddata-6.7.1 → boarddata-6.7.2}/boarddata.egg-info/requires.txt +0 -0
  28. {boarddata-6.7.1 → boarddata-6.7.2}/boarddata.egg-info/top_level.txt +0 -0
  29. {boarddata-6.7.1 → boarddata-6.7.2}/cache.py +0 -0
  30. {boarddata-6.7.1 → boarddata-6.7.2}/client.py +0 -0
  31. {boarddata-6.7.1 → boarddata-6.7.2}/dq/__init__.py +0 -0
  32. {boarddata-6.7.1 → boarddata-6.7.2}/dq/adjudicate.py +0 -0
  33. {boarddata-6.7.1 → boarddata-6.7.2}/dq/context.py +0 -0
  34. {boarddata-6.7.1 → boarddata-6.7.2}/dq/db.py +0 -0
  35. {boarddata-6.7.1 → boarddata-6.7.2}/dq/domains.py +0 -0
  36. {boarddata-6.7.1 → boarddata-6.7.2}/dq/models.py +0 -0
  37. {boarddata-6.7.1 → boarddata-6.7.2}/dq/oracle.py +0 -0
  38. {boarddata-6.7.1 → boarddata-6.7.2}/dq/registry.py +0 -0
  39. {boarddata-6.7.1 → boarddata-6.7.2}/dq/report.py +0 -0
  40. {boarddata-6.7.1 → boarddata-6.7.2}/dq/rules/__init__.py +0 -0
  41. {boarddata-6.7.1 → boarddata-6.7.2}/dq/rules/cross_company.py +0 -0
  42. {boarddata-6.7.1 → boarddata-6.7.2}/dq/rules/lexical.py +0 -0
  43. {boarddata-6.7.1 → boarddata-6.7.2}/dq/s3.py +0 -0
  44. {boarddata-6.7.1 → boarddata-6.7.2}/dq/verify_s3.py +0 -0
  45. {boarddata-6.7.1 → boarddata-6.7.2}/errors.py +0 -0
  46. {boarddata-6.7.1 → boarddata-6.7.2}/py.typed +0 -0
  47. {boarddata-6.7.1 → boarddata-6.7.2}/scripts/__init__.py +0 -0
  48. {boarddata-6.7.1 → boarddata-6.7.2}/scripts/dq_apply.py +0 -0
  49. {boarddata-6.7.1 → boarddata-6.7.2}/scripts/dq_batch.py +0 -0
  50. {boarddata-6.7.1 → boarddata-6.7.2}/scripts/dq_plan.py +0 -0
  51. {boarddata-6.7.1 → boarddata-6.7.2}/scripts/dq_revert.py +0 -0
  52. {boarddata-6.7.1 → boarddata-6.7.2}/scripts/dq_scan.py +0 -0
  53. {boarddata-6.7.1 → boarddata-6.7.2}/scripts/dq_verify_s3.py +0 -0
  54. {boarddata-6.7.1 → boarddata-6.7.2}/setup.cfg +0 -0
  55. {boarddata-6.7.1 → boarddata-6.7.2}/tests/__init__.py +0 -0
  56. {boarddata-6.7.1 → boarddata-6.7.2}/tests/conftest.py +0 -0
  57. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_auditors.py +0 -0
  58. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_base.py +0 -0
  59. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_build_payload.py +0 -0
  60. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_cache.py +0 -0
  61. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_comex.py +0 -0
  62. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_committees.py +0 -0
  63. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_companies.py +0 -0
  64. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_config.py +0 -0
  65. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_criteria.py +0 -0
  66. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_directors.py +0 -0
  67. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_documents.py +0 -0
  68. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_esg.py +0 -0
  69. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_exports.py +0 -0
  70. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_indexes.py +0 -0
  71. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_persons.py +0 -0
  72. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_risks.py +0 -0
  73. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_sentinel.py +0 -0
  74. {boarddata-6.7.1 → boarddata-6.7.2}/tests/test_utilities.py +0 -0
  75. {boarddata-6.7.1 → boarddata-6.7.2}/types/__init__.py +0 -0
  76. {boarddata-6.7.1 → boarddata-6.7.2}/types/auditors.py +0 -0
  77. {boarddata-6.7.1 → boarddata-6.7.2}/types/comex.py +0 -0
  78. {boarddata-6.7.1 → boarddata-6.7.2}/types/committees.py +0 -0
  79. {boarddata-6.7.1 → boarddata-6.7.2}/types/companies.py +0 -0
  80. {boarddata-6.7.1 → boarddata-6.7.2}/types/core.py +0 -0
  81. {boarddata-6.7.1 → boarddata-6.7.2}/types/criteria.py +0 -0
  82. {boarddata-6.7.1 → boarddata-6.7.2}/types/directors.py +0 -0
  83. {boarddata-6.7.1 → boarddata-6.7.2}/types/documents.py +0 -0
  84. {boarddata-6.7.1 → boarddata-6.7.2}/types/esg.py +0 -0
  85. {boarddata-6.7.1 → boarddata-6.7.2}/types/indexes.py +0 -0
  86. {boarddata-6.7.1 → boarddata-6.7.2}/types/persons.py +0 -0
  87. {boarddata-6.7.1 → boarddata-6.7.2}/types/risks.py +0 -0
  88. {boarddata-6.7.1 → boarddata-6.7.2}/types/sentinel.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boarddata
3
- Version: 6.7.1
3
+ Version: 6.7.2
4
4
  Summary: Python SDK for the BoardData V2 REST API
5
5
  License: MIT
6
6
  Classifier: Development Status :: 4 - Beta
@@ -6,7 +6,7 @@ from .cache import FileTokenCache, ScalewaySecretTokenCache
6
6
  from .client import BoardDataClient
7
7
  from .errors import BoardDataError, ExportTooLargeError
8
8
 
9
- __version__ = "6.7.1"
9
+ __version__ = "6.7.2"
10
10
  __all__ = [
11
11
  "Base",
12
12
  "BoardDataClient",
@@ -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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: boarddata
3
- Version: 6.7.1
3
+ Version: 6.7.2
4
4
  Summary: Python SDK for the BoardData V2 REST API
5
5
  License: MIT
6
6
  Classifier: Development Status :: 4 - Beta
@@ -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`` — partial. The document row is recreated from a full pre-delete
11
- snapshot, but its assembly↔document link(s) cannot be restored through the
12
- current API (the link is read-only / has no create endpoint). The links are
13
- recorded in the manifest so the gap is explicit, never silent.
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) and not API-restorable record them
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": "partial",
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": "partial", # doc recreated (new id); assembly links not restored
162
- "new_document_id": new.get("id"),
163
- "unrestored_assembly_links": list(entry.get("assembly_links", [])),
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}")
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "boarddata"
7
- version = "6.7.1"
7
+ version = "6.7.2"
8
8
  description = "Python SDK for the BoardData V2 REST API"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -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