followthemoney 4.3.0__py3-none-any.whl → 4.3.2__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.
- followthemoney/__init__.py +1 -1
- followthemoney/compare.py +19 -0
- followthemoney/model.py +2 -0
- followthemoney/property.py +10 -3
- followthemoney/proxy.py +1 -1
- followthemoney/schema/Thing.yaml +1 -1
- followthemoney/schema.py +4 -2
- followthemoney/statement/util.py +2 -2
- followthemoney/types/address.py +3 -3
- followthemoney/types/checksum.py +3 -3
- followthemoney/types/country.py +3 -3
- followthemoney/types/date.py +3 -3
- followthemoney/types/entity.py +3 -3
- followthemoney/types/gender.py +6 -6
- followthemoney/types/identifier.py +8 -8
- followthemoney/types/ip.py +3 -3
- followthemoney/types/json.py +2 -2
- followthemoney/types/language.py +3 -3
- followthemoney/types/mimetype.py +3 -3
- followthemoney/types/name.py +3 -3
- followthemoney/types/number.py +2 -2
- followthemoney/types/phone.py +3 -3
- followthemoney/types/string.py +2 -2
- followthemoney/types/topic.py +6 -3
- followthemoney/types/url.py +3 -3
- {followthemoney-4.3.0.dist-info → followthemoney-4.3.2.dist-info}/METADATA +2 -2
- {followthemoney-4.3.0.dist-info → followthemoney-4.3.2.dist-info}/RECORD +30 -30
- {followthemoney-4.3.0.dist-info → followthemoney-4.3.2.dist-info}/WHEEL +0 -0
- {followthemoney-4.3.0.dist-info → followthemoney-4.3.2.dist-info}/entry_points.txt +0 -0
- {followthemoney-4.3.0.dist-info → followthemoney-4.3.2.dist-info}/licenses/LICENSE +0 -0
followthemoney/__init__.py
CHANGED
|
@@ -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.3.
|
|
12
|
+
__version__ = "4.3.2"
|
|
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/model.py
CHANGED
|
@@ -9,6 +9,7 @@ from followthemoney.types.common import PropertyType, PropertyTypeToDict
|
|
|
9
9
|
from followthemoney.schema import Schema, SchemaToDict
|
|
10
10
|
from followthemoney.property import Property
|
|
11
11
|
from followthemoney.exc import InvalidModel, InvalidData
|
|
12
|
+
from followthemoney.util import const
|
|
12
13
|
|
|
13
14
|
if TYPE_CHECKING:
|
|
14
15
|
from followthemoney.proxy import EntityProxy
|
|
@@ -72,6 +73,7 @@ class Model(object):
|
|
|
72
73
|
if not isinstance(data, dict):
|
|
73
74
|
raise InvalidModel("Model file is not a mapping: %s" % filepath)
|
|
74
75
|
for name, config in data.items():
|
|
76
|
+
name = const(name)
|
|
75
77
|
self.schemata[name] = Schema(self, name, config)
|
|
76
78
|
|
|
77
79
|
def get(self, name: Union[str, Schema]) -> Optional[Schema]:
|
followthemoney/property.py
CHANGED
|
@@ -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
|
|
@@ -86,17 +87,16 @@ class Property:
|
|
|
86
87
|
self.schema = schema
|
|
87
88
|
|
|
88
89
|
#: Machine-readable name for this property.
|
|
89
|
-
self.name =
|
|
90
|
+
self.name = name
|
|
90
91
|
if not check_property_name(self.name):
|
|
91
92
|
raise InvalidModel("Invalid name: %s" % self.name)
|
|
92
93
|
|
|
93
94
|
#: Qualified property name, which also includes the schema name.
|
|
94
95
|
self.qname = const("%s:%s" % (schema.name, self.name))
|
|
95
96
|
|
|
96
|
-
self._hash = hash("<Property(%r)>" % self.qname)
|
|
97
|
-
|
|
98
97
|
self._label = data.get("label", name)
|
|
99
98
|
self._description = data.get("description")
|
|
99
|
+
self._hash = hash("<Property(%r)>" % self.qname)
|
|
100
100
|
|
|
101
101
|
#: This property is deprecated and should not be used.
|
|
102
102
|
self.deprecated = as_bool(data.get("deprecated", False))
|
|
@@ -157,6 +157,13 @@ class Property:
|
|
|
157
157
|
raise InvalidModel("Invalid reverse: %s" % self)
|
|
158
158
|
self.reverse = self.range._add_reverse(model, self._reverse, self)
|
|
159
159
|
|
|
160
|
+
if self.type == registry.identifier and self.format is not None:
|
|
161
|
+
format_ = get_identifier_format(self.format)
|
|
162
|
+
if format_ is None or format_.NAME != self.format:
|
|
163
|
+
raise InvalidModel("Invalid identifier format: %s" % self.format)
|
|
164
|
+
# Internalize the string:
|
|
165
|
+
self.format = format_.NAME
|
|
166
|
+
|
|
160
167
|
@property
|
|
161
168
|
def label(self) -> str:
|
|
162
169
|
"""User-facing title for this property."""
|
followthemoney/proxy.py
CHANGED
followthemoney/schema/Thing.yaml
CHANGED
followthemoney/schema.py
CHANGED
|
@@ -106,7 +106,7 @@ class Schema:
|
|
|
106
106
|
|
|
107
107
|
def __init__(self, model: "Model", name: str, data: SchemaSpec) -> None:
|
|
108
108
|
#: Machine-readable name of the schema, used for identification.
|
|
109
|
-
self.name =
|
|
109
|
+
self.name = name
|
|
110
110
|
self.model = model
|
|
111
111
|
self._label = data.get("label", name)
|
|
112
112
|
self._plural = data.get("plural", self.label)
|
|
@@ -191,6 +191,7 @@ class Schema:
|
|
|
191
191
|
#: inherited from parent schemata.
|
|
192
192
|
self.properties: Dict[str, Property] = {}
|
|
193
193
|
for pname, prop in data.get("properties", {}).items():
|
|
194
|
+
pname = const(pname)
|
|
194
195
|
self.properties[pname] = Property(self, pname, prop)
|
|
195
196
|
|
|
196
197
|
def generate(self, model: "Model") -> None:
|
|
@@ -264,6 +265,7 @@ class Schema:
|
|
|
264
265
|
name = data.get("name")
|
|
265
266
|
if name is None:
|
|
266
267
|
raise InvalidModel("Unnamed reverse: %s" % other)
|
|
268
|
+
name = const(name)
|
|
267
269
|
|
|
268
270
|
prop = self.get(name)
|
|
269
271
|
if prop is None:
|
|
@@ -272,7 +274,7 @@ class Schema:
|
|
|
272
274
|
"type": registry.entity.name,
|
|
273
275
|
"reverse": {"name": other.name},
|
|
274
276
|
"range": other.schema.name,
|
|
275
|
-
"hidden": data.get("hidden", other.hidden),
|
|
277
|
+
"hidden": as_bool(data.get("hidden", other.hidden)),
|
|
276
278
|
}
|
|
277
279
|
prop = Property(self, name, spec)
|
|
278
280
|
prop.stub = True
|
followthemoney/statement/util.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import sys
|
|
2
1
|
from functools import cache
|
|
3
2
|
from typing import Tuple
|
|
4
3
|
|
|
5
4
|
from followthemoney.model import Model
|
|
5
|
+
from followthemoney.util import const
|
|
6
6
|
|
|
7
7
|
BASE_ID = "id"
|
|
8
8
|
|
|
@@ -28,4 +28,4 @@ def get_prop_type(schema: str, prop: str) -> str:
|
|
|
28
28
|
def unpack_prop(id: str) -> Tuple[str, str, str]:
|
|
29
29
|
schema, prop = id.split(":", 1)
|
|
30
30
|
prop_type = get_prop_type(schema, prop)
|
|
31
|
-
return
|
|
31
|
+
return const(schema), prop_type, const(prop)
|
followthemoney/types/address.py
CHANGED
|
@@ -6,7 +6,7 @@ from rigour.text.distance import levenshtein_similarity
|
|
|
6
6
|
|
|
7
7
|
from followthemoney.types.common import PropertyType
|
|
8
8
|
from followthemoney.util import defer as _
|
|
9
|
-
from followthemoney.util import dampen
|
|
9
|
+
from followthemoney.util import dampen
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
12
|
from followthemoney.proxy import EntityProxy
|
|
@@ -20,8 +20,8 @@ class AddressType(PropertyType):
|
|
|
20
20
|
|
|
21
21
|
LINE_BREAKS = re.compile(r"(\r\n|\n|<BR/>|<BR>|\t|ESQ\.,|ESQ,|;)")
|
|
22
22
|
COMMATA = re.compile(r"(,\s?[,\.])")
|
|
23
|
-
name =
|
|
24
|
-
group =
|
|
23
|
+
name = "address"
|
|
24
|
+
group = "addresses"
|
|
25
25
|
label = _("Address")
|
|
26
26
|
plural = _("Addresses")
|
|
27
27
|
matchable = True
|
followthemoney/types/checksum.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from followthemoney.types.common import PropertyType
|
|
2
|
-
from followthemoney.util import
|
|
2
|
+
from followthemoney.util import defer as _
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class ChecksumType(PropertyType):
|
|
@@ -12,8 +12,8 @@ class ChecksumType(PropertyType):
|
|
|
12
12
|
of this type are scrubbed when submitted via the normal API. Checksums can only
|
|
13
13
|
be defined by uploading a document to be ingested."""
|
|
14
14
|
|
|
15
|
-
name =
|
|
16
|
-
group =
|
|
15
|
+
name = "checksum"
|
|
16
|
+
group = "checksums"
|
|
17
17
|
label = _("Checksum")
|
|
18
18
|
plural = _("Checksums")
|
|
19
19
|
matchable = True
|
followthemoney/types/country.py
CHANGED
|
@@ -3,7 +3,7 @@ from babel.core import Locale
|
|
|
3
3
|
from rigour.territories import get_ftm_countries, lookup_territory
|
|
4
4
|
|
|
5
5
|
from followthemoney.types.common import EnumType, EnumValues
|
|
6
|
-
from followthemoney.util import
|
|
6
|
+
from followthemoney.util import defer as _
|
|
7
7
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
from followthemoney.proxy import EntityProxy
|
|
@@ -15,8 +15,8 @@ class CountryType(EnumType):
|
|
|
15
15
|
a number of unusual and controversial designations (e.g. the Soviet Union,
|
|
16
16
|
Transnistria, Somaliland, Kosovo)."""
|
|
17
17
|
|
|
18
|
-
name =
|
|
19
|
-
group =
|
|
18
|
+
name = "country"
|
|
19
|
+
group = "countries"
|
|
20
20
|
label = _("Country")
|
|
21
21
|
plural = _("Countries")
|
|
22
22
|
matchable = True
|
followthemoney/types/date.py
CHANGED
|
@@ -5,7 +5,7 @@ from prefixdate import parse, parse_format, Precision
|
|
|
5
5
|
|
|
6
6
|
from followthemoney.types.common import PropertyType
|
|
7
7
|
from followthemoney.util import defer as _
|
|
8
|
-
from followthemoney.util import dampen
|
|
8
|
+
from followthemoney.util import dampen
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
11
11
|
from followthemoney.proxy import EntityProxy
|
|
@@ -20,8 +20,8 @@ class DateType(PropertyType):
|
|
|
20
20
|
The timezone is always expected to be UTC and cannot be specified otherwise. There is
|
|
21
21
|
no support for calendar weeks (`2021-W7`) and date ranges (`2021-2024`)."""
|
|
22
22
|
|
|
23
|
-
name =
|
|
24
|
-
group =
|
|
23
|
+
name = "date"
|
|
24
|
+
group = "dates"
|
|
25
25
|
label = _("Date")
|
|
26
26
|
plural = _("Dates")
|
|
27
27
|
matchable = True
|
followthemoney/types/entity.py
CHANGED
|
@@ -4,7 +4,7 @@ from typing import Any, Optional, TYPE_CHECKING
|
|
|
4
4
|
from followthemoney.types.common import PropertyType
|
|
5
5
|
from followthemoney.value import Value
|
|
6
6
|
from followthemoney.util import ENTITY_ID_LEN, get_entity_id, sanitize_text
|
|
7
|
-
from followthemoney.util import
|
|
7
|
+
from followthemoney.util import gettext, defer as _
|
|
8
8
|
from followthemoney.exc import InvalidData
|
|
9
9
|
|
|
10
10
|
if TYPE_CHECKING:
|
|
@@ -22,8 +22,8 @@ class EntityType(PropertyType):
|
|
|
22
22
|
|
|
23
23
|
REGEX_RAW = r"^[0-9a-zA-Z]([0-9a-zA-Z\.\-]*[0-9a-zA-Z])?$"
|
|
24
24
|
REGEX = re.compile(REGEX_RAW)
|
|
25
|
-
name =
|
|
26
|
-
group =
|
|
25
|
+
name = "entity"
|
|
26
|
+
group = "entities"
|
|
27
27
|
label = _("Entity")
|
|
28
28
|
plural = _("Entities")
|
|
29
29
|
matchable = True
|
followthemoney/types/gender.py
CHANGED
|
@@ -2,7 +2,7 @@ from typing import Optional, TYPE_CHECKING
|
|
|
2
2
|
from babel.core import Locale
|
|
3
3
|
|
|
4
4
|
from followthemoney.types.common import EnumType, EnumValues
|
|
5
|
-
from followthemoney.util import
|
|
5
|
+
from followthemoney.util import gettext, defer as _
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from followthemoney.proxy import EntityProxy
|
|
@@ -14,9 +14,9 @@ class GenderType(EnumType):
|
|
|
14
14
|
government databases and represent it in a way that can be used by
|
|
15
15
|
structured tools. I'm not sure this justifies the simplification."""
|
|
16
16
|
|
|
17
|
-
MALE =
|
|
18
|
-
FEMALE =
|
|
19
|
-
OTHER =
|
|
17
|
+
MALE = "male"
|
|
18
|
+
FEMALE = "female"
|
|
19
|
+
OTHER = "other"
|
|
20
20
|
|
|
21
21
|
LOOKUP = {
|
|
22
22
|
"m": MALE,
|
|
@@ -34,8 +34,8 @@ class GenderType(EnumType):
|
|
|
34
34
|
"divers": OTHER,
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
name =
|
|
38
|
-
group =
|
|
37
|
+
name = "gender"
|
|
38
|
+
group = "genders"
|
|
39
39
|
label = _("Gender")
|
|
40
40
|
plural = _("Genders")
|
|
41
41
|
matchable = False
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import re
|
|
2
2
|
from typing import Optional, TYPE_CHECKING
|
|
3
|
-
from rigour.ids import
|
|
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
|
|
7
|
-
from followthemoney.util import
|
|
7
|
+
from followthemoney.util import defer as _
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from followthemoney.proxy import EntityProxy
|
|
@@ -20,8 +20,8 @@ class IdentifierType(PropertyType):
|
|
|
20
20
|
Four- or five-digit industry classifiers create more noise than value."""
|
|
21
21
|
|
|
22
22
|
COMPARE_CLEAN = re.compile(r"[\W_]+")
|
|
23
|
-
name =
|
|
24
|
-
group =
|
|
23
|
+
name = "identifier"
|
|
24
|
+
group = "identifiers"
|
|
25
25
|
label = _("Identifier")
|
|
26
26
|
plural = _("Identifiers")
|
|
27
27
|
matchable = True
|
|
@@ -35,8 +35,8 @@ class IdentifierType(PropertyType):
|
|
|
35
35
|
format: Optional[str] = None,
|
|
36
36
|
proxy: Optional["EntityProxy"] = None,
|
|
37
37
|
) -> Optional[str]:
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
65
|
-
|
|
64
|
+
format_ = get_identifier_format(format)
|
|
65
|
+
if format_ is not None:
|
|
66
66
|
return format_.format(value)
|
|
67
67
|
return value
|
followthemoney/types/ip.py
CHANGED
|
@@ -2,7 +2,7 @@ from typing import Optional, TYPE_CHECKING
|
|
|
2
2
|
from ipaddress import ip_address
|
|
3
3
|
|
|
4
4
|
from followthemoney.types.common import PropertyType
|
|
5
|
-
from followthemoney.util import
|
|
5
|
+
from followthemoney.util import defer as _
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from followthemoney.proxy import EntityProxy
|
|
@@ -13,8 +13,8 @@ class IpType(PropertyType):
|
|
|
13
13
|
by the protocol versions 4 (e.g. `192.168.1.143`) and 6
|
|
14
14
|
(e.g. `0:0:0:0:0:ffff:c0a8:18f`)."""
|
|
15
15
|
|
|
16
|
-
name =
|
|
17
|
-
group =
|
|
16
|
+
name = "ip"
|
|
17
|
+
group = "ips"
|
|
18
18
|
label = _("IP Address")
|
|
19
19
|
plural = _("IP Addresses")
|
|
20
20
|
matchable = True
|
followthemoney/types/json.py
CHANGED
|
@@ -3,7 +3,7 @@ from typing import Any, Optional, Sequence, TYPE_CHECKING
|
|
|
3
3
|
from banal import ensure_list
|
|
4
4
|
|
|
5
5
|
from followthemoney.types.common import PropertyType
|
|
6
|
-
from followthemoney.util import
|
|
6
|
+
from followthemoney.util import sanitize_text, defer as _
|
|
7
7
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
from followthemoney.proxy import EntityProxy
|
|
@@ -14,7 +14,7 @@ class JsonType(PropertyType):
|
|
|
14
14
|
and some other edge cases. It's a really bad idea and we should try to get rid
|
|
15
15
|
of JSON properties."""
|
|
16
16
|
|
|
17
|
-
name =
|
|
17
|
+
name = "json"
|
|
18
18
|
group = None
|
|
19
19
|
label = _("Nested data")
|
|
20
20
|
plural = _("Nested data")
|
followthemoney/types/language.py
CHANGED
|
@@ -4,7 +4,7 @@ from rigour.langs import iso_639_alpha3
|
|
|
4
4
|
|
|
5
5
|
from followthemoney.types.common import EnumType, EnumValues
|
|
6
6
|
from followthemoney.util import defer as _, gettext
|
|
7
|
-
from followthemoney.util import
|
|
7
|
+
from followthemoney.util import get_env_list
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
10
|
from followthemoney.proxy import EntityProxy
|
|
@@ -16,8 +16,8 @@ class LanguageType(EnumType):
|
|
|
16
16
|
for additional languages once there is a specific need for them to be
|
|
17
17
|
supported."""
|
|
18
18
|
|
|
19
|
-
name =
|
|
20
|
-
group =
|
|
19
|
+
name = "language"
|
|
20
|
+
group = "languages"
|
|
21
21
|
label = _("Language")
|
|
22
22
|
plural = _("Languages")
|
|
23
23
|
matchable = False
|
followthemoney/types/mimetype.py
CHANGED
|
@@ -3,7 +3,7 @@ from rigour.mime import normalize_mimetype, parse_mimetype
|
|
|
3
3
|
from rigour.mime import DEFAULT
|
|
4
4
|
|
|
5
5
|
from followthemoney.types.common import PropertyType
|
|
6
|
-
from followthemoney.util import
|
|
6
|
+
from followthemoney.util import defer as _
|
|
7
7
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
from followthemoney.proxy import EntityProxy
|
|
@@ -18,8 +18,8 @@ class MimeType(PropertyType):
|
|
|
18
18
|
MIME type properties do not contain parameters as used in HTTP headers,
|
|
19
19
|
like `charset=UTF-8`."""
|
|
20
20
|
|
|
21
|
-
name =
|
|
22
|
-
group =
|
|
21
|
+
name = "mimetype"
|
|
22
|
+
group = "mimetypes"
|
|
23
23
|
label = _("MIME-Type")
|
|
24
24
|
plural = _("MIME-Types")
|
|
25
25
|
matchable = False
|
followthemoney/types/name.py
CHANGED
|
@@ -7,7 +7,7 @@ from rigour.text.distance import levenshtein_similarity
|
|
|
7
7
|
|
|
8
8
|
from followthemoney.types.common import PropertyType
|
|
9
9
|
from followthemoney.util import dampen
|
|
10
|
-
from followthemoney.util import
|
|
10
|
+
from followthemoney.util import defer as _
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
13
|
from followthemoney.proxy import EntityProxy
|
|
@@ -21,8 +21,8 @@ class NameType(PropertyType):
|
|
|
21
21
|
No validation rules apply, and things having multiple names must be considered
|
|
22
22
|
a perfectly ordinary case."""
|
|
23
23
|
|
|
24
|
-
name =
|
|
25
|
-
group =
|
|
24
|
+
name = "name"
|
|
25
|
+
group = "names"
|
|
26
26
|
label = _("Name")
|
|
27
27
|
plural = _("Names")
|
|
28
28
|
matchable = True
|
followthemoney/types/number.py
CHANGED
|
@@ -2,7 +2,7 @@ import re
|
|
|
2
2
|
from typing import Optional, Tuple
|
|
3
3
|
|
|
4
4
|
from followthemoney.types.common import PropertyType
|
|
5
|
-
from followthemoney.util import
|
|
5
|
+
from followthemoney.util import defer as _
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class NumberType(PropertyType):
|
|
@@ -24,7 +24,7 @@ class NumberType(PropertyType):
|
|
|
24
24
|
_FLOAT_FMT = "{:" + SEPARATOR + "." + str(PRECISION) + "f}"
|
|
25
25
|
_INT_FMT = "{:" + SEPARATOR + "d}"
|
|
26
26
|
|
|
27
|
-
name =
|
|
27
|
+
name = "number"
|
|
28
28
|
label = _("Number")
|
|
29
29
|
plural = _("Numbers")
|
|
30
30
|
matchable = False
|
followthemoney/types/phone.py
CHANGED
|
@@ -6,7 +6,7 @@ from phonenumbers.phonenumberutil import region_code_for_number, NumberParseExce
|
|
|
6
6
|
|
|
7
7
|
from followthemoney.types.common import PropertyType
|
|
8
8
|
from followthemoney.util import defer as _
|
|
9
|
-
from followthemoney.util import
|
|
9
|
+
from followthemoney.util import dampen
|
|
10
10
|
|
|
11
11
|
if TYPE_CHECKING:
|
|
12
12
|
from followthemoney.proxy import EntityProxy
|
|
@@ -29,8 +29,8 @@ class PhoneType(PropertyType):
|
|
|
29
29
|
validation outcome from doing the two operations the other way around. Always
|
|
30
30
|
define the country first."""
|
|
31
31
|
|
|
32
|
-
name =
|
|
33
|
-
group =
|
|
32
|
+
name = "phone"
|
|
33
|
+
group = "phones"
|
|
34
34
|
label = _("Phone number")
|
|
35
35
|
plural = _("Phone numbers")
|
|
36
36
|
matchable = True
|
followthemoney/types/string.py
CHANGED
|
@@ -6,7 +6,7 @@ from followthemoney.util import MEGABYTE
|
|
|
6
6
|
class StringType(PropertyType):
|
|
7
7
|
"""A simple string property with no additional semantics."""
|
|
8
8
|
|
|
9
|
-
name =
|
|
9
|
+
name = "string"
|
|
10
10
|
label = _("Label")
|
|
11
11
|
plural = _("Labels")
|
|
12
12
|
matchable = False
|
|
@@ -21,7 +21,7 @@ class TextType(StringType):
|
|
|
21
21
|
string properties, it might make sense to treat properties of this type as
|
|
22
22
|
full-text search material."""
|
|
23
23
|
|
|
24
|
-
name =
|
|
24
|
+
name = "text"
|
|
25
25
|
label = _("Text")
|
|
26
26
|
plural = _("Texts")
|
|
27
27
|
total_size = 30 * MEGABYTE
|
followthemoney/types/topic.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from babel.core import Locale
|
|
2
2
|
|
|
3
3
|
from followthemoney.types.common import EnumType, EnumValues
|
|
4
|
-
from followthemoney.util import
|
|
4
|
+
from followthemoney.util import gettext, defer as _
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class TopicType(EnumType):
|
|
@@ -15,8 +15,8 @@ class TopicType(EnumType):
|
|
|
15
15
|
enable queries such as _find all paths between a government procurement
|
|
16
16
|
award and a politician_."""
|
|
17
17
|
|
|
18
|
-
name =
|
|
19
|
-
group =
|
|
18
|
+
name = "topic"
|
|
19
|
+
group = "topics"
|
|
20
20
|
label = _("Topic")
|
|
21
21
|
plural = _("Topics")
|
|
22
22
|
matchable = False
|
|
@@ -86,6 +86,8 @@ class TopicType(EnumType):
|
|
|
86
86
|
"sanction.linked": _("Sanction-linked entity"),
|
|
87
87
|
"sanction.counter": _("Counter-sanctioned entity"),
|
|
88
88
|
"export.control": _("Export controlled"),
|
|
89
|
+
# For BIS 50% rule:
|
|
90
|
+
"export.control.linked": _("Export control-linked"),
|
|
89
91
|
"export.risk": _("Trade risk"),
|
|
90
92
|
"debarment": _("Debarred entity"),
|
|
91
93
|
"poi": _("Person of interest"),
|
|
@@ -103,6 +105,7 @@ class TopicType(EnumType):
|
|
|
103
105
|
"crime",
|
|
104
106
|
"debarment",
|
|
105
107
|
"export.control",
|
|
108
|
+
"export.control.linked",
|
|
106
109
|
"export.risk",
|
|
107
110
|
"poi",
|
|
108
111
|
"mare.detained",
|
followthemoney/types/url.py
CHANGED
|
@@ -2,7 +2,7 @@ from typing import Optional, TYPE_CHECKING
|
|
|
2
2
|
from rigour.urls import clean_url, compare_urls
|
|
3
3
|
|
|
4
4
|
from followthemoney.types.common import PropertyType
|
|
5
|
-
from followthemoney.util import
|
|
5
|
+
from followthemoney.util import dampen, defer as _
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from followthemoney.proxy import EntityProxy
|
|
@@ -16,8 +16,8 @@ class UrlType(PropertyType):
|
|
|
16
16
|
SCHEMES = ("http", "https", "ftp", "mailto")
|
|
17
17
|
DEFAULT_SCHEME = "http"
|
|
18
18
|
|
|
19
|
-
name =
|
|
20
|
-
group =
|
|
19
|
+
name = "url"
|
|
20
|
+
group = "urls"
|
|
21
21
|
label = _("URL")
|
|
22
22
|
plural = _("URLs")
|
|
23
23
|
matchable = True
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: followthemoney
|
|
3
|
-
Version: 4.3.
|
|
3
|
+
Version: 4.3.2
|
|
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
|
|
@@ -50,7 +50,7 @@ Requires-Dist: pytz>=2021.1
|
|
|
50
50
|
Requires-Dist: pyyaml<7.0.0,>=5.0.0
|
|
51
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.
|
|
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,18 +1,18 @@
|
|
|
1
|
-
followthemoney/__init__.py,sha256=
|
|
2
|
-
followthemoney/compare.py,sha256=
|
|
1
|
+
followthemoney/__init__.py,sha256=kG5Rw6bsNjeqFk_uLfIEX5RmB6YTLuc5iQJG_ZnqUII,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
6
|
followthemoney/helpers.py,sha256=KCdv1XAE7KQEXBiXp52Kvuck7wMaeNVBM3uaFemcvb4,7873
|
|
7
7
|
followthemoney/messages.py,sha256=zUEa9CFecU8nRafIzhN6TKCh1kEihiIyIS1qr8PxY4g,806
|
|
8
|
-
followthemoney/model.py,sha256=
|
|
8
|
+
followthemoney/model.py,sha256=chAUGob5tXWS0o8f0X6mSFCCnI2HoHE5pXU9O5ukrpc,7447
|
|
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=
|
|
13
|
-
followthemoney/proxy.py,sha256=
|
|
12
|
+
followthemoney/property.py,sha256=bIkSEAMbxCxZUV4ze_65edZjR9CI7WWvy-7CS32hgSk,8303
|
|
13
|
+
followthemoney/proxy.py,sha256=nahd9lLZzum_-QEchqydinDa1Zg5_Ffl0fyo35BNncQ,19707
|
|
14
14
|
followthemoney/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
followthemoney/schema.py,sha256=
|
|
15
|
+
followthemoney/schema.py,sha256=ylyL7mBx7TG86OlZmXg6y4sgpEiIUBZUPy9q83hMHrc,18149
|
|
16
16
|
followthemoney/util.py,sha256=LoCSp1iE6VwXjotCkBXFRppeQs55726GzOuNIu3CvRE,4409
|
|
17
17
|
followthemoney/value.py,sha256=BJ4Sj5Tg2kMrslR6FjQUr96d8Kt75U7ny9NgzVGT0ZE,2335
|
|
18
18
|
followthemoney/cli/__init__.py,sha256=0mmz84uhXRp2qUn3syKnDXofU3MMAAe291s7htqX0Bg,187
|
|
@@ -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=
|
|
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
|
|
@@ -117,7 +117,7 @@ followthemoney/statement/__init__.py,sha256=7m2VUCAuqNZXIY0WFJRFkw5UG14QuxATL4f_
|
|
|
117
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
|
-
followthemoney/statement/util.py,sha256=
|
|
120
|
+
followthemoney/statement/util.py,sha256=QMYSwAcnh2fCM1LtH_-v8Z5GdwOZfUTT1UkQ_ZMQ470,797
|
|
121
121
|
followthemoney/translations/messages.pot,sha256=JhtY9NJ9wP_EAX4APxOqMyvKcX53oIC9kAxBsliJkf4,107703
|
|
122
122
|
followthemoney/translations/ar/LC_MESSAGES/followthemoney.mo,sha256=uhb2crSNh8K2ts_QUeD2wvgWgzzpLJWRzXok-Uyx3Zk,38795
|
|
123
123
|
followthemoney/translations/ar/LC_MESSAGES/followthemoney.po,sha256=DuIfvR5v0sPGwFbeg3y6_jCbeglvHWXQ2LDH6prfwLc,121326
|
|
@@ -142,27 +142,27 @@ 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=
|
|
146
|
-
followthemoney/types/checksum.py,sha256=
|
|
145
|
+
followthemoney/types/address.py,sha256=Gc-hqz00dRRkeANqkyPD2wtt7ksR9wMf4CX-U-5XvMo,2214
|
|
146
|
+
followthemoney/types/checksum.py,sha256=_0ev2Wwtd4iX_bLz0Lu-xcJIxNfH_V9kBKKtuZhoAwg,802
|
|
147
147
|
followthemoney/types/common.py,sha256=4ks7zPT8rknrGSd4JFc1zRkS-TL4SX-25_ZbjcVDos0,10081
|
|
148
|
-
followthemoney/types/country.py,sha256=
|
|
149
|
-
followthemoney/types/date.py,sha256=
|
|
148
|
+
followthemoney/types/country.py,sha256=X3Z1j6rIiCITpLtpFXwjTIh9uJwI99_gmPMJx8Jsq2w,1512
|
|
149
|
+
followthemoney/types/date.py,sha256=O3Xav9QNBqjy7LuUWiZrUdGrOvwwOdk6ea5qQEStIwQ,3084
|
|
150
150
|
followthemoney/types/email.py,sha256=L3RTYrMABlNQF7hCynXGfzoj6YNEHW5JAY_BwuhoZdA,3375
|
|
151
|
-
followthemoney/types/entity.py,sha256=
|
|
152
|
-
followthemoney/types/gender.py,sha256=
|
|
153
|
-
followthemoney/types/identifier.py,sha256=
|
|
154
|
-
followthemoney/types/ip.py,sha256=
|
|
155
|
-
followthemoney/types/json.py,sha256=
|
|
156
|
-
followthemoney/types/language.py,sha256=
|
|
157
|
-
followthemoney/types/mimetype.py,sha256=
|
|
158
|
-
followthemoney/types/name.py,sha256=
|
|
159
|
-
followthemoney/types/number.py,sha256=
|
|
160
|
-
followthemoney/types/phone.py,sha256=
|
|
161
|
-
followthemoney/types/string.py,sha256=
|
|
162
|
-
followthemoney/types/topic.py,sha256=
|
|
163
|
-
followthemoney/types/url.py,sha256=
|
|
164
|
-
followthemoney-4.3.
|
|
165
|
-
followthemoney-4.3.
|
|
166
|
-
followthemoney-4.3.
|
|
167
|
-
followthemoney-4.3.
|
|
168
|
-
followthemoney-4.3.
|
|
151
|
+
followthemoney/types/entity.py,sha256=56h6x8Ct7hWZIC3BjZHmRKGy9Ff2vuULNWH3xDRsKiU,2317
|
|
152
|
+
followthemoney/types/gender.py,sha256=XY9us98Sk25O1xnHN-88tbv9pHy6Mn7SR8GRYi6v5gI,1683
|
|
153
|
+
followthemoney/types/identifier.py,sha256=TYJwE7urjHFxEcDuiZMxGoCN6n34rAIdCt5_96Y7vI0,2198
|
|
154
|
+
followthemoney/types/ip.py,sha256=rCXkRrh_jDeWAhswCgSe6Z4uhIW7yvLAxIEw4x1SM3A,1279
|
|
155
|
+
followthemoney/types/json.py,sha256=Hefwns1-ziJf310MWvdfX5ICkOgj9cnnMJuqq1e6qKY,1676
|
|
156
|
+
followthemoney/types/language.py,sha256=JDFCO9g9lvgKihhYTz6e7TbJd3V9RTGJlS8kDn6aSCY,2726
|
|
157
|
+
followthemoney/types/mimetype.py,sha256=oqVP8EfGckPAI3WAziHomp6oUN7KXdIPWzGZPsRtIA8,1242
|
|
158
|
+
followthemoney/types/name.py,sha256=zd0aC4VGp1SYUI8Rj0-ZXlrpUI7ZcnJIljZqsEsV-CY,2363
|
|
159
|
+
followthemoney/types/number.py,sha256=vpAyhmc7UQlIm8h7Z5k8k4cTk37ykRF-AgYA1r_g1QQ,3934
|
|
160
|
+
followthemoney/types/phone.py,sha256=_HanfxxTV7jp75gZO2evBc9HWwQTxEMQRaoVDcoXDIQ,3790
|
|
161
|
+
followthemoney/types/string.py,sha256=SEh3xqQCnm377PGvwfR6ao85pHJCNeCUWBKnvccrJ7I,1216
|
|
162
|
+
followthemoney/types/topic.py,sha256=9FIH_WmwVOFg1CJRBF4KeE6vNTn-QQkzsKU5XaMqNJ0,4604
|
|
163
|
+
followthemoney/types/url.py,sha256=sSHKtzvm4kc-VTvNCPIDykOG1hUoawhORj6Bklo0a2A,1434
|
|
164
|
+
followthemoney-4.3.2.dist-info/METADATA,sha256=mbUEVQBE2Sx9c9SjZjHWnRreRI0HPuDZ5k-KnNhF8uo,6747
|
|
165
|
+
followthemoney-4.3.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
166
|
+
followthemoney-4.3.2.dist-info/entry_points.txt,sha256=caoFTlf213jhg5sz3TNSofutjUTzaKtWATuSIdd9Cps,653
|
|
167
|
+
followthemoney-4.3.2.dist-info/licenses/LICENSE,sha256=H6_EVXisnJC0-18CjXIaqrBSFq_VH3OnS7u3dccOv6g,1148
|
|
168
|
+
followthemoney-4.3.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|