Skip to content

Commit b62fc46

Browse files
committed
Add example that uses filtering
1 parent f261712 commit b62fc46

6 files changed

Lines changed: 137 additions & 39 deletions

File tree

‎examples/aiola/simple_filter.py‎

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# %% [markdown]
2+
# This script shows off how we can filter collections down to flat lists of
3+
# products using the tools in HIPPO. The main object of interest here is the
4+
# `FilteredCollection` class.
5+
#
6+
# The hippo API is broken down into modules that you can use to interact wtih certain
7+
# sub-sections of the API. Here, we're going to be interacting with three main concepts:
8+
#
9+
# 1. Collections: A collection is a group of products that are related in some way. For
10+
# our case here, the colleciton is the DR4/S13 150 GHz data.
11+
# 2. Products: A product is a single piece of data. In this case, we're going to be
12+
# working with the "splits" in a particular patch.
13+
# 3. Relationships: Products can be related to one another. In this case, we're going to
14+
# be creating a parent/child relationship between the splits and the co-added map.
15+
#
16+
# There are two main objects of interest that are accessed through the core
17+
# `ClientSettings` singleton:
18+
#
19+
# 1. The `client`: this object is your HTTP link to the hippo server.
20+
# 2. The `cache`: this is an on-disk cache of products in hippo. This is the only
21+
# way you should interact with the filesystem for existing products when using hippo.
22+
# %%
23+
from henry import Henry
24+
from hippoclient.core import ClientSettings
25+
26+
settings = ClientSettings(
27+
host="http://localhost:8000",
28+
token_tag=None,
29+
caches=[
30+
{
31+
"type": "filesystem",
32+
"path": "/tmp",
33+
}
34+
],
35+
)
36+
37+
henry = Henry(settings=settings, slugify=True)
38+
39+
# %% [markdown]
40+
# We can filter on three things:
41+
#
42+
# - Product type (based on its metadata class)
43+
# - Metadata keys and values
44+
# - Available slugs.
45+
#
46+
47+
# %%
48+
PATCH = "D6"
49+
COLLECTION_ID = "6aee15399c65b086f7388bc4"
50+
51+
if COLLECTION_ID is None:
52+
raise ValueError(
53+
"Please set the COLLECTION variable to the ID of the collection you want to use."
54+
)
55+
56+
collection = henry.pull_collection(
57+
COLLECTION_ID, realize_sources=False, pull_children=True
58+
)
59+
60+
filtered = collection.filter(
61+
metadata_filter={"patch": PATCH},
62+
)
63+
64+
print("Found", len(filtered.products), "matching products")
65+
66+
67+
# %% [markdown]
68+
# We can further filter collections, using the same functionality. If we are _sure_
69+
# that we want to have only a single (or no) product, we can realize this with the
70+
# `one` method. The first cell below -- called on our filtered collection -- will raise
71+
# an error:
72+
73+
# %%
74+
match = filtered.one()
75+
76+
# %%
77+
SPLIT = "set1"
78+
79+
second_filter = filtered.filter(
80+
metadata_filter={"split": SPLIT},
81+
)
82+
83+
print("Found", len(second_filter.products), "matching products")
84+
product = second_filter.one()
85+
86+
# %% [markdown]
87+
# Now we have a single product that matches our query. There are two options here:
88+
# we can realize this product, or check it out to the filesystem.
89+
90+
# %%
91+
realized_product = henry.realize(product)
92+
print(realized_product)
93+
# %% [markdown]
94+
# This realizes all of the 'slugs' and associated sources. If you wanted just a sub-set
95+
# you could have pasesd the 'slugs' argument. To check out the product to the filesystem,
96+
# you can also use the `checkout` method:
97+
98+
# %%
99+
checked_out_product = henry.checkout(
100+
product, directory="/tmp/product_checkout", slugs=["xlink"]
101+
)
102+
103+
# %%
104+
print(checked_out_product)
105+
for slug, source in checked_out_product.sources.items():
106+
print(f"Slug: {slug}, Source: {source}")
107+
# %% [markdown]
108+
# With both, we can now access the products directly:
109+
110+
# %%
111+
print("Realized source_only map:", realized_product["source_only"])
112+
print("Checked out xlink map:", checked_out_product["xlink"])
113+
114+
# %%
115+
from astropy.io import fits
116+
117+
source_only_map = fits.open(realized_product["source_only"].path)
118+
xlink_map = fits.open(checked_out_product["xlink"].path)
119+
# %%

