rtbnext 1.0.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.
- rtbnext/__init__.py +26 -0
- rtbnext/_version.py +7 -0
- rtbnext/core/cache.py +102 -0
- rtbnext/core/http_client.py +153 -0
- rtbnext/core/loader.py +173 -0
- rtbnext/core/parser.py +84 -0
- rtbnext/core/rate_limiter.py +86 -0
- rtbnext/defaults.py +26 -0
- rtbnext/endpoint/base.py +80 -0
- rtbnext/endpoint/filter.py +112 -0
- rtbnext/endpoint/list.py +85 -0
- rtbnext/endpoint/mover.py +33 -0
- rtbnext/endpoint/profile.py +158 -0
- rtbnext/endpoint/stats.py +156 -0
- rtbnext/endpoint/system.py +25 -0
- rtbnext/py.typed +0 -0
- rtbnext/resource/base.py +206 -0
- rtbnext/resource/collectable.py +201 -0
- rtbnext/resource/collection.py +313 -0
- rtbnext/resource/dateable.py +59 -0
- rtbnext/resource/indexable.py +91 -0
- rtbnext/resource/series.py +266 -0
- rtbnext/rtbnext.py +107 -0
- rtbnext/schema/assets.py +101 -0
- rtbnext/schema/filter.py +35 -0
- rtbnext/schema/generic.py +105 -0
- rtbnext/schema/list.py +64 -0
- rtbnext/schema/mover.py +40 -0
- rtbnext/schema/profile.py +137 -0
- rtbnext/schema/stats.py +133 -0
- rtbnext/schema/system.py +15 -0
- rtbnext/utils.py +36 -0
- rtbnext-1.0.0.dist-info/METADATA +161 -0
- rtbnext-1.0.0.dist-info/RECORD +36 -0
- rtbnext-1.0.0.dist-info/WHEEL +4 -0
- rtbnext-1.0.0.dist-info/licenses/LICENSE +21 -0
rtbnext/endpoint/base.py
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Base Endpoint
|
|
3
|
+
|
|
4
|
+
Implements the base endpoint class.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING, cast
|
|
10
|
+
|
|
11
|
+
from rtbnext.core.loader import ResourceStateLoader
|
|
12
|
+
from rtbnext.core.parser import Parser, ParserFn, ParserMode, parser
|
|
13
|
+
from rtbnext.resource.base import Resource, ResourcePool
|
|
14
|
+
from rtbnext.resource.collectable import CollectableResource, EntityFn, FindFn, SearchFn
|
|
15
|
+
from rtbnext.resource.dateable import DateableResource, DateFn
|
|
16
|
+
from rtbnext.resource.indexable import IndexableResource, IndexFn, KeysFn
|
|
17
|
+
from rtbnext.resource.series import PointFn, TimeSeriesResource, TimeSeriesRow
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from rtbnext.rtbnext import Endpoints
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class EndpointBase:
|
|
24
|
+
"""
|
|
25
|
+
Base class for all API endpoints.
|
|
26
|
+
|
|
27
|
+
Provides shared resource factory helpers for basic, collectable,
|
|
28
|
+
indexable, time series, and dateable resources.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
loader: ResourceStateLoader,
|
|
34
|
+
pool: ResourcePool,
|
|
35
|
+
endpoints: Endpoints
|
|
36
|
+
) -> None:
|
|
37
|
+
self._loader, self._pool, self._endpoints = loader, pool, endpoints
|
|
38
|
+
|
|
39
|
+
def _resource( self, path: str, mode: ParserMode = "json" ) -> Resource:
|
|
40
|
+
"""Returns a basic resource."""
|
|
41
|
+
|
|
42
|
+
return self._pool.get( path, lambda: Resource( path, self._loader, parser( mode ) ) )
|
|
43
|
+
|
|
44
|
+
def _collectable(
|
|
45
|
+
self, path: str, *,
|
|
46
|
+
entity: EntityFn,
|
|
47
|
+
find: FindFn | None = None,
|
|
48
|
+
search: SearchFn | None = None
|
|
49
|
+
) -> CollectableResource:
|
|
50
|
+
"""Returns a collectable resource."""
|
|
51
|
+
|
|
52
|
+
return self._pool.get( path, lambda: CollectableResource(
|
|
53
|
+
path, self._loader, Parser.json, entity= entity, find= find, search= search
|
|
54
|
+
) )
|
|
55
|
+
|
|
56
|
+
def _dateable( self, path: str, *, date: DateFn ) -> DateableResource:
|
|
57
|
+
"""Returns a dateable resource."""
|
|
58
|
+
|
|
59
|
+
return self._pool.get( path, lambda: DateableResource(
|
|
60
|
+
path, self._loader, Parser.json, date= date
|
|
61
|
+
) )
|
|
62
|
+
|
|
63
|
+
def _indexable(
|
|
64
|
+
self, path: str, *,
|
|
65
|
+
index: IndexFn,
|
|
66
|
+
keys: KeysFn | None = None
|
|
67
|
+
) -> IndexableResource:
|
|
68
|
+
"""Returns a indexable resource."""
|
|
69
|
+
|
|
70
|
+
return self._pool.get( path, lambda: IndexableResource(
|
|
71
|
+
path, self._loader, Parser.json, index= index, keys= keys
|
|
72
|
+
) )
|
|
73
|
+
|
|
74
|
+
def _series( self, path: str, *, point: PointFn ) -> TimeSeriesResource:
|
|
75
|
+
"""Returns a time series resource."""
|
|
76
|
+
|
|
77
|
+
return self._pool.get( path, lambda: TimeSeriesResource(
|
|
78
|
+
path, self._loader, cast( ParserFn[ list[ TimeSeriesRow ] ], Parser.csv ),
|
|
79
|
+
point= point
|
|
80
|
+
) )
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Filter Endpoint
|
|
3
|
+
|
|
4
|
+
Provides access to filter collections for special categories,
|
|
5
|
+
demographics, and indices.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import TypedDict
|
|
11
|
+
|
|
12
|
+
from rtbnext.endpoint.base import EndpointBase
|
|
13
|
+
from rtbnext.endpoint.profile import ProfileEntity
|
|
14
|
+
from rtbnext.resource.collectable import CollectableResource
|
|
15
|
+
from rtbnext.resource.indexable import IndexableResource
|
|
16
|
+
from rtbnext.schema.filter import Filter, FilterIndex, FilterItem
|
|
17
|
+
from rtbnext.schema.generic import AgeGroup, Gender, Industry, MaritalStatus
|
|
18
|
+
from rtbnext.utils import sanitize
|
|
19
|
+
|
|
20
|
+
type FilterCollection = CollectableResource[ Filter, FilterItem, ProfileEntity[ FilterItem ] ]
|
|
21
|
+
|
|
22
|
+
FilterIndexTree = TypedDict( "FilterIndexTree", {
|
|
23
|
+
"industry": dict[ Industry, FilterCollection ],
|
|
24
|
+
"citizenship": dict[ str, FilterCollection ],
|
|
25
|
+
"country": dict[ str, FilterCollection ],
|
|
26
|
+
"state": dict[ str, FilterCollection ],
|
|
27
|
+
"gender": dict[ Gender, FilterCollection ],
|
|
28
|
+
"age": dict[ AgeGroup, FilterCollection ],
|
|
29
|
+
"maritalStatus": dict[ MaritalStatus, FilterCollection ],
|
|
30
|
+
"special": dict[ str, FilterCollection ]
|
|
31
|
+
} )
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class FilterEndpoint( EndpointBase ):
|
|
35
|
+
"""
|
|
36
|
+
Endpoint implementation for filter resources.
|
|
37
|
+
|
|
38
|
+
Provides access to filter collections for special categories,
|
|
39
|
+
demographics, and indices.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def _filter( self, path: str ) -> FilterCollection:
|
|
43
|
+
"""Creates a filter collection using the profile endpoint's collection builder."""
|
|
44
|
+
|
|
45
|
+
return self._endpoints.profile.collect( path )
|
|
46
|
+
|
|
47
|
+
@property
|
|
48
|
+
def deceased( self ) -> FilterCollection:
|
|
49
|
+
"""Returns the filter collection for deceased profiles."""
|
|
50
|
+
|
|
51
|
+
return self._filter( "v2/filter/special/deceased.json" )
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def drop_off( self ) -> FilterCollection:
|
|
55
|
+
"""Returns the filter collection for dropped off profiles."""
|
|
56
|
+
|
|
57
|
+
return self._filter( "v2/filter/special/dropOff.json" )
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def family( self ) -> FilterCollection:
|
|
61
|
+
"""Returns the filter collection for family profiles."""
|
|
62
|
+
|
|
63
|
+
return self._filter( "v2/filter/special/family.json" )
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def self_made( self ) -> FilterCollection:
|
|
67
|
+
"""Returns the filter collection for self-made profiles."""
|
|
68
|
+
|
|
69
|
+
return self._filter( "v2/filter/special/selfMade.json" )
|
|
70
|
+
|
|
71
|
+
def industry( self, key: Industry ) -> FilterCollection:
|
|
72
|
+
"""Returns the filter collection by industry."""
|
|
73
|
+
|
|
74
|
+
return self._filter( f"v2/filter/industry/{ sanitize( key ) }.json" )
|
|
75
|
+
|
|
76
|
+
def age( self, key: AgeGroup ) -> FilterCollection:
|
|
77
|
+
"""Returns the filter collection by age group."""
|
|
78
|
+
|
|
79
|
+
return self._filter( f"v2/filter/age/{ key }.json" )
|
|
80
|
+
|
|
81
|
+
def gender( self, key: Gender ) -> FilterCollection:
|
|
82
|
+
"""Returns the filter collection by gender."""
|
|
83
|
+
|
|
84
|
+
return self._filter( f"v2/filter/gender/{ sanitize( key ) }.json" )
|
|
85
|
+
|
|
86
|
+
def marital_status( self, key: MaritalStatus ) -> FilterCollection:
|
|
87
|
+
"""Returns the filter collection by marital status."""
|
|
88
|
+
|
|
89
|
+
return self._filter( f"v2/filter/maritalStatus/{ sanitize( key ) }.json" )
|
|
90
|
+
|
|
91
|
+
def citizenship( self, key: str ) -> FilterCollection:
|
|
92
|
+
"""Returns the filter collection by citizenship, indexed by ISO code."""
|
|
93
|
+
|
|
94
|
+
return self._filter( f"v2/filter/citizenship/{ key.upper() }.json" )
|
|
95
|
+
|
|
96
|
+
def country( self, key: str ) -> FilterCollection:
|
|
97
|
+
"""Returns the filter collection by country, indexed by ISO code."""
|
|
98
|
+
|
|
99
|
+
return self._filter( f"v2/filter/country/{ key.upper() }.json" )
|
|
100
|
+
|
|
101
|
+
def state( self, key: str ) -> FilterCollection:
|
|
102
|
+
"""Returns the filter collection by U.S. state, indexed by USPS code."""
|
|
103
|
+
|
|
104
|
+
return self._filter( f"v2/filter/state/{ key.upper() }.json" )
|
|
105
|
+
|
|
106
|
+
@property
|
|
107
|
+
def index( self ) -> IndexableResource[ FilterIndex, FilterIndexTree ]:
|
|
108
|
+
"""Provides the root filter index resource."""
|
|
109
|
+
|
|
110
|
+
return self._indexable( "v2/filter/index.json",
|
|
111
|
+
index= lambda path: self._filter( f"v2/filter/{ "/".join( path ) }.json" )
|
|
112
|
+
)
|
rtbnext/endpoint/list.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""
|
|
2
|
+
List Endpoint
|
|
3
|
+
|
|
4
|
+
Provides access to item snapshots, date-indexed list resources,
|
|
5
|
+
and the index listing all available lists.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from functools import cached_property
|
|
11
|
+
|
|
12
|
+
from rtbnext.endpoint.base import EndpointBase
|
|
13
|
+
from rtbnext.endpoint.profile import ProfileEntity
|
|
14
|
+
from rtbnext.resource.collectable import CollectableResource
|
|
15
|
+
from rtbnext.resource.dateable import DateableResource
|
|
16
|
+
from rtbnext.schema.generic import DateIndex
|
|
17
|
+
from rtbnext.schema.list import List, ListIndex, ListIndexItem, ListItem
|
|
18
|
+
from rtbnext.utils import sanitize, ymd
|
|
19
|
+
|
|
20
|
+
type ListSnapshot = CollectableResource[ List, ListItem, ProfileEntity[ ListItem ] ]
|
|
21
|
+
type ListDateIndex = DateableResource[ DateIndex, ListSnapshot ]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ListEntity:
|
|
25
|
+
"""Lazy entity for list index items."""
|
|
26
|
+
|
|
27
|
+
def __init__( self, endpoint: ListEndpoint, item: ListIndexItem ) -> None:
|
|
28
|
+
self._endpoint, self._item = endpoint, item
|
|
29
|
+
|
|
30
|
+
def __getitem__( self, name: str ) -> object:
|
|
31
|
+
"""Forwards unknown properties to the underlying list item."""
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
return self._item[ name ]
|
|
35
|
+
except KeyError:
|
|
36
|
+
raise AttributeError( name ) from None
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def raw( self ) -> ListIndexItem:
|
|
40
|
+
"""Returns the raw list item passed to list entity."""
|
|
41
|
+
|
|
42
|
+
return self._item
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def uri( self ) -> str:
|
|
46
|
+
"""Returns the list URI."""
|
|
47
|
+
|
|
48
|
+
return self._item[ "uri" ]
|
|
49
|
+
|
|
50
|
+
@cached_property
|
|
51
|
+
def dates( self ) -> ListDateIndex:
|
|
52
|
+
"""Returns the lazily loaded list date index resource."""
|
|
53
|
+
|
|
54
|
+
return self._endpoint.get( self.uri )
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ListEndpoint( EndpointBase ):
|
|
58
|
+
"""
|
|
59
|
+
Endpoint implementation for list resources.
|
|
60
|
+
|
|
61
|
+
Provides access to item snapshots, date-indexed list resources,
|
|
62
|
+
and the index listing all available lists.
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def snapshot( self, uri: str, date: str ) -> ListSnapshot:
|
|
66
|
+
"""Returns a snapshot collection for a list URI at a specific date."""
|
|
67
|
+
|
|
68
|
+
return self._endpoints.profile.collect(
|
|
69
|
+
f"v2/list/{ sanitize( uri ) }/{ ymd( date ) }.json"
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def get( self, uri: str ) -> ListDateIndex:
|
|
73
|
+
"""Returns a date-indexed list resource for a list URI."""
|
|
74
|
+
|
|
75
|
+
return self._dateable( f"v2/list/{ sanitize( uri ) }/index.json",
|
|
76
|
+
date= lambda date: self.snapshot( uri, date )
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def index( self ) -> CollectableResource[ ListIndex, ListIndexItem, ListEntity ]:
|
|
81
|
+
"""Returns the root list index resource."""
|
|
82
|
+
|
|
83
|
+
return self._collectable( "v2/list/index.json",
|
|
84
|
+
entity= lambda item: ListEntity( self, item )
|
|
85
|
+
)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mover Endpoint
|
|
3
|
+
|
|
4
|
+
Provides access to mover snapshots and mover index resources.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from rtbnext.endpoint.base import EndpointBase
|
|
10
|
+
from rtbnext.resource.base import Resource
|
|
11
|
+
from rtbnext.resource.dateable import DateableResource
|
|
12
|
+
from rtbnext.schema.generic import DateIndex
|
|
13
|
+
from rtbnext.schema.mover import Mover
|
|
14
|
+
from rtbnext.utils import ymd
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class MoverEndpoint( EndpointBase ):
|
|
18
|
+
"""
|
|
19
|
+
Endpoint implementation for mover resources.
|
|
20
|
+
|
|
21
|
+
Provides access to mover snapshots and mover index resources.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
def snapshot( self, date: object ) -> Resource[ Mover ]:
|
|
25
|
+
"""Returns a mover snapshot for a given date."""
|
|
26
|
+
|
|
27
|
+
return self._resource( f"v2/mover/{ ymd( date ) }.json" )
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def index( self ) -> DateableResource[ DateIndex, Resource[ Mover ] ]:
|
|
31
|
+
"""Returns the root mover index resource."""
|
|
32
|
+
|
|
33
|
+
return self._dateable( "v2/mover/index.json", date= self.snapshot )
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Profile Endpoint
|
|
3
|
+
|
|
4
|
+
Provides access to profile metadata, details, history, index,
|
|
5
|
+
and search index.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from functools import cached_property
|
|
11
|
+
from typing import Generic, TypedDict
|
|
12
|
+
|
|
13
|
+
from rtbnext.endpoint.base import EndpointBase
|
|
14
|
+
from rtbnext.resource.base import Resource
|
|
15
|
+
from rtbnext.resource.collectable import CollectableResource, CollectItem, FindFn, I, SearchFn
|
|
16
|
+
from rtbnext.resource.series import AggregateRange, AggregateValue, TimeSeriesResource
|
|
17
|
+
from rtbnext.schema.profile import (
|
|
18
|
+
ProfileData, ProfileHistory, ProfileHistoryItem, ProfileIndex, ProfileIndexItem, ProfileMeta,
|
|
19
|
+
SearchIndex, SearchIndexItem
|
|
20
|
+
)
|
|
21
|
+
from rtbnext.utils import sanitize
|
|
22
|
+
|
|
23
|
+
ProfileHistoryPoint = TypedDict( "ProfileHistoryPoint", {
|
|
24
|
+
"date": str,
|
|
25
|
+
"rank": int,
|
|
26
|
+
"networth": float,
|
|
27
|
+
"change": float,
|
|
28
|
+
"percent": float
|
|
29
|
+
} )
|
|
30
|
+
|
|
31
|
+
ProfileHistoryAggregatePoint = TypedDict( "ProfileHistoryAggregatePoint", {
|
|
32
|
+
"date": str,
|
|
33
|
+
"label": str,
|
|
34
|
+
"range": AggregateRange,
|
|
35
|
+
"rank": AggregateValue,
|
|
36
|
+
"networth": AggregateValue,
|
|
37
|
+
"change": AggregateValue,
|
|
38
|
+
"percent": AggregateValue
|
|
39
|
+
} )
|
|
40
|
+
|
|
41
|
+
type ProfileHistorySeries = TimeSeriesResource[
|
|
42
|
+
ProfileHistory, ProfileHistoryPoint, ProfileHistoryAggregatePoint
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class ProfileEntity( Generic[ I ] ):
|
|
47
|
+
"""
|
|
48
|
+
Lazy profile entity exposing related profile resources.
|
|
49
|
+
|
|
50
|
+
Wraps a raw profile item and provides lazy access to its
|
|
51
|
+
associated metadata, profile data, and history resources.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
def __init__( self, endpoint: ProfileEndpoint, item: I ) -> None:
|
|
55
|
+
self._endpoint, self._item = endpoint, item
|
|
56
|
+
|
|
57
|
+
def __getitem__( self, name: str ) -> object:
|
|
58
|
+
"""Forwards unknown properties to the underlying profile item."""
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
return self._item[ name ]
|
|
62
|
+
except KeyError:
|
|
63
|
+
raise AttributeError( name ) from None
|
|
64
|
+
|
|
65
|
+
@property
|
|
66
|
+
def raw( self ) -> I:
|
|
67
|
+
"""Returns the raw item passed to profile entity."""
|
|
68
|
+
|
|
69
|
+
return self._item
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def uri( self ) -> str:
|
|
73
|
+
"""Returns the profile URI."""
|
|
74
|
+
|
|
75
|
+
return self._item[ "uri" ]
|
|
76
|
+
|
|
77
|
+
@cached_property
|
|
78
|
+
def meta( self ) -> Resource[ ProfileMeta ]:
|
|
79
|
+
"""Returns the lazily loaded profile metadata resource."""
|
|
80
|
+
|
|
81
|
+
return self._endpoint.meta( self.uri )
|
|
82
|
+
|
|
83
|
+
@cached_property
|
|
84
|
+
def data( self ) -> Resource[ ProfileData ]:
|
|
85
|
+
"""Returns the lazily loaded profile data resource."""
|
|
86
|
+
|
|
87
|
+
return self._endpoint.data( self.uri )
|
|
88
|
+
|
|
89
|
+
@cached_property
|
|
90
|
+
def history( self ) -> ProfileHistorySeries:
|
|
91
|
+
"""Returns the lazily loaded profile history resource."""
|
|
92
|
+
|
|
93
|
+
return self._endpoint.history( self.uri )
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class ProfileEndpoint( EndpointBase ):
|
|
97
|
+
"""
|
|
98
|
+
Endpoint implementation for profile resources.
|
|
99
|
+
|
|
100
|
+
Provides access to profile metadata, details, history,
|
|
101
|
+
index, and search index.
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def _point( self, point: ProfileHistoryItem ) -> ProfileHistoryPoint:
|
|
105
|
+
"""Converts a raw profile history row into a typed history point."""
|
|
106
|
+
|
|
107
|
+
date, rank, ntw, change, pct = point
|
|
108
|
+
return { "date": date, "rank": rank, "networth": ntw, "change": change, "percent": pct }
|
|
109
|
+
|
|
110
|
+
def _entity( self, item: I ) -> ProfileEntity[ I ]:
|
|
111
|
+
"""Creates a profile entity with lazy-loaded related resources."""
|
|
112
|
+
|
|
113
|
+
return ProfileEntity( self, item )
|
|
114
|
+
|
|
115
|
+
def collect(
|
|
116
|
+
self, path: str, *,
|
|
117
|
+
find: FindFn[ I ] | None = None,
|
|
118
|
+
search: SearchFn[ I ] | None = None
|
|
119
|
+
) -> CollectableResource:
|
|
120
|
+
"""Returns a profile collection resource from a JSON endpoint."""
|
|
121
|
+
|
|
122
|
+
return self._collectable( path, entity= self._entity, find= find, search= search )
|
|
123
|
+
|
|
124
|
+
def meta( self, uri: str ) -> Resource[ ProfileMeta ]:
|
|
125
|
+
"""Returns profile metadata for the given URI."""
|
|
126
|
+
|
|
127
|
+
return self._resource( f"v2/profile/{ sanitize( uri ) }/meta.json" )
|
|
128
|
+
|
|
129
|
+
def data( self, uri: str ) -> Resource[ ProfileData ]:
|
|
130
|
+
"""Returns profile data for the given URI."""
|
|
131
|
+
|
|
132
|
+
return self._resource( f"v2/profile/{ sanitize( uri ) }/profile.json" )
|
|
133
|
+
|
|
134
|
+
def history( self, uri: str ) -> ProfileHistorySeries:
|
|
135
|
+
"""Returns profile history time-series data for the given URI."""
|
|
136
|
+
|
|
137
|
+
return self._series( f"v2/profile/{ sanitize( uri ) }/history.csv", point= self._point )
|
|
138
|
+
|
|
139
|
+
def get( self, uri: str ) -> ProfileEntity[ CollectItem ]:
|
|
140
|
+
"""Returns the profile entity for a URI."""
|
|
141
|
+
|
|
142
|
+
return self._entity( CollectItem( uri= sanitize( uri ) ) )
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def index( self ) -> CollectableResource[
|
|
146
|
+
ProfileIndex, ProfileIndexItem, ProfileEntity[ ProfileIndexItem ]
|
|
147
|
+
]:
|
|
148
|
+
"""Returns the profile index collection."""
|
|
149
|
+
|
|
150
|
+
return self.collect( "v2/profile/index.json" )
|
|
151
|
+
|
|
152
|
+
@property
|
|
153
|
+
def search_index( self ) -> CollectableResource[
|
|
154
|
+
SearchIndex, SearchIndexItem, ProfileEntity[ SearchIndexItem ]
|
|
155
|
+
]:
|
|
156
|
+
"""Returns the profile search index collection."""
|
|
157
|
+
|
|
158
|
+
return self.collect( "v2/profile/search.json" )
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Stats Endpoint
|
|
3
|
+
|
|
4
|
+
Provides access to stats resources, scatter collections, historic
|
|
5
|
+
data, and grouped indices.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Literal, TypedDict
|
|
11
|
+
|
|
12
|
+
from rtbnext.endpoint.base import EndpointBase
|
|
13
|
+
from rtbnext.endpoint.profile import ProfileEntity
|
|
14
|
+
from rtbnext.resource.base import Resource
|
|
15
|
+
from rtbnext.resource.collectable import CollectableResource
|
|
16
|
+
from rtbnext.resource.indexable import IndexableResource
|
|
17
|
+
from rtbnext.resource.series import AggregateRange, AggregateValue, TimeSeriesResource
|
|
18
|
+
from rtbnext.schema.generic import Industry
|
|
19
|
+
from rtbnext.schema.stats import (
|
|
20
|
+
CitizenshipStatsIndex, DBStats, GlobalStats, History, HistoryItem, IndustryStatsIndex,
|
|
21
|
+
ProfileStats, Scatter, ScatterItem, Top10, WealthStats
|
|
22
|
+
)
|
|
23
|
+
from rtbnext.utils import sanitize
|
|
24
|
+
|
|
25
|
+
HistoryPoint = TypedDict( "HistoryPoint", {
|
|
26
|
+
"date": str,
|
|
27
|
+
"count": int,
|
|
28
|
+
"total": float,
|
|
29
|
+
"woman": int,
|
|
30
|
+
"quota": float,
|
|
31
|
+
"change": float,
|
|
32
|
+
"percent": float
|
|
33
|
+
} )
|
|
34
|
+
|
|
35
|
+
HistoryAggregatePoint = TypedDict( "HistoryAggregatePoint", {
|
|
36
|
+
"date": str,
|
|
37
|
+
"label": str,
|
|
38
|
+
"range": AggregateRange,
|
|
39
|
+
"count": AggregateValue,
|
|
40
|
+
"total": AggregateValue,
|
|
41
|
+
"woman": AggregateValue,
|
|
42
|
+
"quota": AggregateValue,
|
|
43
|
+
"change": AggregateValue,
|
|
44
|
+
"percent": AggregateValue
|
|
45
|
+
} )
|
|
46
|
+
|
|
47
|
+
type HistorySeries = TimeSeriesResource[ History, HistoryPoint, HistoryAggregatePoint ]
|
|
48
|
+
type CitizenshipIndexTree = dict[ str, HistorySeries ]
|
|
49
|
+
type IndustryIndexTree = dict[ Industry, HistorySeries ]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class StatsEndpoint( EndpointBase ):
|
|
53
|
+
"""
|
|
54
|
+
Endpoint implementation for stats resources.
|
|
55
|
+
|
|
56
|
+
Provides access to stats resources, scatter collections,
|
|
57
|
+
historic data, and grouped indices.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def _point( self, point: HistoryItem ) -> HistoryPoint:
|
|
61
|
+
"""Converts a raw history row into a typed history point."""
|
|
62
|
+
|
|
63
|
+
date, count, total, woman, quota, change, percent = point
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
"date": date, "count": count, "total": total, "woman": woman,
|
|
67
|
+
"quota": quota, "change": change, "percent": percent
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def _keys( self, value: object ) -> list[ str ] | None:
|
|
71
|
+
"""Returns available index keys."""
|
|
72
|
+
|
|
73
|
+
if not isinstance( value, dict ):
|
|
74
|
+
return None
|
|
75
|
+
|
|
76
|
+
if isinstance( items := value.get( "items" ), dict ):
|
|
77
|
+
value.clear()
|
|
78
|
+
value.update( items )
|
|
79
|
+
|
|
80
|
+
return [] if "date" in value else list( value.keys() )
|
|
81
|
+
|
|
82
|
+
def _group( self, group: Literal[ "industry", "citizenship" ] ) -> IndexableResource:
|
|
83
|
+
"""Builds an industry or citizenship index."""
|
|
84
|
+
|
|
85
|
+
return self._indexable( f"v2/stats/{ group }/index.json",
|
|
86
|
+
index= lambda path: getattr( self, group )( path[ -1 ] ),
|
|
87
|
+
keys= self._keys
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def db( self ) -> Resource[ DBStats ]:
|
|
92
|
+
"""Returns the database statistics resource."""
|
|
93
|
+
|
|
94
|
+
return self._resource( "v2/stats/db.json" )
|
|
95
|
+
|
|
96
|
+
@property
|
|
97
|
+
def global_( self ) -> Resource[ GlobalStats ]:
|
|
98
|
+
"""Returns the global statistics resource."""
|
|
99
|
+
|
|
100
|
+
return self._resource( "v2/stats/global.json" )
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def profile( self ) -> Resource[ ProfileStats ]:
|
|
104
|
+
"""Returns the profile statistics resource."""
|
|
105
|
+
|
|
106
|
+
return self._resource( "v2/stats/profile.json" )
|
|
107
|
+
|
|
108
|
+
@property
|
|
109
|
+
def scatter( self ) -> CollectableResource[
|
|
110
|
+
Scatter, ScatterItem, ProfileEntity[ ScatterItem ]
|
|
111
|
+
]:
|
|
112
|
+
"""Returns the profile scatter stats collection resource."""
|
|
113
|
+
|
|
114
|
+
return self._endpoints.profile.collect( "v2/stats/scatter.json" )
|
|
115
|
+
|
|
116
|
+
@property
|
|
117
|
+
def wealth( self ) -> Resource[ WealthStats ]:
|
|
118
|
+
"""Returns the wealth statistics resource."""
|
|
119
|
+
|
|
120
|
+
return self._resource( "v2/stats/wealth.json" )
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def history( self ) -> HistorySeries:
|
|
124
|
+
"""Returns the historical stats time series resource."""
|
|
125
|
+
|
|
126
|
+
return self._series( "v2/stats/history.csv", point= self._point )
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def top10( self ) -> Resource[ Top10 ]:
|
|
130
|
+
"""Returns the top 10 billionaires list resource."""
|
|
131
|
+
|
|
132
|
+
return self._resource( "v2/stats/top10.json" )
|
|
133
|
+
|
|
134
|
+
def industry( self, key: Industry ) -> HistorySeries:
|
|
135
|
+
"""Returns time series for a specific industry."""
|
|
136
|
+
|
|
137
|
+
return self._series( f"v2/stats/industry/{ sanitize( key ) }.csv", point= self._point )
|
|
138
|
+
|
|
139
|
+
def citizenship( self, key: str ) -> HistorySeries:
|
|
140
|
+
"""Returns time series for a specific citizenship, indexed by ISO code."""
|
|
141
|
+
|
|
142
|
+
return self._series( f"v2/stats/citizenship/{ key.upper() }.csv", point= self._point )
|
|
143
|
+
|
|
144
|
+
@property
|
|
145
|
+
def industry_index( self ) -> IndexableResource[ IndustryStatsIndex, IndustryIndexTree ]:
|
|
146
|
+
"""Returns the industry stats index."""
|
|
147
|
+
|
|
148
|
+
return self._group( "industry" )
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def citizenship_index( self ) -> IndexableResource[
|
|
152
|
+
CitizenshipStatsIndex, CitizenshipIndexTree
|
|
153
|
+
]:
|
|
154
|
+
"""Returns the citizenship stats index."""
|
|
155
|
+
|
|
156
|
+
return self._group( "citizenship" )
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""
|
|
2
|
+
System Endpoint
|
|
3
|
+
|
|
4
|
+
Provides access to system status information.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from rtbnext.endpoint.base import EndpointBase
|
|
10
|
+
from rtbnext.resource.base import Resource
|
|
11
|
+
from rtbnext.schema.system import SystemStatus
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class SystemEndpoint( EndpointBase ):
|
|
15
|
+
"""
|
|
16
|
+
Endpoint implementation for system resources.
|
|
17
|
+
|
|
18
|
+
Provides access to system status information.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def status( self ) -> Resource[ SystemStatus ]:
|
|
23
|
+
"""Returns the current system status resource."""
|
|
24
|
+
|
|
25
|
+
return self._resource( "v2/system/status.json" )
|
rtbnext/py.typed
ADDED
|
File without changes
|