boarddata 6.2.1__tar.gz → 6.2.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.2.1 → boarddata-6.2.2}/PKG-INFO +1 -1
- {boarddata-6.2.1 → boarddata-6.2.2}/__init__.py +1 -1
- {boarddata-6.2.1 → boarddata-6.2.2}/_persons.py +19 -3
- {boarddata-6.2.1 → boarddata-6.2.2}/boarddata.egg-info/PKG-INFO +1 -1
- {boarddata-6.2.1 → boarddata-6.2.2}/pyproject.toml +1 -1
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_persons.py +46 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/CLAUDE.md +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/README.md +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/_assemblies.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/_auditors.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/_base.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/_comex.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/_companies.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/_criteria.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/_directors.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/_documents.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/_esg.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/_indexes.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/_sentinel.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/_utilities.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/boarddata.egg-info/SOURCES.txt +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/boarddata.egg-info/dependency_links.txt +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/boarddata.egg-info/requires.txt +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/boarddata.egg-info/top_level.txt +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/cache.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/client.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/errors.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/py.typed +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/setup.cfg +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/__init__.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/conftest.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_assemblies.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_auditors.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_base.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_build_payload.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_cache.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_comex.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_companies.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_config.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_criteria.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_directors.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_documents.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_esg.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_exports.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_indexes.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_sentinel.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/tests/test_utilities.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/__init__.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/assemblies.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/auditors.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/comex.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/companies.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/core.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/criteria.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/directors.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/documents.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/esg.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/indexes.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/persons.py +0 -0
- {boarddata-6.2.1 → boarddata-6.2.2}/types/sentinel.py +0 -0
|
@@ -19,6 +19,7 @@ class PersonMixin:
|
|
|
19
19
|
*,
|
|
20
20
|
search: str | None = None,
|
|
21
21
|
similarity: str | None = None,
|
|
22
|
+
similarity_threshold: float | None = None,
|
|
22
23
|
gender: str | None = None,
|
|
23
24
|
nationality: str | None = None,
|
|
24
25
|
page: int | None = None,
|
|
@@ -29,6 +30,9 @@ class PersonMixin:
|
|
|
29
30
|
Args:
|
|
30
31
|
search: Search by first/last name.
|
|
31
32
|
similarity: Trigram similarity search (returns best matches by name).
|
|
33
|
+
similarity_threshold: Minimum trigram similarity (0.0–1.0). Only
|
|
34
|
+
applies when ``similarity`` is set. ``None`` uses the server
|
|
35
|
+
default (0.80).
|
|
32
36
|
gender: Filter by gender code.
|
|
33
37
|
nationality: Filter by nationality code(s), comma-separated.
|
|
34
38
|
page: Page number (1-indexed).
|
|
@@ -44,6 +48,7 @@ class PersonMixin:
|
|
|
44
48
|
"persons/",
|
|
45
49
|
search=search,
|
|
46
50
|
similarity=similarity,
|
|
51
|
+
similarity_threshold=similarity_threshold,
|
|
47
52
|
gender=gender,
|
|
48
53
|
nationality=nationality,
|
|
49
54
|
page=page,
|
|
@@ -261,11 +266,18 @@ class PersonMixin:
|
|
|
261
266
|
"""
|
|
262
267
|
return self._post("schools/", {"name": name, "country": country}) # type: ignore[attr-defined]
|
|
263
268
|
|
|
264
|
-
def find_person_by_similarity(
|
|
269
|
+
def find_person_by_similarity(
|
|
270
|
+
self,
|
|
271
|
+
full_name: str,
|
|
272
|
+
*,
|
|
273
|
+
similarity_threshold: float | None = None,
|
|
274
|
+
) -> PersonSimilarityItem | None:
|
|
265
275
|
"""Find the best-matching person by trigram similarity.
|
|
266
276
|
|
|
267
277
|
Args:
|
|
268
278
|
full_name: Full name to search for (e.g. ``"Jean Dupont"``).
|
|
279
|
+
similarity_threshold: Minimum trigram similarity (0.0–1.0).
|
|
280
|
+
``None`` uses the server default (0.80).
|
|
269
281
|
|
|
270
282
|
Returns:
|
|
271
283
|
PersonSimilarityItem for the best match, or ``None`` if no match found.
|
|
@@ -273,7 +285,7 @@ class PersonMixin:
|
|
|
273
285
|
Raises:
|
|
274
286
|
BoardDataError: On non-2xx API response.
|
|
275
287
|
"""
|
|
276
|
-
resp = self.list_persons(similarity=full_name)
|
|
288
|
+
resp = self.list_persons(similarity=full_name, similarity_threshold=similarity_threshold)
|
|
277
289
|
if isinstance(resp, dict) and "results" in resp:
|
|
278
290
|
results = resp["results"]
|
|
279
291
|
elif isinstance(resp, list):
|
|
@@ -295,6 +307,7 @@ class PersonMixin:
|
|
|
295
307
|
schools: list[int] | None = None,
|
|
296
308
|
professional_address: AddressPayload | None = None,
|
|
297
309
|
field_sources: list[FieldSourcePayload] | None = None,
|
|
310
|
+
similarity_threshold: float | None = None,
|
|
298
311
|
) -> UpsertResult:
|
|
299
312
|
"""Find person by similarity, create or update.
|
|
300
313
|
|
|
@@ -309,6 +322,9 @@ class PersonMixin:
|
|
|
309
322
|
schools: List of school IDs.
|
|
310
323
|
professional_address: Professional address.
|
|
311
324
|
field_sources: Document provenance for fields.
|
|
325
|
+
similarity_threshold: Minimum trigram similarity (0.0–1.0) for the
|
|
326
|
+
pre-write match step. ``None`` uses the server default (0.80).
|
|
327
|
+
Raise for strict ingestion; lower for loose dedup behavior.
|
|
312
328
|
|
|
313
329
|
Returns:
|
|
314
330
|
UpsertResult::
|
|
@@ -318,7 +334,7 @@ class PersonMixin:
|
|
|
318
334
|
Raises:
|
|
319
335
|
BoardDataError: On non-2xx API response.
|
|
320
336
|
"""
|
|
321
|
-
person = self.find_person_by_similarity(person_name)
|
|
337
|
+
person = self.find_person_by_similarity(person_name, similarity_threshold=similarity_threshold)
|
|
322
338
|
person_uid = person["id"] if person else None
|
|
323
339
|
|
|
324
340
|
payload = self._build_payload( # type: ignore[attr-defined]
|
|
@@ -28,6 +28,24 @@ class TestListPersons:
|
|
|
28
28
|
_, kwargs = c._request.call_args
|
|
29
29
|
assert kwargs["params"] == {"search": "Jean", "gender": "M", "page": 1}
|
|
30
30
|
|
|
31
|
+
def test_forwards_similarity_threshold(self):
|
|
32
|
+
c = make_client()
|
|
33
|
+
c._request.return_value = {"count": 0, "results": []}
|
|
34
|
+
c.list_persons(similarity="Jean Dupont", similarity_threshold=0.85)
|
|
35
|
+
_, kwargs = c._request.call_args
|
|
36
|
+
assert kwargs["params"] == {
|
|
37
|
+
"similarity": "Jean Dupont",
|
|
38
|
+
"similarity_threshold": 0.85,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
def test_omits_similarity_threshold_when_none(self):
|
|
42
|
+
c = make_client()
|
|
43
|
+
c._request.return_value = {"count": 0, "results": []}
|
|
44
|
+
c.list_persons(similarity="Jean Dupont")
|
|
45
|
+
_, kwargs = c._request.call_args
|
|
46
|
+
assert kwargs["params"] == {"similarity": "Jean Dupont"}
|
|
47
|
+
assert "similarity_threshold" not in kwargs["params"]
|
|
48
|
+
|
|
31
49
|
|
|
32
50
|
class TestFindPersonBySimilarity:
|
|
33
51
|
def test_returns_best_match(self):
|
|
@@ -41,6 +59,16 @@ class TestFindPersonBySimilarity:
|
|
|
41
59
|
c._request.return_value = {"count": 0, "results": []}
|
|
42
60
|
assert c.find_person_by_similarity("Unknown") is None
|
|
43
61
|
|
|
62
|
+
def test_forwards_similarity_threshold(self):
|
|
63
|
+
c = make_client()
|
|
64
|
+
c._request.return_value = {"count": 0, "results": []}
|
|
65
|
+
c.find_person_by_similarity("Jean Dupont", similarity_threshold=0.9)
|
|
66
|
+
_, kwargs = c._request.call_args
|
|
67
|
+
assert kwargs["params"] == {
|
|
68
|
+
"similarity": "Jean Dupont",
|
|
69
|
+
"similarity_threshold": 0.9,
|
|
70
|
+
}
|
|
71
|
+
|
|
44
72
|
|
|
45
73
|
class TestUpsertPerson:
|
|
46
74
|
def test_creates_when_not_found(self):
|
|
@@ -62,3 +90,21 @@ class TestUpsertPerson:
|
|
|
62
90
|
result = c.upsert_person("Jean Dupont", first_name="Jean", last_name="Dupont")
|
|
63
91
|
assert result["action"] == "updated"
|
|
64
92
|
assert result["id"] == "existing"
|
|
93
|
+
|
|
94
|
+
def test_forwards_similarity_threshold_to_lookup(self):
|
|
95
|
+
c = make_client()
|
|
96
|
+
c._request.side_effect = [
|
|
97
|
+
{"count": 0, "results": []}, # similarity search
|
|
98
|
+
{"id": "new-uuid", "first_name": "Jean", "last_name": "Dupont"}, # create
|
|
99
|
+
]
|
|
100
|
+
c.upsert_person(
|
|
101
|
+
"Jean Dupont",
|
|
102
|
+
first_name="Jean",
|
|
103
|
+
last_name="Dupont",
|
|
104
|
+
similarity_threshold=0.95,
|
|
105
|
+
)
|
|
106
|
+
lookup_call = c._request.call_args_list[0]
|
|
107
|
+
assert lookup_call.kwargs["params"] == {
|
|
108
|
+
"similarity": "Jean Dupont",
|
|
109
|
+
"similarity_threshold": 0.95,
|
|
110
|
+
}
|
|
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
|