followthemoney 4.0.0__py3-none-any.whl → 4.0.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.
- followthemoney/__init__.py +1 -1
- followthemoney/dataset/dataset.py +19 -12
- followthemoney/dataset/publisher.py +4 -4
- followthemoney/dataset/resource.py +9 -6
- followthemoney/dataset/util.py +13 -16
- followthemoney/entity.py +13 -8
- followthemoney/model.py +2 -2
- followthemoney/names.py +33 -0
- followthemoney/proxy.py +1 -1
- followthemoney/schema.py +3 -3
- followthemoney/statement/entity.py +3 -2
- followthemoney/statement/serialize.py +9 -3
- followthemoney/value.py +3 -3
- {followthemoney-4.0.0.dist-info → followthemoney-4.0.2.dist-info}/METADATA +1 -1
- {followthemoney-4.0.0.dist-info → followthemoney-4.0.2.dist-info}/RECORD +18 -17
- {followthemoney-4.0.0.dist-info → followthemoney-4.0.2.dist-info}/WHEEL +0 -0
- {followthemoney-4.0.0.dist-info → followthemoney-4.0.2.dist-info}/entry_points.txt +0 -0
- {followthemoney-4.0.0.dist-info → followthemoney-4.0.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.0.
|
|
12
|
+
__version__ = "4.0.2"
|
|
13
13
|
|
|
14
14
|
# Data model singleton
|
|
15
15
|
model = Model.instance()
|
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
import yaml
|
|
2
2
|
import logging
|
|
3
|
-
from datetime import datetime
|
|
4
3
|
from functools import cached_property
|
|
5
4
|
from typing import TYPE_CHECKING
|
|
6
5
|
from typing_extensions import Self
|
|
7
6
|
from typing import Any, Dict, List, Optional, Set, Type, TypeVar
|
|
8
|
-
from pydantic import BaseModel,
|
|
7
|
+
from pydantic import BaseModel, field_validator, model_validator
|
|
9
8
|
|
|
10
9
|
from followthemoney.dataset.coverage import DataCoverage
|
|
11
10
|
from followthemoney.dataset.publisher import DataPublisher
|
|
12
11
|
from followthemoney.dataset.resource import DataResource
|
|
13
|
-
from followthemoney.dataset.util import
|
|
12
|
+
from followthemoney.dataset.util import Url, DateTimeISO, dataset_name_check
|
|
14
13
|
from followthemoney.util import PathLike
|
|
15
14
|
|
|
16
15
|
if TYPE_CHECKING:
|
|
17
16
|
from followthemoney.dataset.catalog import DataCatalog
|
|
18
17
|
|
|
19
18
|
DS = TypeVar("DS", bound="Dataset")
|
|
19
|
+
|
|
20
20
|
log = logging.getLogger(__name__)
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
class DatasetModel(BaseModel):
|
|
24
24
|
name: str
|
|
25
25
|
title: str
|
|
26
|
-
license: Optional[
|
|
26
|
+
license: Optional[Url] = None
|
|
27
27
|
summary: Optional[str] = None
|
|
28
28
|
description: Optional[str] = None
|
|
29
|
-
url: Optional[
|
|
30
|
-
updated_at: Optional[
|
|
31
|
-
last_export: Optional[
|
|
29
|
+
url: Optional[Url] = None
|
|
30
|
+
updated_at: Optional[DateTimeISO] = None
|
|
31
|
+
last_export: Optional[DateTimeISO] = None
|
|
32
32
|
entity_count: Optional[int] = None
|
|
33
33
|
thing_count: Optional[int] = None
|
|
34
34
|
version: Optional[str] = None
|
|
@@ -64,15 +64,13 @@ class DatasetModel(BaseModel):
|
|
|
64
64
|
raise ValueError("No resource named %r!" % name)
|
|
65
65
|
|
|
66
66
|
|
|
67
|
-
class Dataset
|
|
67
|
+
class Dataset:
|
|
68
68
|
"""A container for entities, often from one source or related to one topic.
|
|
69
69
|
A dataset is a set of data, sez W3C."""
|
|
70
70
|
|
|
71
|
-
Model = DatasetModel
|
|
72
|
-
|
|
73
71
|
def __init__(self: Self, data: Dict[str, Any]) -> None:
|
|
74
|
-
self.model =
|
|
75
|
-
|
|
72
|
+
self.model = DatasetModel.model_validate(data)
|
|
73
|
+
self.name = self.model.name
|
|
76
74
|
self.children: Set[Self] = set()
|
|
77
75
|
|
|
78
76
|
@cached_property
|
|
@@ -135,3 +133,12 @@ class Dataset(Named):
|
|
|
135
133
|
|
|
136
134
|
catalog = DataCatalog(cls, {})
|
|
137
135
|
return catalog.make_dataset(data)
|
|
136
|
+
|
|
137
|
+
def __eq__(self, other: Any) -> bool:
|
|
138
|
+
try:
|
|
139
|
+
return not not self.name == other.name
|
|
140
|
+
except AttributeError:
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
def __lt__(self, other: Any) -> bool:
|
|
144
|
+
return self.name.__lt__(other.name)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
|
|
3
|
-
from pydantic import BaseModel
|
|
3
|
+
from pydantic import BaseModel
|
|
4
4
|
|
|
5
|
-
from followthemoney.dataset.util import CountryCode
|
|
5
|
+
from followthemoney.dataset.util import CountryCode, Url
|
|
6
6
|
from followthemoney.types import registry
|
|
7
7
|
|
|
8
8
|
|
|
@@ -10,13 +10,13 @@ class DataPublisher(BaseModel):
|
|
|
10
10
|
"""Publisher information, eg. the government authority."""
|
|
11
11
|
|
|
12
12
|
name: str
|
|
13
|
-
url: Optional[
|
|
13
|
+
url: Optional[Url] = None
|
|
14
14
|
name_en: Optional[str] = None
|
|
15
15
|
acronym: Optional[str] = None
|
|
16
16
|
description: Optional[str] = None
|
|
17
17
|
country: Optional[CountryCode] = None
|
|
18
18
|
official: Optional[bool] = False
|
|
19
|
-
logo_url: Optional[
|
|
19
|
+
logo_url: Optional[Url] = None
|
|
20
20
|
|
|
21
21
|
@property
|
|
22
22
|
def country_label(self) -> Optional[str]:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
from datetime import datetime
|
|
2
1
|
from typing import Optional
|
|
3
|
-
from pydantic import BaseModel,
|
|
2
|
+
from pydantic import BaseModel, field_validator, computed_field
|
|
4
3
|
|
|
4
|
+
from followthemoney.dataset.util import Url, DateTimeISO
|
|
5
5
|
from followthemoney.types import registry
|
|
6
6
|
|
|
7
7
|
|
|
@@ -9,9 +9,9 @@ class DataResource(BaseModel):
|
|
|
9
9
|
"""A downloadable resource that is part of a dataset."""
|
|
10
10
|
|
|
11
11
|
name: str
|
|
12
|
-
url: Optional[
|
|
12
|
+
url: Optional[Url] = None
|
|
13
13
|
checksum: Optional[str] = None
|
|
14
|
-
timestamp: Optional[
|
|
14
|
+
timestamp: Optional[DateTimeISO] = None
|
|
15
15
|
mime_type: Optional[str] = None
|
|
16
16
|
title: Optional[str] = None
|
|
17
17
|
size: Optional[int] = None
|
|
@@ -19,10 +19,13 @@ class DataResource(BaseModel):
|
|
|
19
19
|
@field_validator("mime_type", mode="after")
|
|
20
20
|
@classmethod
|
|
21
21
|
def ensure_mime_type(cls, value: str) -> Optional[str]:
|
|
22
|
-
|
|
22
|
+
cleaned = registry.mimetype.clean_text(value)
|
|
23
|
+
if cleaned is None:
|
|
23
24
|
raise ValueError(f"Invalid MIME type: {value!r}")
|
|
24
|
-
return
|
|
25
|
+
return cleaned
|
|
25
26
|
|
|
27
|
+
# Re: the type: ignore, see https://github.com/python/mypy/issues/1362 and https://docs.pydantic.dev/2.0/usage/computed_fields/
|
|
28
|
+
@computed_field # type: ignore[prop-decorator]
|
|
26
29
|
@property
|
|
27
30
|
def mime_type_label(self) -> Optional[str]:
|
|
28
31
|
if self.mime_type is None:
|
followthemoney/dataset/util.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
from datetime import datetime
|
|
1
2
|
from normality import slugify
|
|
2
3
|
from typing import Annotated, Any
|
|
3
|
-
from
|
|
4
|
+
from rigour.time import datetime_iso
|
|
5
|
+
from pydantic import AfterValidator, BeforeValidator, HttpUrl, PlainSerializer
|
|
4
6
|
|
|
5
7
|
from followthemoney.types import registry
|
|
6
8
|
|
|
@@ -36,23 +38,18 @@ def type_check_country(value: Any) -> str:
|
|
|
36
38
|
CountryCode = Annotated[str, BeforeValidator(type_check_country)]
|
|
37
39
|
|
|
38
40
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
def type_check_http_url(v: str) -> str:
|
|
42
|
+
url = HttpUrl(v)
|
|
43
|
+
return str(url)
|
|
41
44
|
|
|
42
|
-
def __init__(self, name: str) -> None:
|
|
43
|
-
self.name = name
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
try:
|
|
47
|
-
return not not self.name == other.name
|
|
48
|
-
except AttributeError:
|
|
49
|
-
return False
|
|
46
|
+
Url = Annotated[str, AfterValidator(type_check_http_url)]
|
|
50
47
|
|
|
51
|
-
def __lt__(self, other: Any) -> bool:
|
|
52
|
-
return self.name.__lt__(other.name)
|
|
53
48
|
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
def serialize_dt(dt: datetime) -> str:
|
|
50
|
+
text = datetime_iso(dt)
|
|
51
|
+
assert text is not None, "Invalid datetime: %r" % dt
|
|
52
|
+
return text
|
|
56
53
|
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
|
|
55
|
+
DateTimeISO = Annotated[datetime, PlainSerializer(serialize_dt)]
|
followthemoney/entity.py
CHANGED
|
@@ -44,15 +44,20 @@ class ValueEntity(EntityProxy):
|
|
|
44
44
|
if stmt_data["prop"] != BASE_ID:
|
|
45
45
|
self.add(stmt_data["prop"], stmt_data["value"])
|
|
46
46
|
|
|
47
|
-
def merge(self:
|
|
47
|
+
def merge(self: VE, other: EntityProxy) -> VE:
|
|
48
48
|
merged = super().merge(other)
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
49
|
+
if isinstance(other, ValueEntity):
|
|
50
|
+
merged._caption = pick_name(_defined(self._caption, other._caption))
|
|
51
|
+
merged.referents.update(other.referents)
|
|
52
|
+
merged.datasets.update(other.datasets)
|
|
53
|
+
merged.first_seen = min(
|
|
54
|
+
_defined(self.first_seen, other.first_seen), default=None
|
|
55
|
+
)
|
|
56
|
+
merged.last_seen = max(
|
|
57
|
+
_defined(self.last_seen, other.last_seen), default=None
|
|
58
|
+
)
|
|
59
|
+
changed = _defined(self.last_change, other.last_change)
|
|
60
|
+
merged.last_change = max(changed, default=None)
|
|
56
61
|
return merged
|
|
57
62
|
|
|
58
63
|
def to_dict(self) -> Dict[str, Any]:
|
followthemoney/model.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import yaml
|
|
3
|
-
from functools import
|
|
3
|
+
from functools import cache
|
|
4
4
|
from typing import TYPE_CHECKING, Any
|
|
5
5
|
from typing import Dict, Generator, Iterator, Optional, Set, TypedDict, Union
|
|
6
6
|
|
|
@@ -118,7 +118,7 @@ class Model(object):
|
|
|
118
118
|
for entity in gen.map(record).values():
|
|
119
119
|
yield entity
|
|
120
120
|
|
|
121
|
-
@
|
|
121
|
+
@cache
|
|
122
122
|
def common_schema(
|
|
123
123
|
self, left: Union[str, Schema], right: Union[str, Schema]
|
|
124
124
|
) -> Schema:
|
followthemoney/names.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from rigour.names import NamePartTag, NameTypeTag
|
|
2
|
+
|
|
3
|
+
from followthemoney.schema import Schema
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# Define the mapping of property names to name part tags.
|
|
7
|
+
# This is used to tag the parts of the name with their type by using
|
|
8
|
+
# `Name.tag_text` with the value of the property to mark name parts.
|
|
9
|
+
PROP_PART_TAGS = (
|
|
10
|
+
("firstName", NamePartTag.GIVEN),
|
|
11
|
+
("lastName", NamePartTag.FAMILY),
|
|
12
|
+
("secondName", NamePartTag.MIDDLE),
|
|
13
|
+
("middleName", NamePartTag.MIDDLE),
|
|
14
|
+
("fatherName", NamePartTag.PATRONYMIC),
|
|
15
|
+
("motherName", NamePartTag.MATRONYMIC),
|
|
16
|
+
("title", NamePartTag.HONORIFIC),
|
|
17
|
+
("nameSuffix", NamePartTag.SUFFIX),
|
|
18
|
+
("weakAlias", NamePartTag.NICK),
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def schema_type_tag(schema: Schema) -> NameTypeTag:
|
|
23
|
+
"""Return the name type tag for the given schema."""
|
|
24
|
+
if schema.is_a("Person"):
|
|
25
|
+
return NameTypeTag.PER
|
|
26
|
+
elif schema.is_a("Organization"):
|
|
27
|
+
return NameTypeTag.ORG
|
|
28
|
+
elif schema.is_a("LegalEntity"):
|
|
29
|
+
return NameTypeTag.ENT
|
|
30
|
+
elif schema.name in ("Vessel", "Asset", "Airplane", "Security"):
|
|
31
|
+
return NameTypeTag.OBJ
|
|
32
|
+
else:
|
|
33
|
+
return NameTypeTag.UNK
|
followthemoney/proxy.py
CHANGED
|
@@ -421,7 +421,7 @@ class EntityProxy(object):
|
|
|
421
421
|
"""Make a deep copy of the current entity proxy."""
|
|
422
422
|
return self.__class__.from_dict(self.to_dict())
|
|
423
423
|
|
|
424
|
-
def merge(self: E, other:
|
|
424
|
+
def merge(self: E, other: "EntityProxy") -> E:
|
|
425
425
|
"""Merge another entity proxy into this one. This will try and find
|
|
426
426
|
the common schema between both entities and then add all property
|
|
427
427
|
values from the other entity into this one."""
|
followthemoney/schema.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, Any, cast
|
|
2
2
|
from typing import Dict, List, Optional, Set, TypedDict, Union
|
|
3
3
|
from banal import ensure_list, ensure_dict, as_bool
|
|
4
|
-
from functools import
|
|
4
|
+
from functools import cache
|
|
5
5
|
|
|
6
6
|
from followthemoney.property import Property, PropertySpec, PropertyToDict, ReverseSpec
|
|
7
7
|
from followthemoney.types import registry
|
|
@@ -382,12 +382,12 @@ class Schema:
|
|
|
382
382
|
self._matchable_schemata.add(schema)
|
|
383
383
|
return self._matchable_schemata
|
|
384
384
|
|
|
385
|
-
@
|
|
385
|
+
@cache
|
|
386
386
|
def can_match(self, other: "Schema") -> bool:
|
|
387
387
|
"""Check if an schema can match with another schema."""
|
|
388
388
|
return other in self.matchable_schemata
|
|
389
389
|
|
|
390
|
-
@
|
|
390
|
+
@cache
|
|
391
391
|
def is_a(self, other: Union[str, "Schema"]) -> bool:
|
|
392
392
|
"""Check if the schema or one of its parents is the same as the given
|
|
393
393
|
candidate ``other``."""
|
|
@@ -147,7 +147,7 @@ class StatementEntity(EntityProxy):
|
|
|
147
147
|
|
|
148
148
|
def add_statement(self, stmt: Statement) -> None:
|
|
149
149
|
schema = self.schema
|
|
150
|
-
if not schema.is_a(stmt.schema):
|
|
150
|
+
if schema.name != stmt.schema and not schema.is_a(stmt.schema):
|
|
151
151
|
try:
|
|
152
152
|
self.schema = schema.model.common_schema(schema, stmt.schema)
|
|
153
153
|
except InvalidData as exc:
|
|
@@ -163,7 +163,8 @@ class StatementEntity(EntityProxy):
|
|
|
163
163
|
else:
|
|
164
164
|
self.last_change = max(self.last_change, stmt.first_seen)
|
|
165
165
|
else:
|
|
166
|
-
|
|
166
|
+
if stmt.prop not in self._statements:
|
|
167
|
+
self._statements[stmt.prop] = set()
|
|
167
168
|
self._statements[stmt.prop].add(stmt)
|
|
168
169
|
|
|
169
170
|
def get(self, prop: P, quiet: bool = False) -> List[str]:
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import csv
|
|
2
2
|
import click
|
|
3
3
|
import orjson
|
|
4
|
+
import logging
|
|
4
5
|
from io import TextIOWrapper
|
|
5
6
|
from pathlib import Path
|
|
6
7
|
from types import TracebackType
|
|
@@ -11,6 +12,7 @@ from rigour.boolean import text_bool
|
|
|
11
12
|
from followthemoney.statement.statement import Statement, StatementDict
|
|
12
13
|
from followthemoney.statement.util import unpack_prop
|
|
13
14
|
|
|
15
|
+
log = logging.getLogger(__name__)
|
|
14
16
|
|
|
15
17
|
JSON = "json"
|
|
16
18
|
CSV = "csv"
|
|
@@ -85,15 +87,19 @@ def read_pack_statements_decoded(fh: TextIO) -> Generator[Statement, None, None]
|
|
|
85
87
|
headers = LEGACY_PACK_COLUMNS
|
|
86
88
|
continue
|
|
87
89
|
data = dict(zip(headers, row))
|
|
88
|
-
|
|
90
|
+
try:
|
|
91
|
+
schema, _, prop = unpack_prop(data["prop"])
|
|
92
|
+
except TypeError:
|
|
93
|
+
log.error("Invalid property in pack statement: %s" % data["prop"])
|
|
94
|
+
continue
|
|
89
95
|
yield Statement(
|
|
90
96
|
entity_id=data["entity_id"],
|
|
91
97
|
prop=prop,
|
|
92
98
|
schema=schema,
|
|
93
99
|
value=data["value"],
|
|
94
100
|
dataset=data["dataset"],
|
|
95
|
-
lang=data
|
|
96
|
-
original_value=data
|
|
101
|
+
lang=data["lang"] or None,
|
|
102
|
+
original_value=data["original_value"] or None,
|
|
97
103
|
origin=data.get("origin"),
|
|
98
104
|
first_seen=data["first_seen"],
|
|
99
105
|
external=data["external"] == "t",
|
followthemoney/value.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any, List, Mapping,
|
|
1
|
+
from typing import Any, Iterable, List, Mapping, Union
|
|
2
2
|
from datetime import datetime, date, timezone
|
|
3
3
|
import typing
|
|
4
4
|
from prefixdate import DatePrefix
|
|
@@ -9,7 +9,7 @@ if typing.TYPE_CHECKING:
|
|
|
9
9
|
from followthemoney.proxy import EntityProxy
|
|
10
10
|
|
|
11
11
|
Value = Union[str, int, float, bool, date, datetime, DatePrefix, None, "EntityProxy"]
|
|
12
|
-
Values = Union[Value,
|
|
12
|
+
Values = Union[Value, Iterable[Value]]
|
|
13
13
|
|
|
14
14
|
|
|
15
15
|
def string_list(value: Any, sanitize: bool = False) -> List[str]:
|
|
@@ -59,7 +59,7 @@ def string_list(value: Any, sanitize: bool = False) -> List[str]:
|
|
|
59
59
|
if text is None:
|
|
60
60
|
return []
|
|
61
61
|
return [text]
|
|
62
|
-
if isinstance(value,
|
|
62
|
+
if isinstance(value, Iterable):
|
|
63
63
|
stexts: List[str] = []
|
|
64
64
|
for inner in value:
|
|
65
65
|
stexts.extend(string_list(inner, sanitize=sanitize))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: followthemoney
|
|
3
|
-
Version: 4.0.
|
|
3
|
+
Version: 4.0.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
|
|
@@ -1,19 +1,20 @@
|
|
|
1
|
-
followthemoney/__init__.py,sha256
|
|
1
|
+
followthemoney/__init__.py,sha256=-I2qZRQKtvRnPSxLWfOD52OIlJbNTtpNw09EsNvJnmw,856
|
|
2
2
|
followthemoney/compare.py,sha256=rtITMzJOXLDOSj7yKPfOxFaknIu6kRpiLDIM22zakpI,5619
|
|
3
|
-
followthemoney/entity.py,sha256=
|
|
3
|
+
followthemoney/entity.py,sha256=9wLKE3iFapxRQWOs_OAMzK3wtklf2HXaHaMYydIInWE,3045
|
|
4
4
|
followthemoney/exc.py,sha256=GyMgwY4QVm87hLevDfV7gM1MJsDqfNCi_UQw7F_A8X8,858
|
|
5
5
|
followthemoney/graph.py,sha256=7X1CGHGvmktS2LSZqld2iXWzG7B831eCNYyBqamqEJ8,10921
|
|
6
6
|
followthemoney/helpers.py,sha256=Btb6BlHg_c-qCXZo-NP_LURKG-qu-QD3Fj1ev_c7Xic,7956
|
|
7
7
|
followthemoney/messages.py,sha256=zUEa9CFecU8nRafIzhN6TKCh1kEihiIyIS1qr8PxY4g,806
|
|
8
|
-
followthemoney/model.py,sha256=
|
|
8
|
+
followthemoney/model.py,sha256=bWFVNa-DhYzc8BdSXBZdG2ev6Nh9uHx6i4tin8DvEEU,7374
|
|
9
|
+
followthemoney/names.py,sha256=LODQqExKEHdH4z6Mmbhlm0KeKRzGcptaSWzYXZ7lONI,1120
|
|
9
10
|
followthemoney/namespace.py,sha256=cp7X8aGaZ8HHf7SOfHr2vJHPI2todz2DoyLdiZLNMyg,4472
|
|
10
11
|
followthemoney/ontology.py,sha256=WWY_PYQGl5Ket4zZBuZglzQxD2Bh9UqHok6GJNNX7GA,3001
|
|
11
12
|
followthemoney/property.py,sha256=RDTzTXJeeLFLptQL1_gr1S1T-vdDe-8MGMwsRaGQh0I,7665
|
|
12
|
-
followthemoney/proxy.py,sha256=
|
|
13
|
+
followthemoney/proxy.py,sha256=KhByvSQk1BrZxTinKjjUsbEuD96RveQQ4LRzQIz6pUA,19617
|
|
13
14
|
followthemoney/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
14
|
-
followthemoney/schema.py,sha256=
|
|
15
|
+
followthemoney/schema.py,sha256=WYnPE4Lego0pJHlojECEv0aO9Miw_YIvEb35HoDo4Zk,18087
|
|
15
16
|
followthemoney/util.py,sha256=DhhcRilSetZpzvCew56AE6zNwenW5a4Y-KtmKM43rjc,4447
|
|
16
|
-
followthemoney/value.py,sha256=
|
|
17
|
+
followthemoney/value.py,sha256=BJ4Sj5Tg2kMrslR6FjQUr96d8Kt75U7ny9NgzVGT0ZE,2335
|
|
17
18
|
followthemoney/cli/__init__.py,sha256=0mmz84uhXRp2qUn3syKnDXofU3MMAAe291s7htqX0Bg,187
|
|
18
19
|
followthemoney/cli/aggregate.py,sha256=xQTFpU3cVVj7fplpX4OJVrRlTVpn6b9kBr_Vb87pKfg,2164
|
|
19
20
|
followthemoney/cli/cli.py,sha256=cWSQIrMS0b40uzIveoIfR9CEBbQEwcfonYhDTpioqBM,3584
|
|
@@ -25,10 +26,10 @@ followthemoney/cli/util.py,sha256=C3nGMVY3-9JHSFLn3AGvTNcAdvGcgfFS-7jXIzKg6Ik,47
|
|
|
25
26
|
followthemoney/dataset/__init__.py,sha256=rOKsI39dccDaYcSa7ASoNKkhmbFYUArxMCRqtrxy2iE,477
|
|
26
27
|
followthemoney/dataset/catalog.py,sha256=bIpxr0jvJeutNSmCaXREQac7TyvZak2Y_QoCFdCM0d4,3001
|
|
27
28
|
followthemoney/dataset/coverage.py,sha256=rBnKs7VngCtIuaDqrF5D0ygCHg8NAMkYbmtl7336PSI,724
|
|
28
|
-
followthemoney/dataset/dataset.py,sha256=
|
|
29
|
-
followthemoney/dataset/publisher.py,sha256=
|
|
30
|
-
followthemoney/dataset/resource.py,sha256=
|
|
31
|
-
followthemoney/dataset/util.py,sha256=
|
|
29
|
+
followthemoney/dataset/dataset.py,sha256=wWUzWsdzDW9qXLy8lS6Bpy08WMcaNU30oiMXU8jfo14,4724
|
|
30
|
+
followthemoney/dataset/publisher.py,sha256=eHbguTyDRVRC0ohD6phaLIm5d9Y-eJK6XYIQaelrnN4,694
|
|
31
|
+
followthemoney/dataset/resource.py,sha256=S_-tNjMwHQ8LcSOsZO_xhXD-vLK90wyxtIRBbyCJ0Xo,1164
|
|
32
|
+
followthemoney/dataset/util.py,sha256=ajUIBRF64dizdgy9LAp2abvFXRFOWCqQX9sDbToWFYo,1607
|
|
32
33
|
followthemoney/export/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
33
34
|
followthemoney/export/common.py,sha256=5b-Qlu3MaA0kSzzMAP93FAWncpgiioENnCnHikWYxhs,1021
|
|
34
35
|
followthemoney/export/csv.py,sha256=reWq1jYIv7sY2PEI4JwIxahYNNqnSiPfMCS3kQX4RZ8,2652
|
|
@@ -112,8 +113,8 @@ followthemoney/schema/Vessel.yaml,sha256=nFaUJ_0BzFJstvog1iDvwV9DHKHr9ky4DLb1NZG
|
|
|
112
113
|
followthemoney/schema/Video.yaml,sha256=LY3DYMWTHXiAhL0hxBCNCz50cp2sPbUlEhhig5Fbjos,327
|
|
113
114
|
followthemoney/schema/Workbook.yaml,sha256=iikWPElz4klA7SkWH7eae6xqhbkMCIP_3zdeXzFEMU0,354
|
|
114
115
|
followthemoney/statement/__init__.py,sha256=PvhLPhmQrezBKCe8rEwJlyTWlrnCzSfyfchVc8gXXEA,568
|
|
115
|
-
followthemoney/statement/entity.py,sha256=
|
|
116
|
-
followthemoney/statement/serialize.py,sha256=
|
|
116
|
+
followthemoney/statement/entity.py,sha256=92tOai7Yt5GZkOylZcy7866P0iLJsYEzmt-2T7WbXMg,15540
|
|
117
|
+
followthemoney/statement/serialize.py,sha256=9eXzQ1biR2mSxWRID5C7xDdku4b4ZImHeRJ53yLZ0yo,7225
|
|
117
118
|
followthemoney/statement/statement.py,sha256=Ae-EYuzS8S12BkaRqrvMuI1C7YwlRKa5C_pTBELyNMM,8029
|
|
118
119
|
followthemoney/statement/util.py,sha256=B-ozuRc1TWvpop52873Pqt5OPj8H6uk4KyRJLfAhr10,780
|
|
119
120
|
followthemoney/translations/messages.pot,sha256=JhtY9NJ9wP_EAX4APxOqMyvKcX53oIC9kAxBsliJkf4,107703
|
|
@@ -159,8 +160,8 @@ followthemoney/types/phone.py,sha256=r8uRqWinS0CYnYBTs405k5gO4jeatUDgjdzzijoMKJE
|
|
|
159
160
|
followthemoney/types/string.py,sha256=fqyTauAm4mNnNaoH-yH087RBbNh-G5ZZUO3awTGQUUg,1230
|
|
160
161
|
followthemoney/types/topic.py,sha256=CS5IoI8gm4MSVxfV6K4mGd20_tT1SaKMkcOt_ObSsAg,3678
|
|
161
162
|
followthemoney/types/url.py,sha256=QFpS_JIV8unFHuh_uGv22SWUUkocBoOpzLsAJWom_gI,1455
|
|
162
|
-
followthemoney-4.0.
|
|
163
|
-
followthemoney-4.0.
|
|
164
|
-
followthemoney-4.0.
|
|
165
|
-
followthemoney-4.0.
|
|
166
|
-
followthemoney-4.0.
|
|
163
|
+
followthemoney-4.0.2.dist-info/METADATA,sha256=kP9fR9P4N2Tp9BT7vrczM-lBNUS04rFO6Xg-Wt5Wg7c,6791
|
|
164
|
+
followthemoney-4.0.2.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
165
|
+
followthemoney-4.0.2.dist-info/entry_points.txt,sha256=caoFTlf213jhg5sz3TNSofutjUTzaKtWATuSIdd9Cps,653
|
|
166
|
+
followthemoney-4.0.2.dist-info/licenses/LICENSE,sha256=H6_EVXisnJC0-18CjXIaqrBSFq_VH3OnS7u3dccOv6g,1148
|
|
167
|
+
followthemoney-4.0.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|