followthemoney 4.1.1__py3-none-any.whl → 4.2.0__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.1.1"
12
+ __version__ = "4.2.0"
13
13
 
14
14
  # Data model singleton
15
15
  model = Model.instance()
followthemoney/entity.py CHANGED
@@ -20,6 +20,21 @@ class ValueEntity(EntityProxy):
20
20
  applications should use this entity class as the base class.
21
21
  """
22
22
 
23
+ __slots__ = [
24
+ "schema",
25
+ "id",
26
+ "key_prefix",
27
+ "context",
28
+ "_properties",
29
+ "_size",
30
+ "_caption",
31
+ "datasets",
32
+ "referents",
33
+ "first_seen",
34
+ "last_seen",
35
+ "last_change",
36
+ ]
37
+
23
38
  def __init__(
24
39
  self,
25
40
  schema: Schema,
followthemoney/helpers.py CHANGED
@@ -7,7 +7,7 @@
7
7
  # probably be the first place to break.
8
8
  from os.path import splitext
9
9
  from typing import Iterable, List, Optional, Set
10
- from normality import safe_filename
10
+ from normality import safe_filename, squash_spaces
11
11
  from mimetypes import guess_extension
12
12
  from itertools import product
13
13
  from datetime import datetime, timedelta
@@ -148,23 +148,21 @@ def inline_names(entity: E, related: E) -> None:
148
148
 
149
149
 
150
150
  def combine_names(entity: E) -> E:
151
- """This function will try to build names from name parts provided as part
152
- of a person entity. This is of course impossible to do culturally correctly
153
- for the whole planet at once, so it should be mostly used for internal-facing
154
- (e.g. matching) processes."""
151
+ """Build a full names from name parts of a Person entity and add them as aliases.
152
+
153
+ This is of course impossible to do culturally correctly for the whole planet at
154
+ once, so it should be mostly used for internal-facing (e.g. matching) processes."""
155
155
  if entity.schema.is_a("Person"):
156
156
  first_names = entity.get("firstName")
157
- second_names = entity.get("secondName")
158
- second_names.append("")
159
- middle_names = entity.get("middleName")
160
- middle_names.append("")
161
- father_names = entity.get("fatherName")
162
- father_names.append("")
157
+ second_names = entity.get("secondName") + [""]
158
+ middle_names = entity.get("middleName") + [""]
159
+ father_names = entity.get("fatherName") + [""]
160
+ mother_names = entity.get("motherName") + [""]
163
161
  last_names = entity.get("lastName")
164
- for (first, second, middle, father, last) in product(
165
- first_names, second_names, middle_names, father_names, last_names
162
+ for (first, second, middle, father, mother, last) in product(
163
+ first_names, second_names, middle_names, father_names, mother_names, last_names
166
164
  ):
167
- name = join_text(first, second, middle, father, last)
165
+ name = squash_spaces(" ".join([first, second, middle, father, mother, last]))
168
166
  if name is not None:
169
167
  entity.add("alias", name)
170
168
 
@@ -0,0 +1,43 @@
1
+ Risk:
2
+ label: Risk
3
+ plural: Risks
4
+ description: "A risk associated with an entity"
5
+ extends:
6
+ - Interval
7
+ matchable: false
8
+ featured:
9
+ - entity
10
+ - country
11
+ - reason
12
+ - status
13
+ - startDate
14
+ required:
15
+ - entity
16
+ caption:
17
+ - reason
18
+ - status
19
+ properties:
20
+ entity:
21
+ label: "Entity"
22
+ reverse:
23
+ name: risks
24
+ label: "Risks"
25
+ type: entity
26
+ range: Thing
27
+ topics:
28
+ label: Topics
29
+ type: topic
30
+ status:
31
+ label: "Status"
32
+ duration:
33
+ label: "Duration"
34
+ type: number
35
+ reason:
36
+ label: "Reason"
37
+ type: text
38
+ country:
39
+ label: "Country"
40
+ type: country
41
+ listingDate:
42
+ label: "Listing date"
43
+ type: date
@@ -38,6 +38,9 @@ Vessel:
38
38
  grossRegisteredTonnage:
39
39
  label: Gross Registered Tonnage
40
40
  type: number
41
+ deadweightTonnage:
42
+ label: Deadweight Tonnage
43
+ type: number
41
44
  nameChangeDate:
42
45
  label: Date of Name Change
43
46
  type: date
@@ -372,6 +372,7 @@ class StatementEntity(EntityProxy):
372
372
  }
373
373
  referents: Set[Optional[str]] = set(self.extra_referents)
374
374
  datasets = set(self.datasets)
375
+ origins: Set[str] = set()
375
376
  first_seen = None
376
377
  last_seen = None
377
378
  for stmts in self._statements.values():
@@ -385,9 +386,13 @@ class StatementEntity(EntityProxy):
385
386
  if stmt.entity_id is not None and stmt.entity_id != self.id:
386
387
  referents.add(stmt.entity_id)
387
388
  datasets.add(stmt.dataset)
389
+ if stmt.origin is not None:
390
+ origins.add(stmt.origin)
388
391
 
389
392
  data["referents"] = list(referents)
390
393
  data["datasets"] = list(datasets)
394
+ if origins:
395
+ data["origin"] = list(origins)
391
396
 
392
397
  if first_seen is not None:
393
398
  data["first_seen"] = first_seen
@@ -1,7 +1,6 @@
1
- import countrynames
2
1
  from typing import Optional, TYPE_CHECKING
3
2
  from babel.core import Locale
4
- from rigour.territories import get_territory, get_ftm_countries
3
+ from rigour.territories import get_ftm_countries, lookup_territory
5
4
 
6
5
  from followthemoney.types.common import EnumType, EnumValues
7
6
  from followthemoney.util import const, defer as _
@@ -37,16 +36,11 @@ class CountryType(EnumType):
37
36
 
38
37
  The input may be a country code, a country name, etc.
39
38
  """
