followthemoney 4.2.2__py3-none-any.whl → 4.3.1__py3-none-any.whl

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.

Potentially problematic release.


This version of followthemoney might be problematic. Click here for more details.

@@ -9,7 +9,7 @@ from followthemoney.statement import Statement, StatementEntity, SE
9
9
  from followthemoney.dataset import Dataset, DefaultDataset, DS
10
10
  from followthemoney.util import set_model_locale
11
11
 
12
- __version__ = "4.2.2"
12
+ __version__ = "4.3.1"
13
13
 
14
14
  # Data model singleton
15
15
  model = Model.instance()
followthemoney/compare.py CHANGED
@@ -71,12 +71,31 @@ def _compare(scores: Scores, weights: Weights, n_std: int = 1) -> float:
71
71
  return 1.0 / (1.0 + math.exp(-prob))
72
72
 
73
73
 
74
+ def entity_is_same(left: EntityProxy, right: EntityProxy) -> bool:
75
+ """Check if two entities are the same apart from their ID."""
76
+ if left.schema != right.schema:
77
+ return False
78
+
79
+ props = set(left.properties.keys()).union(right.properties.keys())
80
+ if 0 == len(props):
81
+ return False
82
+
83
+ for prop in props:
84
+ left_vals = sorted(left.get(prop))
85
+ right_vals = sorted(right.get(prop))
86
+ if left_vals != right_vals:
87
+ return False
88
+ return True
89
+
90
+
74
91
  def compare(
75
92
  left: EntityProxy,
76
93
  right: EntityProxy,
77
94
  weights: Weights = COMPARE_WEIGHTS,
78
95
  ) -> float:
79
96
  """Compare two entities and return a match score."""
97
+ if entity_is_same(left, right):
98
+ return 1.0
80
99
  scores = compare_scores(left, right)
81
100
  return _compare(scores, weights)
82
101
 
followthemoney/helpers.py CHANGED
@@ -13,8 +13,7 @@ from itertools import product
13
13
  from datetime import datetime, timedelta
14
14
 
15
15
  from followthemoney.types import registry
16
- from followthemoney.proxy import E
17
- from followthemoney.util import join_text
16
+ from followthemoney.proxy import E, EntityProxy
18
17
 
19
18
  PROV_MIN_DATES = ("createdAt", "authoredAt", "publishedAt")
20
19
  PROV_MAX_DATES = ("modifiedAt", "retrievedAt")
@@ -47,7 +46,7 @@ def simplify_provenance(proxy: E) -> E:
47
46
 
48
47
 
49
48
  def entity_filename(
50
- proxy: E, base_name: Optional[str] = None, extension: Optional[str] = None
49
+ proxy: EntityProxy, base_name: Optional[str] = None, extension: Optional[str] = None
51
50
  ) -> Optional[str]:
52
51
  """Derive a safe filename for the given entity."""
53
52
  if proxy.schema.is_a("Document"):
@@ -85,7 +84,7 @@ def name_entity(entity: E) -> E:
85
84
 
86
85
 
87
86
  def check_person_cutoff(
88
- entity: E,
87
+ entity: EntityProxy,
89
88
  death_cutoff: datetime = datetime(2000, 1, 1),
90
89
  birth_cutoff: Optional[datetime] = None,
91
90
  ) -> bool:
@@ -153,17 +152,17 @@ def combine_names(entity: E) -> E:
153
152
  This is of course impossible to do culturally correctly for the whole planet at
