Skip to content

Commit dfa546a

Browse files
authored
Feature/visualizations (#164)
This updates the jupyter server built via the CI. Other than that, it includes some helper functions for videos.
1 parent 1c35a2d commit dfa546a

File tree

16 files changed

+787
-1004
lines changed

16 files changed

+787
-1004
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,5 @@ kaggleds/
144144
examples/*/kaggleds/
145145
docs/*/*.svg
146146
test/aperturedb/logs
147+
adb-python/*
148+
docker/notebook/aperturedata/*

aperturedb/Blobs.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from __future__ import annotations
2+
from typing import Dict
23

34
from aperturedb.Connector import Connector
45
from aperturedb.Entities import Entities
@@ -9,5 +10,14 @@ class Blobs(Entities):
910
db_object = "_Blob"
1011

1112
@classmethod
12-
def retrieve(cls, db: Connector, spec: Query) -> Blobs:
13-
return super().retrieve(db, spec)[-1]
13+
def retrieve(cls,
14+
db: Connector,
15+
spec: Query,
16+
with_adjacent: Dict[str, Query] = None) -> Blobs:
17+
spec.with_class = cls.db_object
18+
19+
results = Entities.retrieve(
20+
db=db, spec=spec, with_adjacent=with_adjacent)
21+
22+
blobs = results[-1]
23+
return blobs

aperturedb/BoundingBoxes.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from __future__ import annotations
2+
from typing import Dict
23

34
from aperturedb.Connector import Connector
45
from aperturedb.Entities import Entities
@@ -9,5 +10,14 @@ class BoundingBoxes(Entities):
910
db_object = "_BoundingBox"
1011

1112
@classmethod
12-
def retrieve(cls, db: Connector, spec: Query) -> BoundingBoxes:
13-
return super().retrieve(db, spec)[-1]
13+
def retrieve(cls,
14+
db: Connector,
15+
spec: Query,
16+
with_adjacent: Dict[str, Query] = None) -> BoundingBoxes:
17+
spec.with_class = cls.db_object
18+
19+
results = Entities.retrieve(
20+
db=db, spec=spec, with_adjacent=with_adjacent)
21+
22+
bboxes = results[-1]
23+
return bboxes

aperturedb/Constraints.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ def equal(self, key, value) -> Constraints:
2020
self.constraints[self._conjunction][key] = ["==", value]
2121
return self
2222

23+
def notequal(self, key, value) -> Constraints:
24+
self.constraints[self._conjunction][key] = ["!=", value]
25+
return self
26+
2327
def greaterequal(self, key, value) -> Constraints:
2428
self.constraints[self._conjunction][key] = [">=", value]
2529
return self

aperturedb/Entities.py

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ class Entities(Subscriptable):
2323
@classmethod
2424
def retrieve(cls,
2525
db: Connector,
26-
spec: Query
26+
spec: Query,
27+
with_adjacent: Dict[str, Query] = None
2728
) -> List[Entities]:
2829
"""
2930
Using the Entities.retrieve method, is a simpple layer, with typical native queries converted
@@ -39,19 +40,41 @@ def retrieve(cls,
3940
Returns:
4041
List[Entities]: _description_
4142
"""
43+
44+
# Since adjacent items are usually a way to filter the results,
45+
# the native query is constructed in the reverse order, with
46+
# first filtering out the relevant itmes based on adjacent items.
47+
fs = None
48+
count = 0
49+
if with_adjacent:
50+
for k, v in with_adjacent.items():
51+
if fs is None:
52+
fs = v
53+
else:
54+
fs = fs.connected_to(v)
55+
count += 1
56+
# Eventually, connect the specification of Images to the specification of the adjacent items.
57+
fs = fs.connected_to(spec)
58+
else:
59+
fs = spec
60+
61+
spec = fs
62+
4263
cls.known_entities = load_entities_registry(
4364
custom_entities=spec.command_properties(prop="with_class"))
4465
cls.db = db
45-
if cls.db_object != "Entity":
46-
spec.with_class = cls.db_object
4766

4867
query = spec.query()
4968
print(f"query={query}")
5069
res, r, b = execute_batch(query, [], db)
5170
if res > 0:
5271
print(f"resp={r}")
5372
results = []
54-
for wc, req, resp in zip(spec.command_properties(prop="with_class"), spec.command_properties(prop="find_command"), r):
73+
for wc, req, blobs, resp in zip(
74+
spec.command_properties(prop="with_class"),
75+
spec.command_properties(prop="find_command"),
76+
spec.command_properties(prop="blobs"),
77+
r):
5578
subresponse = resp[req]['entities']
5679
if not isinstance(subresponse, list):
5780
flattened = []
@@ -61,13 +84,39 @@ def retrieve(cls,
6184
try:
6285
entities = cls.known_entities[wc](
6386
db=db, response=subresponse, type=wc)
87+
entities.blobs = blobs
88+
if wc[0] == "_":
89+
entities.find_command = f"Find{wc[1:]}"
6490
results.append(entities)
6591
except Exception as e:
6692
print(e)
6793
print(cls.known_entities)
6894
raise e
95+
96+
cls.__postprocess__(entities=results[-1], with_adjacent=with_adjacent)
6997
return results
7098

99+
# This needs to be defined so that the application can access the adjacent items,
100+
# with every item of this iterable.
101+
@classmethod
102+
def __decorator(cls, index, adjacent):
103+
item = {}
104+
for k, v in adjacent.items():
105+
item[k] = v[index]
106+
return item
107+
108+
@classmethod
109+
def __postprocess__(cls, entities: Entities, with_adjacent: object = None):
110+
adjacent = {}
111+
if with_adjacent:
112+
for k, v in with_adjacent.items():
113+
adjacent[k] = entities.get_connected_entities(
114+
etype=v.with_class,
115+
constraints=v.constraints)
116+
117+
entities.decorator = cls.__decorator
118+
entities.adjacent = adjacent
119+
71120
def __init__(self, db: Connector = None, response: dict = None, type: str = None) -> None:
72121
super().__init__()
73122
self.db = db
@@ -99,7 +148,7 @@ def __sub__(self, other: Entities) -> Entities:
99148
def sort(self, key) -> Entities:
100149
return Entities(response = sorted(self.response, key=key), type=self.type)
101150

102-
def inspect(self) -> pd.DataFrame:
151+
def inspect(self, **kwargs) -> pd.DataFrame:
103152
return pd.json_normalize([item for item in self])
104153

105154
def update_properties(self, extra_properties: List[dict]) -> bool:
@@ -129,6 +178,7 @@ def get_connected_entities(self, etype: Union[ObjectType, str], constraints: Co
129178
List[Entities]: _description_
130179
"""
131180
result = []
181+
entity_class = etype.value if isinstance(etype, ObjectType) else etype
132182
for entity in self:
133183
query = [
134184
{
@@ -144,7 +194,7 @@ def get_connected_entities(self, etype: Union[ObjectType, str], constraints: Co
144194
"is_connected_to": {
145195
"ref": 1
146196
},
147-
"with_class": type.value,
197+
"with_class": entity_class,
148198
"constraints": constraints.constraints,
149199
"results": {
150200
"all_properties": True
@@ -153,10 +203,34 @@ def get_connected_entities(self, etype: Union[ObjectType, str], constraints: Co
153203
}
154204
]
155205
res, r, b = execute_batch(query, [], self.db)
156-
result.append(self.known_entities[type.value](
157-
db=self.db, response=r[1]["FindEntity"]["entities"], type=type.value))
206+
cl = Entities
207+
if entity_class in self.known_entities:
208+
cl = self.known_entities[entity_class]
209+
result.append(cl(
210+
db=self.db, response=r[1]["FindEntity"]["entities"], type=entity_class))
158211
return result
159212

213+
def get_blob(self, entity) -> Any:
214+
"""
215+
Helper to get blobs for FindImage, FindVideo and FindBlob commands.
216+
"""
217+
query = [
218+
{
219+
self.find_command: {
220+
"constraints": {
221+
"_uniqueid": ["==", entity["_uniqueid"]]
222+
},
223+
"blobs": True,
224+
"uniqueids": True,
225+
"results": {
226+
"count": True
227+
}
228+
}
229+
}
230+
]
231+
res, r, b = execute_batch(query, [], self.db)
232+
return b[0]
233+
160234

161235
def load_entities_registry(custom_entities: List[str] = None) -> dict:
162236
from aperturedb.Polygons import Polygons

aperturedb/Images.py

Lines changed: 6 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -16,59 +16,28 @@
1616
from io import BytesIO
1717
from PIL import Image
1818
from pandas import DataFrame
19+
import logging
20+
logger = logging.getLogger(__name__)
1921

2022

2123
class Images(Entities):
2224
db_object = "_Image"
2325

24-
# This needs to be defined so that the application can access the adjacent items,
25-
# with every item of this iterable.
26-
@classmethod
27-
def __decorator(cls, index, adjacent):
28-
item = {}
29-
for k, v in adjacent.items():
30-
item[k] = v[index]
31-
return item
32-
3326
@classmethod
3427
def retrieve(cls,
3528
db: Connector,
3629
spec: Query,
3730
with_adjacent: Dict[str, Query] = None) -> Images:
3831
spec.with_class = cls.db_object
39-
# Sice adjacent items are usually a way to filter the results,
40-
# the native query is constructed in the reverse order, with
41-
# first filtering out the relavant itmes based on adjacent items.
42-
fs = None
43-
count = 0
44-
if with_adjacent:
45-
for k, v in with_adjacent.items():
46-
if fs is None:
47-
fs = v
48-
else:
49-
fs = fs.connected_to(v)
50-
count += 1
51-
# Eventually, connect the specification of Images to the specification of the adjacent items.
52-
fs = fs.connected_to(spec)
53-
else:
54-
fs = spec
55-
56-
results = Entities.retrieve(db=db, spec=fs)
32+
33+
results = Entities.retrieve(
34+
db=db, spec=spec, with_adjacent=with_adjacent)
5735

5836
# A Polygon is only connected to 1 image, and our query is filtered with
5937
# meta info from polygon, so connect the right image to the polygon
6038
# That being said, the ordering should be same as that of the first command in the query
6139
images = results[-1]
6240

63-
adjacent = {}
64-
if with_adjacent:
65-
for k, v in with_adjacent.items():
66-
adjacent[k] = images.get_connected_entities(
67-
type=EntityType(v.with_class),
68-
constraints=v.constraints)
69-
70-
images.decorator = cls.__decorator
71-
images.adjacent = adjacent
7241
return images
7342

7443
def inspect(self, use_thumbnails=True) -> Union[Tuple[widgets.IntSlider, widgets.Output], DataFrame]:
@@ -102,7 +71,6 @@ def getitem(self, idx):
10271
if self.get_image == True:
10372
buffer = self.get_image_by_index(idx)
10473
if buffer is not None:
105-
# nparr = np.frombuffer(buffer, dtype=np.uint8)
10674
item['thumbnail'] = Image.fromarray(
10775
self.get_np_image_by_index(idx))
10876
return item
@@ -308,7 +276,7 @@ def total_results(self):
308276
def get_image_by_index(self, index):
309277

310278
if index >= len(self.images_ids):
311-
print("Index is incorrect")
279+
logger.info("Index is incorrect")
312280
return
313281

314282
uniqueid = self.images_ids[index]

0 commit comments

Comments
 (0)