40
- territory = get_territory(text)
39
+ territory = lookup_territory(text, fuzzy=fuzzy)
41
40
  if territory is not None:
42
41
  ftm_country = territory.ftm_country
43
42
  if ftm_country is not None:
44
43
  return ftm_country
45
- code = countrynames.to_code(text, fuzzy=fuzzy)
46
- if code is not None:
47
- territory = get_territory(code)
48
- if territory is not None:
49
- return territory.ftm_country
50
44
  return None
51
45
 
52
46
  def country_hint(self, value: str) -> str:
@@ -55,10 +55,14 @@ class TopicType(EnumType):
55
55
  "gov.judicial": _("Judicial branch of government"),
56
56
  "gov.security": _("Security services"),
57
57
  "gov.financial": _("Central banking and financial integrity"),
58
+ "gov.religion": _("Religious leadership"),
58
59
  "fin": _("Financial services"),
59
60
  "fin.bank": _("Bank"),
60
61
  "fin.fund": _("Fund"),
61
62
  "fin.adivsor": _("Financial advisor"),
63
+ "mare.detained": _("Maritime detention"),
64
+ "mare.shadow": _("Shadow fleet"),
65
+ "mare.sts": _("Ship-to-ship transfer"),
62
66
  "reg.action": _("Regulator action"),
63
67
  "reg.warn": _("Regulator warning"),
64
68
  "role.pep": _("Politician"),
@@ -87,5 +91,33 @@ class TopicType(EnumType):
87
91
  "poi": _("Person of interest"),
88
92
  }
89
93
 
94
+ RISKS = {
95
+ "corp.disqual",
96
+ "crime.boss",
97
+ "crime.fin",
98
+ "crime.fraud",
99
+ "crime.terror",
100
+ "crime.theft",
101
+ "crime.traffick",
102
+ "crime.war",
103
+ "crime",
104
+ "debarment",
105
+ "export.control",
106
+ "export.risk",
107
+ "poi",
108
+ "mare.detained",
109
+ "mare.shadow",
110
+ "reg.action",
111
+ "reg.warn",
112
+ "role.oligarch",
113
+ "role.pep",
114
+ "role.rca",
115
+ "sanction.counter",
116
+ "sanction.linked",
117
+ "sanction",
118
+ "wanted",
119
+ }
120
+ """A set of topics that imply a counterparty risk in business dealings."""
121
+
90
122
  def _locale_names(self, locale: Locale) -> EnumValues:
91
123
  return {k: gettext(v) for (k, v) in self._TOPICS.items()}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: followthemoney
3
- Version: 4.1.1
3
+ Version: 4.2.0
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
@@ -39,7 +39,6 @@ Requires-Python: >=3.10
39
39
  Requires-Dist: babel<3.0.0,>=2.14.0
40
40
  Requires-Dist: banal<1.1.0,>=1.0.6
41
41
  Requires-Dist: click<9.0.0,>=8.0
42
- Requires-Dist: countrynames<2.0.0,>=1.13.0
43
42
  Requires-Dist: networkx<3.5,>=2.5
44
43
  Requires-Dist: normality<4.0.0,>=3.0.1
45
44
  Requires-Dist: openpyxl<4.0.0,>=3.0.5
@@ -51,7 +50,7 @@ Requires-Dist: pytz>=2021.1
51
50
  Requires-Dist: pyyaml<7.0.0,>=5.0.0
