Skip to content

Commit 22cbdcd

Browse files
Add support for cross-source links to entities function (#128)
1 parent 98894ff commit 22cbdcd

13 files changed

Lines changed: 238 additions & 102 deletions

File tree

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
from elimity_insights_client.api.entities._entities import entities
2-
from elimity_insights_client.api.entities._entity import Entity, Link
2+
from elimity_insights_client.api.entities._entity import Entity, EntityType, Link
33

4-
__all__ = ["Entity", "Link", "entities"]
4+
__all__ = ["Entity", "EntityType", "Link", "entities"]

src/elimity_insights_client/api/entities/_entities.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33
from elimity_insights_client.api._api import Config
44
from elimity_insights_client.api._api import query as api_query
55
from elimity_insights_client.api._api import sources
6-
from elimity_insights_client.api.entities._entity import Entity
6+
from elimity_insights_client.api.entities._entity import Entity, EntityType
77
from elimity_insights_client.api.entities._parse_query_results_page import (
88
parse_query_results_page,
99
)
1010
from elimity_insights_client.api.entities._query import query
1111

1212

13-
def entities(config: Config, entity_type: str, source_id: int) -> List[Entity]:
13+
def entities(config: Config, entity_type: EntityType) -> List[Entity]:
1414
"""
1515
List all entities of the given entity type from the given source.
1616
@@ -19,10 +19,8 @@ def entities(config: Config, entity_type: str, source_id: int) -> List[Entity]:
1919
"""
2020

2121
sos = sources(config)
22-
schema = next(
23-
source.domain_graph_schema for source in sos if source.id == source_id
24-
)
25-
que = query(entity_type, schema, source_id)
22+
schemas = {source.id: source.domain_graph_schema for source in sos}
23+
que = query(entity_type, schemas)
2624
queries = [que]
2725
(page,) = api_query(config, queries)
28-
return parse_query_results_page(entity_type, page, schema)
26+
return parse_query_results_page(entity_type, page, schemas)

src/elimity_insights_client/api/entities/_entity.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,18 @@ class Entity:
1010

1111
attribute_assignments: Dict[str, Value]
1212
id: str
13-
links: Dict[str, List["Link"]]
13+
links: Dict["EntityType", List["Link"]]
1414
name: str
1515

1616

17+
@dataclass(frozen=True)
18+
class EntityType:
19+
"""Type of entities for a given source, represents a unique key in dictionaries of links."""
20+
21+
id: str
22+
source_id: int
23+
24+
1725
@dataclass
1826
class Link:
1927
"""Linked entity with assignments for all attributes."""

src/elimity_insights_client/api/entities/_parse_query_results_page.py

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from dataclasses import dataclass
2-
from typing import List
2+
from typing import Dict, List
33

44
from more_itertools import chunked
55

@@ -8,7 +8,7 @@
88
DomainGraphSchema,
99
)
1010
from elimity_insights_client._util import map_list
11-
from elimity_insights_client.api.entities._entity import Entity, Link
11+
from elimity_insights_client.api.entities._entity import Entity, EntityType, Link
1212
from elimity_insights_client.api.entities._schema import (
1313
attribute_types as schema_attribute_types,
1414
)
@@ -32,24 +32,26 @@ class _AttributeAssignment:
3232

3333
@dataclass
3434
class _LinkGroup:
35-
entity_type: str
35+
entity_type: EntityType
3636
links: List[Link]
3737

3838

3939
def parse_query_results_page(
40-
entity_type: str, page: QueryResultsPage, schema: DomainGraphSchema
40+
entity_type: EntityType,
41+
page: QueryResultsPage,
42+
schemas: Dict[int, DomainGraphSchema],
4143
) -> List[Entity]:
4244
def make_entity(result: QueryResult) -> Entity:
43-
entity = _link(entity_type, schema, result)
45+
entity = _link(entity_type, result, schemas)
4446

45-
def make_group(entity_type: str, page: QueryResultsPage) -> _LinkGroup:
47+
def make_group(entity_type: EntityType, page: QueryResultsPage) -> _LinkGroup:
4648
def make_link(result: QueryResult) -> Link:
47-
return _link(entity_type, schema, result)
49+
return _link(entity_type, result, schemas)
4850

4951
links = map_list(make_link, page.results)
5052
return _LinkGroup(entity_type, links)
5153

