Skip to content

Commit 7dbb44f

Browse files
committed
added data client
1 parent 1b2bea0 commit 7dbb44f

4 files changed

Lines changed: 161 additions & 1 deletion

File tree

hyperleda/common.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from typing import Any
2+
3+
import requests
4+
5+
from hyperleda import error, utils
6+
7+
8+
def request(method: str, url: str, query: dict[str, Any] | None = None, stream: bool = False) -> requests.Response:
9+
kwargs = {}
10+
if query is not None:
11+
kwargs["params"] = utils.clean_dict(query)
12+
if stream:
13+
kwargs["stream"] = True
14+
response = requests.request(method, url, **kwargs)
15+
if not response.ok:
16+
try:
17+
msg = error.APIError.from_dict(response.json())
18+
except Exception:
19+
msg = error.APIError(response.status_code, response.text)
20+
raise msg
21+
return response

hyperleda/data/client.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import dataclasses
2+
from typing import Any
3+
4+
from hyperleda import common, config
5+
from hyperleda.data import model
6+
7+
8+
class HyperLedaDataClient:
9+
"""
10+
Client for the HyperLeda data API, providing access to public query endpoints.
11+
"""
12+
13+
def __init__(self, endpoint: str = config.DEFAULT_ENDPOINT):
14+
self.endpoint = endpoint
15+
16+
def _request(self, method: str, path: str, query: dict[str, Any] | None = None, stream: bool = False) -> Any:
17+
return common.request(method, f"{self.endpoint}{path}", query=query, stream=stream)
18+
19+
def query_simple(self, req: model.QuerySimpleRequestSchema) -> model.QuerySimpleResponseSchema:
20+
"""
21+
Query data about objects using simple parameters (AND logic).
22+
"""
23+
response = self._request(
24+
"GET",
25+
"/api/v1/query/simple",
26+
query=dataclasses.asdict(req),
27+
)
28+
data = response.json()
29+
return model.QuerySimpleResponseSchema(**data["data"])
30+
31+
def query(self, req: model.QueryRequestSchema) -> model.QueryResponseSchema:
32+
"""
33+
Query data about objects using a query string (functions and operators).
34+
"""
35+
response = self._request(
36+
"GET",
37+
"/api/v1/query",
38+
query=dataclasses.asdict(req),
39+
)
40+
data = response.json()
41+
return model.QueryResponseSchema(**data["data"])
42+
43+
def query_fits(self, req: model.FITSRequestSchema) -> bytes:
44+
"""
45+
Query data about objects and return as FITS file (binary).
46+
"""
47+
response = self._request(
48+
"GET",
49+
"/api/v1/query/fits",
50+
query=dataclasses.asdict(req),
51+
stream=True,
52+
)
53+
return response.content

hyperleda/data/model.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# generated by datamodel-codegen:
2+
# filename: swagger.json
3+
# timestamp: 2025-06-07T08:18:05+00:00
4+
5+
from __future__ import annotations
6+
7+
from dataclasses import dataclass
8+
from enum import Enum
9+
from typing import Any
10+
11+
12+
class PageSize(Enum):
13+
integer_10 = 10
14+
integer_25 = 25
15+
integer_50 = 50
16+
integer_100 = 100
17+
18+
19+
@dataclass
20+
class QuerySimpleRequestSchema:
21+
pgcs: list[int] | None = None
22+
ra: float | None = None
23+
dec: float | None = None
24+
radius: float | None = None
25+
name: str | None = None
26+
cz: float | None = None
27+
cz_err_percent: float | None = None
28+
page_size: PageSize | None = PageSize.integer_25
29+
page: int | None = 0
30+
31+
32+
@dataclass
33+
class PGCObject:
34+
pgc: int | None = None
35+
catalogs: dict[str, dict[str, Any]] | None = None
36+
37+
38+
@dataclass
39+
class QuerySimpleResponseSchema:
40+
objects: list[PGCObject] | None = None
41+
42+
43+
@dataclass
44+
class QueryRequestSchema:
45+
q: str | None = None
46+
page_size: PageSize | None = PageSize.integer_25
47+
page: int | None = 0
48+
49+
50+
@dataclass
51+
class QueryResponseSchema:
52+
objects: list[PGCObject] | None = None
53+
54+
55+
class PageSize2(Enum):
56+
integer_10 = 10
57+
integer_25 = 25
58+
integer_50 = 50
59+
integer_100 = 100
60+
integer_1000 = 1000
61+
integer_10000 = 10000
62+
integer_100000 = 100000
63+
64+
65+
@dataclass
66+
class FITSRequestSchema:
67+
pgcs: list[int] | None = None
68+
ra: float | None = None
69+
dec: float | None = None
70+
radius: float | None = None
71+
name: str | None = None
72+
cz: float | None = None
73+
cz_err_percent: float | None = None
74+
page_size: PageSize2 | None = PageSize2.integer_25
75+
page: int | None = 0
76+
77+
78+
@dataclass
79+
class Schema:
80+
pass

makefile

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,18 @@ build:
1111
$(PYTHON) -m build .
1212

1313
# to use the model one needs to start the server on localhost:8080
14-
generate-model:
14+
generate-admin-model:
1515
mkdir -p hyperleda/gen
1616
curl http://localhost:8080/admin/api/docs/swagger.json > hyperleda/gen/swagger.json
1717
datamodel-codegen --input hyperleda/gen/swagger.json --output hyperleda/model.py --output-model-type dataclasses.dataclass --input-file-type openapi
1818
make fix
1919

20+
generate-data-model:
21+
mkdir -p hyperleda/data/gen
22+
curl http://localhost:8080/api/docs/swagger.json > hyperleda/data/gen/swagger.json
23+
datamodel-codegen --input hyperleda/data/gen/swagger.json --output hyperleda/data/model.py --output-model-type dataclasses.dataclass --input-file-type openapi
24+
make fix
25+
2026
# Testing
2127

2228
check: check-format check-lint

0 commit comments

Comments
 (0)