52
51
  Requires-Dist: rdflib<7.2.0,>=6.2.0
53
52
  Requires-Dist: requests<3.0.0,>=2.21.0
54
- Requires-Dist: rigour<2.0.0,>=1.1.1
53
+ Requires-Dist: rigour<2.0.0,>=1.2.0
55
54
  Requires-Dist: sqlalchemy[mypy]<3.0.0,>=2.0.0
56
55
  Provides-Extra: dev
57
56
  Requires-Dist: build; extra == 'dev'
@@ -1,9 +1,9 @@
1
- followthemoney/__init__.py,sha256=_drb_fsELoJ6qpcyjNSJbn8OVaBH-ZdeDQKaFJ1S_Qk,856
1
+ followthemoney/__init__.py,sha256=NqtJnba71wycGIhiZrBIfOYbafL_Tm_Qd0ouNm2EYPA,856
2
2
  followthemoney/compare.py,sha256=bZlnj2VMoe67q4Lyq_VwS1a-EJnEK1kC8prbs8jyL9E,5774
3
- followthemoney/entity.py,sha256=hHY9yysn_iFTtXqcHg4hHhYfmgLw4prWul8rD1X82Y0,3184
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=Btb6BlHg_c-qCXZo-NP_LURKG-qu-QD3Fj1ev_c7Xic,7956
6
+ followthemoney/helpers.py,sha256=EsneNJ5DZXHTPUYfXLxGESphsPwCtGiWv-71vvVBWus,7982
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
@@ -97,6 +97,7 @@ followthemoney/schema/ProjectParticipant.yaml,sha256=xNehEu90uqUfboNouezhZQ8ZQLx
97
97
  followthemoney/schema/PublicBody.yaml,sha256=BNfLBqH1OapoEninAjWmqZx_n-G5QUnzzydW7300TiY,301
98
98
  followthemoney/schema/RealEstate.yaml,sha256=NWFHXqEHskYQN-kvQESZpu74nztShqoYSZEjZAr-DHM,1363
99
99
  followthemoney/schema/Representation.yaml,sha256=sCvFnUDQaElq2cqSB0rILcMYb2gaMZqlzxlHxyX9IGg,792
100
+ followthemoney/schema/Risk.yaml,sha256=2BRVBqb6wiLHxb_V50P-YMAOhjC64UVHDyh5PASpCIA,728
100
101
  followthemoney/schema/Sanction.yaml,sha256=-3_NrTp1KPSsSMkttFzwo81zFKplx15w-9EmwPnE8-o,1278
101
102
  followthemoney/schema/Security.yaml,sha256=w8Och0cslWjHPAs60HZ6JarEXdIbqGlIbN1NlvgN_7Y,1212
102
103
  followthemoney/schema/Similar.yaml,sha256=gD8rZEaPQWzU-rEfsKdn62uEucF3KxYBcPMoSdnxvME,817
@@ -109,11 +110,11 @@ followthemoney/schema/UnknownLink.yaml,sha256=lneS_HZNgeLyJxwzWnLx0ZoyY3MXt99I_K
109
110
  followthemoney/schema/UserAccount.yaml,sha256=2bbPKNtt1R3zWSSkaq_SVzRPfFzX74kAxwtIxTymHA8,840
110
111
  followthemoney/schema/Value.yaml,sha256=cNHTCtakMTXDW0Qpb6ArZodi9rMJ-Ebvp1WsOIRRzw4,310
111
112
  followthemoney/schema/Vehicle.yaml,sha256=Ypl4A5HJFOZfZh3DK0ewN-hyJuCMcovR0mPNddIZrOA,1051
112
- followthemoney/schema/Vessel.yaml,sha256=nFaUJ_0BzFJstvog1iDvwV9DHKHr9ky4DLb1NZGGh1E,1096
113
+ followthemoney/schema/Vessel.yaml,sha256=zWHUfSK8g6Pz58ZyCaK0AFJ4u_UHjEIUGC4c_7oS11Y,1170
113
114
  followthemoney/schema/Video.yaml,sha256=LY3DYMWTHXiAhL0hxBCNCz50cp2sPbUlEhhig5Fbjos,327
114
115
  followthemoney/schema/Workbook.yaml,sha256=iikWPElz4klA7SkWH7eae6xqhbkMCIP_3zdeXzFEMU0,354
115
116
  followthemoney/statement/__init__.py,sha256=7m2VUCAuqNZXIY0WFJRFkw5UG14QuxATL4f_xbqKwhw,633