‎examples/aiola/simplepopulate.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ def link_match(x):
174174
patch=get_patch(next(iter(maps.values()))),
175175
frequency="150",
176176
polarization_convention="IAU",
177+
split=get_set(next(iter(maps.values()))),
177178
)
178179
primary_map = find_primary_map(maps)
179180

‎henry/core.py‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,21 @@ def realize(
155155
"""
156156

157157
if isinstance(item, RemoteProduct):
158-
cache_product(
158+
sources = cache_product(
159159
client=self.client,
160160
cache=self.cache,
161161
console=self.console,
162162
id=item.product_id,
163163
slugs=slugs,
164164
)
165+
166+
item = item.model_copy(
167+
update={
168+
"sources": {
169+
slug: LocalSource(path=path) for slug, path in sources.items()
170+
}
171+
}
172+
)
165173
elif isinstance(item, (RemoteCollection, FilteredCollection)):
166174
for i in item:
167175
self.realize(i, slugs=slugs)
@@ -188,7 +196,7 @@ def checkout(
188196
slugs=slugs,
189197
)
190198

191-
item.sources = self.read_product(directory=path).sources
199+
return self.read_product(directory=path)
192200
elif isinstance(item, (RemoteCollection, FilteredCollection)):
193201
name = apply_slugify(item.name) if self.slugify else item.name
194202
directory /= name

‎hippoclient/collections.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ def cache(
332332
paths = []
333333

334334
for product in collection.products:
335-
paths += cache_product(client, multi_cache, str(product.id))
335+
paths += cache_product(client, multi_cache, str(product.id)).values()
336336

337337
for child in collection.child_collections:
338338
paths += cache(client, multi_cache, str(child.id))

‎hippoclient/product.py‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,7 @@ def cache(
488488
id: str,
489489
console: Console | None = None,
490490
slugs: list[str] | None = None,
491-
) -> list[Path]:
491+
) -> dict[str, Path]:
492492
"""
493493
Cache a product from hippo.
494494
@@ -507,8 +507,8 @@ def cache(
507507
508508
Returns
509509
-------
510-
list[Path]
511-
The list of paths to the cached sources.
510+
dict[str, Path]
511+
The dictionary of slugs to paths to the cached sources.
512512
513513
Raises
514514
------
@@ -531,13 +531,13 @@ def cache(
531531
if console:
532532
console.print(f"Successfully read product {id}")
533533

534-
response_paths = []
534+
response_paths = {}
535535

536536
for file in post_upload_files.values():
537537
# See if it's already cached.
538538
try:
539539
cached = cache.available(file.uuid)
540-
response_paths.append(cached)
540+
response_paths[file.slug] = cached
541541

542542
if console:
543543
console.print(f"Found cached file {file.name}", style="green")
@@ -558,7 +558,7 @@ def cache(
558558
presigned_url=file.url,
559559
)
560560

561-
response_paths.append(cached)
561+
response_paths[file.slug] = cached
562562

563563
if console:
564564
console.print(f"Cached file {file.name} ({file.uuid})", style="yellow")

‎tests/test_client/test_realize_checkout.py‎

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -170,36 +170,6 @@ def test_checkout_empty_filtered_collection(remote_collection, client, tmp_path)
170170
assert list(client.read_collection(directory, allow_incomplete=False)) == []
171171

172172

173-
@pytest.mark.parametrize("mode", ["id", "response"])
174-
@pytest.mark.parametrize("slugify", [False, True])
175-
def test_download_collection(remote_collection, client, tmp_path, mode, slugify):
176-
if mode == "response":
177-
metadata = collections.read(client.client, remote_collection.collection_id)
178-
directory = collections.download_direct(
179-
collection=metadata,
180-
client=client.client,
181-
directory=tmp_path,
182-
console=client.console,
183-
slugify=slugify,
184-
)
185-
assert metadata.name == remote_collection.name
186-
else:
187-
directory = collections.download(
188-
client=client.client,
189-
id=remote_collection.collection_id,
190-
directory=tmp_path,
191-
console=client.console,
192-
slugify=slugify,
193-
)
194-
195-
downloaded = client.read_collection(directory, allow_incomplete=False)
196-
197-
assert downloaded.collection_id == remote_collection.collection_id
198-
assert downloaded.products[0].sources["map"].path.read_text() == "Root product map"
199-
child = downloaded.collections[0].products[0]
200-
assert child.sources["ivar"].path.read_text() == "Child product ivar"
201-
202-
203173
def test_realize_and_checkout_local_product(client, tmp_path):
204174
item = client.new_product(
205175
name="Local product",

0 commit comments

Comments
 (0)