52-
link_entity_types = schema_link_entity_types(entity_type, schema)
54+
link_entity_types = schema_link_entity_types(entity_type, schemas)
5355
group_iter = map(make_group, link_entity_types, result.link_pages)
5456
links = {group.entity_type: group.links for group in group_iter}
5557
return Entity(entity.attribute_assignments, entity.id, links, entity.name)
@@ -65,9 +67,11 @@ def _attribute_assignment(
6567
return _AttributeAssignment(assigned, type.id, value_inclusion)
6668

6769

68-
def _link(entity_type: str, schema: DomainGraphSchema, result: QueryResult) -> Link:
70+
def _link(
71+
entity_type: EntityType, result: QueryResult, schemas: Dict[int, DomainGraphSchema]
72+
) -> Link:
6973
inclusions_iter = chunked(result.inclusions, 2)
70-
attribute_types = schema_attribute_types(entity_type, schema)
74+
attribute_types = schema_attribute_types(entity_type, schemas)
7175
assignment_iter = map(_attribute_assignment, inclusions_iter, attribute_types)
7276
assignments = {
7377
assignment.attribute_type: assignment.value

src/elimity_insights_client/api/entities/_query.py

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import List
1+
from typing import Dict, List
22

33
from more_itertools import interleave
44

@@ -8,6 +8,7 @@
88
Type,
99
)
1010
from elimity_insights_client._util import map_list
11+
from elimity_insights_client.api.entities._entity import EntityType
1112
from elimity_insights_client.api.entities._schema import (
1213
attribute_types as schema_attribute_types,
1314
)
@@ -47,17 +48,16 @@
4748

4849

4950
def query(
50-
entity_type: str,
51-
schema: DomainGraphSchema,
52-
source_id: int,
51+
entity_type: EntityType,
52+
schemas: Dict[int, DomainGraphSchema],
5353
) -> Query:
54-
def make_link_query(entity_type: str) -> Query:
54+
def make_link_query(entity_type: EntityType) -> Query:
5555
link_queries: List[Query] = []
56-
return _query(entity_type, link_queries, schema, source_id)
56+
return _query(entity_type, link_queries, schemas)
5757

58-
link_entity_types = schema_link_entity_types(entity_type, schema)
58+
link_entity_types = schema_link_entity_types(entity_type, schemas)
5959
link_queries = map_list(make_link_query, link_entity_types)
60-
return _query(entity_type, link_queries, schema, source_id)
60+
return _query(entity_type, link_queries, schemas)
6161

6262

6363
def _assigned_inclusion(type: AttributeType) -> AnyExpression:
@@ -87,37 +87,38 @@ def _attribute_inclusion(type: AttributeType) -> AnyExpression:
8787
return TimeAnyExpression(time_expr)
8888

8989

90-
def _inclusions(entity_type: str, schema: DomainGraphSchema) -> List[AnyExpression]:
91-
attribute_types = schema_attribute_types(entity_type, schema)
90+
def _inclusions(
91+
entity_type: EntityType, schemas: Dict[int, DomainGraphSchema]
92+
) -> List[AnyExpression]:
93+
attribute_types = schema_attribute_types(entity_type, schemas)
9294
assigned_iter = map(_assigned_inclusion, attribute_types)
9395
attribute_iter = map(_attribute_inclusion, attribute_types)
9496
inclusion_iter = interleave(assigned_iter, attribute_iter)
9597
return list(inclusion_iter)
9698

9799

98100
def _query(
99-
entity_type: str,
101+
entity_type: EntityType,
100102
link_queries: List[Query],
101-
schema: DomainGraphSchema,
102-
source_id: int,
103+
schemas: Dict[int, DomainGraphSchema],
103104
) -> Query:
104105
condition = LiteralBooleanExpression(True)
105106
direct_link_group_by_queries: List[DirectLinkGroupByQuery] = []
106107
direct_link_queries: List[DirectLinkQuery] = []
107-
inclusions = _inclusions(entity_type, schema)
108+
inclusions = _inclusions(entity_type, schemas)
108109
link_group_by_queries: List[LinkGroupByQuery] = []
109110
orderings: List[Ordering] = []
110111
return Query(
111112
"",
112113
condition,
113114
direct_link_group_by_queries,
114115
direct_link_queries,
115-
entity_type,
116+
entity_type.id,
116117
inclusions,
117118
999999,
118119
link_group_by_queries,
119120
link_queries,
120121
0,
121122
orderings,
122-
source_id,
123+
entity_type.source_id,
123124
)
Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,32 @@
1-
from typing import List
1+
from itertools import starmap
2+
from typing import Dict, Iterable, List
23

34
from elimity_insights_client._domain_graph_schema import (
45
AttributeType,
56
DomainGraphSchema,
67
)
8+
from elimity_insights_client.api.entities._entity import EntityType
79

810

9-
def attribute_types(entity_type: str, schema: DomainGraphSchema) -> List[AttributeType]:
11+
def attribute_types(
12+
entity_type: EntityType, schemas: Dict[int, DomainGraphSchema]
13+
) -> List[AttributeType]:
14+
schema = schemas[entity_type.source_id]
1015
return [
1116
type
1217
for type in schema.attribute_types
13-
if not type.archived and type.entity_type == entity_type
18+
if not type.archived and type.entity_type == entity_type.id
1419
]
1520

1621

17-
def link_entity_types(entity_type: str, schema: DomainGraphSchema) -> List[str]:
18-
return [type.id for type in schema.entity_types if type.id != entity_type]
22+
def link_entity_types(
23+
entity_type: EntityType, schemas: Dict[int, DomainGraphSchema]
24+
) -> List[EntityType]:
25+
items = schemas.items()
26+
iters = starmap(_entity_types, items)
27+
return [typ for iter in iters for typ in iter if typ != entity_type]
28+
29+
30+
def _entity_types(source_id: int, schema: DomainGraphSchema) -> Iterable[EntityType]:
31+
for type in schema.entity_types:
32+
yield EntityType(type.id, source_id)
Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,35 @@
1-
from typing import Type, TypeVar
1+
from typing import List, Tuple, Type, TypedDict, TypeVar
22

33
from elimity_insights_client._decode_domain_graph_schema import (
44
DomainGraphSchemaDict,
55
decode_domain_graph_schema,
66
)
7+
from elimity_insights_client._domain_graph_schema import DomainGraphSchema
8+
from elimity_insights_client.api.entities._entity import EntityType
79
from tests.elimity_insights_client.api._json import decode_file as json_decode_file
810

911
_T = TypeVar("_T")
1012

13+
_SourceDict = TypedDict(
14+
"_SourceDict",
15+
{
16+
"schema": DomainGraphSchemaDict,
17+
"sourceId": int,
18+
},
19+
)
20+
1121

1222
def json_decode_file_local(filename: str, type: Type[_T]) -> _T:
1323
return json_decode_file(filename, __package__, type)
1424

1525

16-
_json = json_decode_file_local("schema.json", DomainGraphSchemaDict)
17-
schema = decode_domain_graph_schema(_json)
26+
def _item(dict: _SourceDict) -> Tuple[int, DomainGraphSchema]:
27+
schema = dict["schema"]
28+
return dict["sourceId"], decode_domain_graph_schema(schema)
29+
30+
31+
_json = json_decode_file_local("sources.json", List[_SourceDict])
32+
_items = map(_item, _json)
33+
schemas = dict(_items)
34+
35+
type = EntityType("foo", 42)

tests/elimity_insights_client/api/entities/query-results-page.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,30 @@
4545
{
4646
"count": 0,
4747
"results": []
48+
},
49+
{
50+
"count": 1,
51+
"results": [
52+
{
53+
"entity": {
54+
"active": false,
55+
"id": "baz",
56+
"name": "lorem"
57+
},
58+
"inclusions": [
59+
{
60+
"type": "boolean",
61+
"value": "true"
62+
},
63+
{
64+
"type": "number",
65+
"value": "24"
66+
}
67+
],
68+
"linkGroupByPages": [],
69+
"linkPages": []
70+
}
71+
]
4872
}
4973
]
5074
}

tests/elimity_insights_client/api/entities/query.json

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,40 @@
7777
"offset": 0,
7878
"orderBy": [],
7979
"sourceId": 42
80+
},
81+
{
82+
"alias": "",
83+
"condition": {
84+
"boolean": true,
85+
"type": "literal"
86+
},
87+
"directLinkGroupByQueries": [],
88+
"directLinkQueries": [],
89+
"entityType": "bar",
90+
"include": [
91+
{
92+
"expr": {
93+
"attributeType": "ipsum",
94+
"reference": "",
95+
"type": "assigned"
96+
},
97+
"type": "boolean"
98+
},
99+
{
100+
"expr": {
101+
"attributeType": "ipsum",
102+
"reference": "",
103+
"type": "attribute"
104+
},
105+
"type": "number"
106+
}
107+
],
108+
"limit": 999999,
109+
"linkGroupByQueries": [],
110+
"linkQueries": [],
111+
"offset": 0,
112+
"orderBy": [],
113+
"sourceId": 24
80114
}
81115
],
82116
"offset": 0,

0 commit comments

Comments
 (0)