116
- followthemoney/statement/entity.py,sha256=gXvBTBwHL-GfmBXdw9HoP6WSbiwBOrTTOllQwkIObyQ,15986
117
+ followthemoney/statement/entity.py,sha256=MKHGmFeDwcW2lTbAeKSdU53YwDuoLm5iKy4roeb8_lo,16172
117
118
  followthemoney/statement/serialize.py,sha256=9eXzQ1biR2mSxWRID5C7xDdku4b4ZImHeRJ53yLZ0yo,7225
118
119
  followthemoney/statement/statement.py,sha256=Ae-EYuzS8S12BkaRqrvMuI1C7YwlRKa5C_pTBELyNMM,8029
119
120
  followthemoney/statement/util.py,sha256=B-ozuRc1TWvpop52873Pqt5OPj8H6uk4KyRJLfAhr10,780
@@ -144,7 +145,7 @@ followthemoney/types/__init__.py,sha256=rWwQeiuMh2BNIuvhpMfJ4bPADDvt9Axu1eedvNFi
144
145
  followthemoney/types/address.py,sha256=nMFCj5QJyqA1ddpUmDLpRTum0nGXE-J70_WGnaLXnYo,2130
145
146
  followthemoney/types/checksum.py,sha256=zZrU8WX4CY3Vta_vOyfgDNzIwbmtje7AaDv3O1fBMnk,823
146
147
  followthemoney/types/common.py,sha256=4ks7zPT8rknrGSd4JFc1zRkS-TL4SX-25_ZbjcVDos0,10081
147
- followthemoney/types/country.py,sha256=mUCjwhUbA5Ef5HYuKb1KbH4aZ3MxaNwE1p77uOZMuG0,1745
148
+ followthemoney/types/country.py,sha256=n8vihijDVud_3Ra-as4Ize0jf_HbcdKVR5YX3TlKZy0,1533
148
149
  followthemoney/types/date.py,sha256=PjcaEyW6CBzf0-gHWKUsKjWIaD3AVBEl0zLSRQOVXxc,3105
149
150
  followthemoney/types/email.py,sha256=L3RTYrMABlNQF7hCynXGfzoj6YNEHW5JAY_BwuhoZdA,3375
150
151
  followthemoney/types/entity.py,sha256=oDxVEhuxyU1ScpOpebPpUm3o0I9j_p7Qrq-t5yNpluQ,2338
@@ -158,10 +159,10 @@ followthemoney/types/name.py,sha256=ZWGDebv01qByh_yBYOVoS3Edlm3_JVPShQMklKc6ZOA,
158
159
  followthemoney/types/number.py,sha256=OdVuHDd4IYIIHhx_317JKeMjBAGtsJ2TAcxoZKZ4MkY,3948
159
160
  followthemoney/types/phone.py,sha256=r8uRqWinS0CYnYBTs405k5gO4jeatUDgjdzzijoMKJE,3811
160
161
  followthemoney/types/string.py,sha256=fqyTauAm4mNnNaoH-yH087RBbNh-G5ZZUO3awTGQUUg,1230
161
- followthemoney/types/topic.py,sha256=CS5IoI8gm4MSVxfV6K4mGd20_tT1SaKMkcOt_ObSsAg,3678
162
+ followthemoney/types/topic.py,sha256=Mi0Gx0m3bDeTmyuvM6jdRMqv81O03U4eI99R13KGu2Y,4503
162
163
  followthemoney/types/url.py,sha256=QFpS_JIV8unFHuh_uGv22SWUUkocBoOpzLsAJWom_gI,1455
163
- followthemoney-4.1.1.dist-info/METADATA,sha256=OatNsAWxjixfh_s-iGY046rcZRoCs9p-6rPLwB8VxTM,6791
164
- followthemoney-4.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
165
- followthemoney-4.1.1.dist-info/entry_points.txt,sha256=caoFTlf213jhg5sz3TNSofutjUTzaKtWATuSIdd9Cps,653
166
- followthemoney-4.1.1.dist-info/licenses/LICENSE,sha256=H6_EVXisnJC0-18CjXIaqrBSFq_VH3OnS7u3dccOv6g,1148
167
- followthemoney-4.1.1.dist-info/RECORD,,
164
+ followthemoney-4.2.0.dist-info/METADATA,sha256=I1JXOtpniNIMi2jPoM1Vpx_IaYezqCP8l9e3qhldGtc,6748
165
+ followthemoney-4.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
166
+ followthemoney-4.2.0.dist-info/entry_points.txt,sha256=caoFTlf213jhg5sz3TNSofutjUTzaKtWATuSIdd9Cps,653
167
+ followthemoney-4.2.0.dist-info/licenses/LICENSE,sha256=H6_EVXisnJC0-18CjXIaqrBSFq_VH3OnS7u3dccOv6g,1148
168
+ followthemoney-4.2.0.dist-info/RECORD,,