154
153
  once, so it should be mostly used for internal-facing (e.g. matching) processes."""
155
154
  if entity.schema.is_a("Person"):
156
- first_names = entity.get("firstName")
157
- second_names = entity.get("secondName") + [""]
158
- middle_names = entity.get("middleName") + [""]
159
- father_names = entity.get("fatherName") + [""]
160
- mother_names = entity.get("motherName") + [""]
161
155
  last_names = entity.get("lastName")
162
- for (first, second, middle, father, mother, last) in product(
163
- first_names, second_names, middle_names, father_names, mother_names, last_names
164
- ):
165
- name = squash_spaces(" ".join([first, second, middle, father, mother, last]))
166
- if name is not None:
156
+ names_seq = [entity.get("firstName")]
157
+ names_seq.append(entity.get("secondName"))
158
+ names_seq.append(entity.get("middleName"))
159
+ names_seq.append(entity.get("fatherName"))
160
+ names_seq.append(entity.get("motherName"))
161
+ names_seq.append(last_names)
162
+ names_seq = [n for n in names_seq if len(n)]
163
+ for pairing in product(*names_seq):
164
+ name = squash_spaces(" ".join(pairing))
165
+ if len(name):
167
166
  entity.add("alias", name)
168
167
 
169
168
  # If no first name is given, at least add the last name:
@@ -1,5 +1,6 @@
1
1
  import re
2
2
  from banal import is_mapping, as_bool
3
+ from rigour.ids import get_identifier_format
3
4
  from typing import TYPE_CHECKING, Any, List, Optional, TypedDict
4
5
 
5
6
  from followthemoney.exc import InvalidModel
@@ -157,6 +158,11 @@ class Property:
157
158
  raise InvalidModel("Invalid reverse: %s" % self)
158
159
  self.reverse = self.range._add_reverse(model, self._reverse, self)
159
160
 
161
+ if self.type == registry.identifier and self.format is not None:
162
+ format_ = get_identifier_format(self.format)
163
+ if format_ is None or format_.NAME != self.format:
164
+ raise InvalidModel("Invalid identifier format: %s" % self.format)
165
+
160
166
  @property
161
167
  def label(self) -> str:
162
168
  """User-facing title for this property."""
followthemoney/proxy.py CHANGED
@@ -323,7 +323,7 @@ class EntityProxy(object):
323
323
  @property
324
324
  def countries(self) -> List[str]:
325
325
  """Get the set of all country-type values set of the entity."""
326
- return self.get_type_values(registry.country)
326
+ return self.get_type_values(registry.country, matchable=True)
327
327
 
328
328
  @property
329
329
  def temporal_start(self) -> Optional[Tuple[Property, str]]:
@@ -386,6 +386,8 @@ class EntityProxy(object):
386
386
  countries = set(self.countries)
387
387
  if not len(countries):
388
388
  for prop, value in self.itervalues():
389
+ if not prop.matchable:
390
+ continue
389
391
  hint = prop.type.country_hint(value)
390
392
  if hint is not None:
391
393
  countries.add(hint)
@@ -460,7 +462,7 @@ class EntityProxy(object):
460
462
  return self._size
461
463
 
462
464
  def __hash__(self) -> int:
463
- if not self.id:
465
+ if self.id is None:
464
466
  raise RuntimeError("Cannot hash entity without an ID")
465
467
  return hash(self.id)
466
468
 
@@ -84,6 +84,7 @@ Company:
84
84
  bikCode:
85
85
  label: "BIK"
86
86
  description: "Russian bank account code"
87
+ type: identifier
87
88
  pfrNumber:
88
89
  label: "PFR Number"
89
90
  description: "(RU, ПФР) Pension Fund Registration number. AAA-BBB-CCCCCC, where AAA is organisation region, BBB is district, CCCCCC number at a specific branch"
@@ -102,6 +102,17 @@ LegalEntity:
102
102
  bvdId:
103
103
  label: Bureau van Dijk ID
104
104
  type: identifier
105
+ sayariId:
106
+ label: Sayari Entity ID
107
+ type: identifier
108
+ brightQueryId:
109
+ label: BrightQuery ID
110
+ type: identifier
111
+ brightQueryOrgId:
112
+ label: BrightQuery Organization ID
113
+ type: identifier
114
+ hidden: true
115
+ matchable: false
105
116
  uscCode:
106
117
  # cf. https://en.wikipedia.org/wiki/Unified_Social_Credit_Identifier
107
118
  label: "USCC"
@@ -31,16 +31,23 @@ Person:
31
31
  # too many false positives.
32
32
  firstName:
33
33
  label: First name
34
+ description: "The part of a name that indicates the person, also often called given name or forename"
34
35
  secondName:
35
36
  label: Second name
37
+ description: "Deprecated, use one of the other more specific name properties instead."
38
+ deprecated: true
36
39
  middleName:
37
40
  label: Middle name
41
+ description: "The part of name written between a person's given name and family name. Often abbreviated as a middle initial."
38
42
  fatherName:
39
43
  label: Patronymic
44
+ description: "The part of a name based on the given name of one's father"
40
45
  motherName:
41
46
  label: Matronymic
47
+ description: "The part of a name based on the given name of one's mother"
42
48
  lastName:
43
49
  label: Last name
50
+ description: "The part of a name that indicates one's family, also often called surname or family name"
44
51
  nameSuffix:
45
52
  label: Name suffix
46
53
  birthDate:
@@ -92,6 +99,8 @@ Person:
92
99
  label: Religion
93
100
  political:
94
101
  label: Political association
102
+ profession:
103
+ label: Profession
95
104
  education:
96
105
  label: Education
97
106
  spokenLanguage:
@@ -55,7 +55,7 @@ Thing:
55
55
  wikidataId:
56
56
  label: Wikidata ID
57
57
  type: identifier
58
- format: qid
58
+ format: wikidata
59
59
  maxLength: 32
60
60
  keywords:
61
61
  label: Keywords
@@ -7,6 +7,7 @@ from rigour.names.pick import pick_lang_name
7
7
 
8
8
  from followthemoney.model import Model
9
9
  from followthemoney.exc import InvalidData
10
+ from followthemoney.schema import Schema
10
11
  from followthemoney.types.common import PropertyType
11
12
  from followthemoney.property import Property
12
13
  from followthemoney.util import gettext
@@ -477,12 +478,38 @@ class StatementEntity(EntityProxy):
477
478
  dataset: Dataset,
478
479
  statements: Iterable[Statement],
479
480
  ) -> SE:
480
- obj: Optional[SE] = None
481
+ model = Model.instance()
482
+ canonical_id: Optional[str] = None
483
+ schemata: Set[str] = set()
484
+ first_seens: Set[str] = set()
485
+ props: Dict[str, Set[Statement]] = {}
481
486
  for stmt in statements:
482
- if obj is None:
483
- data = {"schema": stmt.schema, "id": stmt.canonical_id}
484
- obj = cls(dataset, data)
485
- obj.add_statement(stmt)
486
- if obj is None:
487
- raise ValueError("No statements given!")
487
+ schemata.add(stmt.schema)
488
+ canonical_id = stmt.canonical_id or canonical_id or stmt.entity_id
489
+ if stmt.prop == BASE_ID:
490
+ if stmt.first_seen is not None:
491
+ first_seens.add(stmt.first_seen)
492
+ else:
493
+ if stmt.prop not in props:
494
+ props[stmt.prop] = set()
495
+ props[stmt.prop].add(stmt)
496
+
497
+ schema: Optional[Schema] = None
498
+ for name in schemata:
499
+ if schema is None:
500
+ schema = model.get(name)
501
+ elif schema.name != name:
502
+ try:
503
+ schema = model.common_schema(schema, name)
504
+ except InvalidData as exc:
505
+ raise InvalidData(f"{canonical_id}: {exc}") from exc
506
+
507
+ if schema is None:
508
+ err = "No valid schema for entity: %s %r" % (canonical_id, schemata)
509
+ raise InvalidData(err)
510
+
511
+ data = {"schema": schema, "id": canonical_id}
512
+ obj = cls(dataset, data)
513
+ obj.last_change = max(first_seens, default=None)
514
+ obj._statements = {p: s for p, s in props.items()}
488
515
  return obj
@@ -47,7 +47,9 @@ class AddressType(PropertyType):
47
47
  right_norm = normalize_address(right)
48
48
  if left_norm is None or right_norm is None:
49
49
  return 0.0
50
- return levenshtein_similarity(left_norm, right_norm, max_edits=3)
50
+ base_len = min(len(left_norm), len(right_norm))
51
+ max_edits = int(base_len * 0.33)
52
+ return levenshtein_similarity(left_norm, right_norm, max_edits=max_edits)
51
53
 
52
54
  def _specificity(self, value: str) -> float:
53
55
  return dampen(10, 60, value)
@@ -1,6 +1,6 @@
1
1
  import re
2
2
  from typing import Optional, TYPE_CHECKING
3
- from rigour.ids import get_identifier_format_names, get_identifier_format
3
+ from rigour.ids import get_identifier_format
4
4
 
5
5
  from followthemoney.types.common import PropertyType
6
6
  from followthemoney.util import dampen, shortest, longest
@@ -35,8 +35,8 @@ class IdentifierType(PropertyType):
35
35
  format: Optional[str] = None,
36
36
  proxy: Optional["EntityProxy"] = None,
37
37
  ) -> Optional[str]:
38
- if format in get_identifier_format_names():
39
- format_ = get_identifier_format(format)
38
+ format_ = get_identifier_format(format)
39
+ if format_ is not None:
40
40
  return format_.normalize(text)
41
41
  return text
42
42
 
@@ -61,7 +61,7 @@ class IdentifierType(PropertyType):
61
61
  return f"id:{value}"
62
62
 
63
63
  def caption(self, value: str, format: Optional[str] = None) -> str:
64
- if format in get_identifier_format_names():
65
- format_ = get_identifier_format(format)
64
+ format_ = get_identifier_format(format)
65
+ if format_ is not None:
66
66
  return format_.format(value)
67
67
  return value
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: followthemoney
3
- Version: 4.2.2
3
+ Version: 4.3.1
4
4
  Summary: A data model for anti corruption data modeling and analysis.
5
5
  Project-URL: Documentation, https://followthemoney.tech/
6
6
  Project-URL: Repository, https://github.com/opensanctions/followthemoney.git
@@ -48,9 +48,9 @@ Requires-Dist: prefixdate<1.0.0,>=0.5.0
48
48
  Requires-Dist: pydantic<3.0.0,>=2.11.0
49
49
  Requires-Dist: pytz>=2021.1
50
50
  Requires-Dist: pyyaml<7.0.0,>=5.0.0
51
- Requires-Dist: rdflib<7.2.0,>=6.2.0
51
+ Requires-Dist: rdflib<7.3.0,>=6.2.0
52
52
  Requires-Dist: requests<3.0.0,>=2.21.0
53
- Requires-Dist: rigour<2.0.0,>=1.3.1
53
+ Requires-Dist: rigour<2.0.0,>=1.4.0
54
54
  Requires-Dist: sqlalchemy[mypy]<3.0.0,>=2.0.0
55
55
  Provides-Extra: dev
56
56
  Requires-Dist: build; extra == 'dev'
@@ -1,16 +1,16 @@
1
- followthemoney/__init__.py,sha256=pTkibO7jJUHnObuIGY1rsjFL9siMpBusU7E9OylQDBI,856
2
- followthemoney/compare.py,sha256=bZlnj2VMoe67q4Lyq_VwS1a-EJnEK1kC8prbs8jyL9E,5774
1
+ followthemoney/__init__.py,sha256=9UJzMQUcqjQ4kWQIDMF1GB0ji8ph-GQlXa3e_X2uuX4,856
2
+ followthemoney/compare.py,sha256=frgumsDv4Ru9UkNof62jDjKCxxpCgV1Rusfu8s20uGA,6327
3
3
  followthemoney/entity.py,sha256=bBiX7hNquXemS3vYCUHKtWI_IqX43Z6i8RQDbZ7gXsg,3449
4
4
  followthemoney/exc.py,sha256=GyMgwY4QVm87hLevDfV7gM1MJsDqfNCi_UQw7F_A8X8,858
5
5
  followthemoney/graph.py,sha256=7X1CGHGvmktS2LSZqld2iXWzG7B831eCNYyBqamqEJ8,10921
6
- followthemoney/helpers.py,sha256=EsneNJ5DZXHTPUYfXLxGESphsPwCtGiWv-71vvVBWus,7982
6
+ followthemoney/helpers.py,sha256=KCdv1XAE7KQEXBiXp52Kvuck7wMaeNVBM3uaFemcvb4,7873
7
7
  followthemoney/messages.py,sha256=zUEa9CFecU8nRafIzhN6TKCh1kEihiIyIS1qr8PxY4g,806
8
8
  followthemoney/model.py,sha256=bWFVNa-DhYzc8BdSXBZdG2ev6Nh9uHx6i4tin8DvEEU,7374
9
9
  followthemoney/names.py,sha256=LODQqExKEHdH4z6Mmbhlm0KeKRzGcptaSWzYXZ7lONI,1120
10
10
  followthemoney/namespace.py,sha256=utggu9IGA8bhgEYom3OUB1KxkAJR_TrMNbY5MUF_db8,4536
11
11
  followthemoney/ontology.py,sha256=WWY_PYQGl5Ket4zZBuZglzQxD2Bh9UqHok6GJNNX7GA,3001
12
- followthemoney/property.py,sha256=9qZ_o2iA-1llLMJ3O2hsW7c2XhkFU1YbvVqretGYUSA,7913
13
- followthemoney/proxy.py,sha256=LD4K1oPABXMX212UZxwLu7XOHRDyVBwTlqudTUsUZRQ,19619
12
+ followthemoney/property.py,sha256=6FoKoUloDoQVM4XMnjjolKArlV4yBq9KOYxv8Os8WGQ,8234
13
+ followthemoney/proxy.py,sha256=nahd9lLZzum_-QEchqydinDa1Zg5_Ffl0fyo35BNncQ,19707
14
14
  followthemoney/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
15
  followthemoney/schema.py,sha256=WYnPE4Lego0pJHlojECEv0aO9Miw_YIvEb35HoDo4Zk,18087
16
16
  followthemoney/util.py,sha256=LoCSp1iE6VwXjotCkBXFRppeQs55726GzOuNIu3CvRE,4409
@@ -54,7 +54,7 @@ followthemoney/schema/Audio.yaml,sha256=Eb1rZGUEOX7XDAj_1YIN28NCBzMvkopQBNwgHt_k
54
54
  followthemoney/schema/BankAccount.yaml,sha256=60v-VD296lW1Qq7fx--CzxfPNwfCcyMV6xIl8OrSy5g,1431
55
55
  followthemoney/schema/Call.yaml,sha256=kbVCnVxucBrEplxehXHThLSJAJjy_GhWan-IeZZjr0M,980
56
56
  followthemoney/schema/CallForTenders.yaml,sha256=2IWonTzfSbrkynMoEWqv5fekUeFM_xDKpKIbRe1XDbo,3227
57
- followthemoney/schema/Company.yaml,sha256=v6OFHZjEIMQPSP9re6nAhI2kLk1MHfP9LfcCc7M-Ifc,3432
57
+ followthemoney/schema/Company.yaml,sha256=qWhk6HoSlep6oEjviXoV9ACTnfo7WZ5DSDvAkXMaQ5M,3455
58
58
  followthemoney/schema/Contract.yaml,sha256=aSPB64T1h-0nuLDv6krasUvvoPZgo6sWUbv60c3vmzI,1541
59
59
  followthemoney/schema/ContractAward.yaml,sha256=b2spaZHYCaP1yR1RCsrI7mUjk-fAF7BUE3dc8Vl3cUQ,1689
60
60
  followthemoney/schema/CourtCase.yaml,sha256=lcovnY0Ne_xcggvkqfCW_RHvsRKo8kFTCPCyovAXRtI,599
@@ -75,7 +75,7 @@ followthemoney/schema/Identification.yaml,sha256=6txjZs6-3Kn94c3G4tDeDt9Jb4FW55-
75
75
  followthemoney/schema/Image.yaml,sha256=wuznboWECGiV96_GQiXq1-oKNoxO8zKisR4xyusnEn8,394
76
76
  followthemoney/schema/Interest.yaml,sha256=VUrehmsN1WgtS1oAa5jn_JGtSkZGGYLGNahp-R5JhOQ,282
77
77
  followthemoney/schema/Interval.yaml,sha256=8YJQ51GI-GxvbjYs3uC593kQtCepWW_7ZiNnlbPm2aM,2084
78
- followthemoney/schema/LegalEntity.yaml,sha256=S56ALyeCZVgjpNy9EqkVkPBIs6QaGuZw31s9bdtEeC4,4789
78
+ followthemoney/schema/LegalEntity.yaml,sha256=586aA1F5CO8rwf49LG3_iobm13zM4O21gL_TONte8oM,5054
79
79
  followthemoney/schema/License.yaml,sha256=bXESXY-JpSmc5sthZe4sssXhx50UoLPAMED9FvEUyRU,534
80
80
  followthemoney/schema/Membership.yaml,sha256=IPmaOX4Ai2r4sGcA5ig2WmLvWHb38akdxp4smEdDWOE,710
81
81
  followthemoney/schema/Mention.yaml,sha256=nBeulR_Jm4x75aJ7yNF0TAVhHJqXQaEzOutLIn_YU-4,1086
@@ -89,7 +89,7 @@ followthemoney/schema/Page.yaml,sha256=YjYqaH2sOry0z4xh44CsX_eyuRClD6ZS0d2o2uQXF
89
89
  followthemoney/schema/Pages.yaml,sha256=KKPGZ06Ehp5mWIGnYfHUBN9jT03bk8nakw0pB5bA_7E,450
90
90
  followthemoney/schema/Passport.yaml,sha256=rpuLC86sdXnHF-prFQM4mAqYzlSGWKvPE4Cphtn2KRw,805
91
91
  followthemoney/schema/Payment.yaml,sha256=WRBJuj9ljsxLBs-0g9Z9UD87uR1RTtuUiYnWOnKr1qA,1757
92
- followthemoney/schema/Person.yaml,sha256=G6L6bf8WQtOC1Xr1TKWRCJt8JlyQKheBPtH1ZmjjS3w,2132
92
+ followthemoney/schema/Person.yaml,sha256=wS_URvex5pG_P5cv0UPafpaz3Jh9Z7CxHZjb3GwB85k,2798
93
93
  followthemoney/schema/PlainText.yaml,sha256=hfnVi-HmQeDbqDquSpkPJax9hNm86ioXGr4hzNzyPFE,278
94
94
  followthemoney/schema/Position.yaml,sha256=ZpxjWOLxwva_on32r9WD5ys0Ty3YxCju41mg9HG-pe0,1308
95
95
  followthemoney/schema/Project.yaml,sha256=2svtyGJopS0UrqPiuYGpBzj30V7k3LRDX4N1U56y4yY,462
@@ -104,7 +104,7 @@ followthemoney/schema/Similar.yaml,sha256=gD8rZEaPQWzU-rEfsKdn62uEucF3KxYBcPMoSd
104
104
  followthemoney/schema/Succession.yaml,sha256=RMJQqZ4Fv88N1RvWTAgjYg9BB5cELSj5CCAjM681Fpg,749
105
105
  followthemoney/schema/Table.yaml,sha256=GcsIAgSO9t2tvObA9zU2HhxlSqTe9CePmUnagu1Z0vI,641
106
106
  followthemoney/schema/TaxRoll.yaml,sha256=ugMzaaS7uyq2OLD50eGLcfvd6Cg0cSt65-T9GVqpRSA,746
107
- followthemoney/schema/Thing.yaml,sha256=hh1oMDQzWiSs1TamBNonmwEdlh2TVrNc3w9hWW8iSeY,2716
107
+ followthemoney/schema/Thing.yaml,sha256=xqGtSzyfE_EWx5PrxETKftF-9NSlsTYIbXrNqhdx9jw,2721
108
108
  followthemoney/schema/Trip.yaml,sha256=nLQD_ApmVJ8D56Czl7K700hhNZjzFV9FOQ3NBSQDLiM,771
109
109
  followthemoney/schema/UnknownLink.yaml,sha256=lneS_HZNgeLyJxwzWnLx0ZoyY3MXt99I_K2X_o9z5g8,682
110
110
  followthemoney/schema/UserAccount.yaml,sha256=2bbPKNtt1R3zWSSkaq_SVzRPfFzX74kAxwtIxTymHA8,840
@@ -114,7 +114,7 @@ followthemoney/schema/Vessel.yaml,sha256=zWHUfSK8g6Pz58ZyCaK0AFJ4u_UHjEIUGC4c_7o
114
114
  followthemoney/schema/Video.yaml,sha256=LY3DYMWTHXiAhL0hxBCNCz50cp2sPbUlEhhig5Fbjos,327
115
115
  followthemoney/schema/Workbook.yaml,sha256=iikWPElz4klA7SkWH7eae6xqhbkMCIP_3zdeXzFEMU0,354
116
116
  followthemoney/statement/__init__.py,sha256=7m2VUCAuqNZXIY0WFJRFkw5UG14QuxATL4f_xbqKwhw,633
117
- followthemoney/statement/entity.py,sha256=mUf-6uvrYvqq9azdHu7qFGkGK_0j9gT6nxscX_JXWR0,17673
117
+ followthemoney/statement/entity.py,sha256=oeudwhqfYLJKqbzxEydasMHqevkDASNyYN6s0yddW6I,18755
118
118
  followthemoney/statement/serialize.py,sha256=9eXzQ1biR2mSxWRID5C7xDdku4b4ZImHeRJ53yLZ0yo,7225
119
119
  followthemoney/statement/statement.py,sha256=Ae-EYuzS8S12BkaRqrvMuI1C7YwlRKa5C_pTBELyNMM,8029
120
120
  followthemoney/statement/util.py,sha256=B-ozuRc1TWvpop52873Pqt5OPj8H6uk4KyRJLfAhr10,780
@@ -142,7 +142,7 @@ followthemoney/translations/ru/LC_MESSAGES/followthemoney.po,sha256=7SQWytOTvoAQ
142
142
  followthemoney/translations/tr/LC_MESSAGES/followthemoney.mo,sha256=SC84e_ZF_oFJG1NKdyZY_W6Kb6POORZB6wdeAcEWmnE,487
143
143
  followthemoney/translations/tr/LC_MESSAGES/followthemoney.po,sha256=AZC3marhtVVq8Ck1FOgnt4sbDMz548nX48O9GDwImbQ,89826
144
144
  followthemoney/types/__init__.py,sha256=rWwQeiuMh2BNIuvhpMfJ4bPADDvt9Axu1eedvNFi0qY,3350
145
- followthemoney/types/address.py,sha256=nMFCj5QJyqA1ddpUmDLpRTum0nGXE-J70_WGnaLXnYo,2130
145
+ followthemoney/types/address.py,sha256=KsKLa9H8FfSkYF8VGslRnt-miGeni1rKICS8t1rzfzo,2235
146
146
  followthemoney/types/checksum.py,sha256=zZrU8WX4CY3Vta_vOyfgDNzIwbmtje7AaDv3O1fBMnk,823
147
147
  followthemoney/types/common.py,sha256=4ks7zPT8rknrGSd4JFc1zRkS-TL4SX-25_ZbjcVDos0,10081
148
148
  followthemoney/types/country.py,sha256=n8vihijDVud_3Ra-as4Ize0jf_HbcdKVR5YX3TlKZy0,1533
@@ -150,7 +150,7 @@ followthemoney/types/date.py,sha256=PjcaEyW6CBzf0-gHWKUsKjWIaD3AVBEl0zLSRQOVXxc,
150
150
  followthemoney/types/email.py,sha256=L3RTYrMABlNQF7hCynXGfzoj6YNEHW5JAY_BwuhoZdA,3375
151
151
  followthemoney/types/entity.py,sha256=oDxVEhuxyU1ScpOpebPpUm3o0I9j_p7Qrq-t5yNpluQ,2338
152
152
  followthemoney/types/gender.py,sha256=fi9iKLbjAUxDCLBtU1MxWidxv7KgCY2eH5746FYlEGk,1725
153
- followthemoney/types/identifier.py,sha256=hzD188FtwG0w3TcmbnDwnUMc8MZVcWgQJKGAvrwygc4,2296
153
+ followthemoney/types/identifier.py,sha256=7YET9mdYAktMup_kTvnxLu9SJzEU7ZJgN3KImzjfT-Y,2219
154
154
  followthemoney/types/ip.py,sha256=mMFTODFiXAJROCUYJvoLAShyIiTIWVmMBh5zT_GquYM,1300
155
155
  followthemoney/types/json.py,sha256=V3qJD5RxJykNX51u3w1Nx9xqoNBnkulhzkJI9XMYKFo,1690
156
156
  followthemoney/types/language.py,sha256=SXgRRH-DyPmyyrqYurSyMiG6WHB8a0Gw81XxroEGD-c,2747
@@ -161,8 +161,8 @@ followthemoney/types/phone.py,sha256=r8uRqWinS0CYnYBTs405k5gO4jeatUDgjdzzijoMKJE
161
161
  followthemoney/types/string.py,sha256=fqyTauAm4mNnNaoH-yH087RBbNh-G5ZZUO3awTGQUUg,1230
162
162
  followthemoney/types/topic.py,sha256=Mi0Gx0m3bDeTmyuvM6jdRMqv81O03U4eI99R13KGu2Y,4503
163
163
  followthemoney/types/url.py,sha256=QFpS_JIV8unFHuh_uGv22SWUUkocBoOpzLsAJWom_gI,1455
164
- followthemoney-4.2.2.dist-info/METADATA,sha256=Q7n2yZuDQJM27GTkVB0nMJgbMSVJLJ1RNCMmzABRFbY,6747
165
- followthemoney-4.2.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
166
- followthemoney-4.2.2.dist-info/entry_points.txt,sha256=caoFTlf213jhg5sz3TNSofutjUTzaKtWATuSIdd9Cps,653
167
- followthemoney-4.2.2.dist-info/licenses/LICENSE,sha256=H6_EVXisnJC0-18CjXIaqrBSFq_VH3OnS7u3dccOv6g,1148
168
- followthemoney-4.2.2.dist-info/RECORD,,
164
+ followthemoney-4.3.1.dist-info/METADATA,sha256=i41B2NtbZrqWsc0F26XIP9GCvbRtMZ1ZsWeRWvk4AAg,6747
165
+ followthemoney-4.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
166
+ followthemoney-4.3.1.dist-info/entry_points.txt,sha256=caoFTlf213jhg5sz3TNSofutjUTzaKtWATuSIdd9Cps,653
167
+ followthemoney-4.3.1.dist-info/licenses/LICENSE,sha256=H6_EVXisnJC0-18CjXIaqrBSFq_VH3OnS7u3dccOv6g,1148
168
+ followthemoney-4.3.1.dist-info/RECORD,,