Skip to content

Commit

Permalink
#8: moved example to examples/ directory
Browse files Browse the repository at this point in the history
  • Loading branch information
Kraysent committed Jul 8, 2024
1 parent 1dd467d commit 358e634
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 54 deletions.
28 changes: 1 addition & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,30 +4,4 @@ Install latest version:
pip install git+https://github.com/HyperLEDA/client.git
```

Usage:

```python
import os
import hyperleda

token = os.getenv("HYPERLEDA_TOKEN") # token is optional for non-admin handlers
client = hyperleda.HyperLedaClient(token=token)
print(client.create_table(hyperleda.CreateTableRequestSchema(
table_name="test_table",
columns=[
hyperleda.ColumnDescription(
name="ra",
data_type="double",
unit="deg",
description="Right ascension",
),
hyperleda.ColumnDescription(
name="dec",
data_type="double",
unit="deg",
description="Declination",
),
],
bibcode="1992ApJ...400L...1W",
)))
```
For examples, see https://github.com/HyperLEDA/client/tree/master/examples.
48 changes: 48 additions & 0 deletions examples/create_table.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import random

import pandas

import hyperleda

client = hyperleda.HyperLedaClient()

table_name = f"test_table_{random.randrange(0, 100000)}"

table_id = client.create_table(
hyperleda.CreateTableRequestSchema(
table_name=table_name,
columns=[
hyperleda.ColumnDescription(
name="ra",
data_type=hyperleda.DataType("double"),
unit="deg",
description="Right ascension",
),
hyperleda.ColumnDescription(
name="dec",
data_type=hyperleda.DataType("double"),
unit="deg",
description="Declination",
),
],
bibcode="1992ApJ...400L...1W",
)
)

print(f"Created table '{table_name}' with ID: {table_id}")

data = pandas.DataFrame(
[
{
"ra": 123.45,
"dec": 25.56,
},
{
"ra": 52.1,
"dec": 65.58,
},
]
)

client.add_data(table_id, data)
print(f"Added data to the table '{table_name}':\n{data}")
5 changes: 2 additions & 3 deletions hyperleda/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
from hyperleda import config, model
from hyperleda.client import HyperLedaClient
from hyperleda.config import *
from hyperleda.error import APIError

__all__ = ["HyperLedaClient", "model", "APIError", "config"]
from hyperleda.model import *
71 changes: 47 additions & 24 deletions hyperleda/client.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import dataclasses
import enum
import json
from typing import Any

import pandas
Expand All @@ -7,6 +9,27 @@
from hyperleda import config, error, model


def _clean_dict(d):
"""
Recursively remove keys with None values from the dictionary.
"""
clean = {}
for k, v in d.items():
if isinstance(v, dict):
nested = _clean_dict(v)
if nested: # Only add non-empty nested dictionaries
clean[k] = nested
elif v is not None:
clean[k] = v
return clean


def _marshaller(obj):
if isinstance(obj, enum.Enum):
return obj.value
return str(obj)


class HyperLedaClient:
"""
This is client for HyperLeda service. It allows one to query different types of data from the database
Expand All @@ -17,31 +40,28 @@ def __init__(self, endpoint: str = config.DEFAULT_ENDPOINT, token: str | None =
self.endpoint = endpoint
self.token = token

def _set_auth(self, headers: dict[str, str]) -> dict[str, str]:
if self.token is not None:
headers["Authorization"] = f"Bearer {self.token}"

return headers

def _post(self, path: str, request: Any) -> dict[str, Any]:
def _request(
self, method: str, path: str, body: dict[str, Any] | None = None, query: dict[str, str] | None = None
) -> dict[str, Any]:
headers = {}
if path.startswith("/api/v1/admin"):
headers = self._set_auth(headers)
if self.token is not None:
headers["Authorization"] = f"Bearer {self.token}"

response = requests.post(f"{self.endpoint}{path}", json=dataclasses.asdict(request), headers=headers)
kwargs = {}

if not response.ok:
raise error.APIError.from_dict(response.json())
if body is not None:
body = _clean_dict(body) if body is not None else None
data = json.dumps(body, default=_marshaller)
kwargs["data"] = data

return response.json()

def _get(self, path: str, query: dict[str, str]) -> dict[str, Any]:
headers = {}
if path.startswith("/api/v1/admin"):
headers = self._set_auth(headers)
if query is not None:
kwargs["params"] = query

response = requests.get(f"{self.endpoint}{path}", params=query, headers=headers)
if len(headers) != 0:
kwargs["headers"] = headers

response = requests.request(method, f"{self.endpoint}{path}", **kwargs)
if not response.ok:
raise error.APIError.from_dict(response.json())

Expand All @@ -52,9 +72,10 @@ def create_internal_source(self, title: str, authors: list[str], year: int) -> s
Creates new source entry in the database for internal communication and unpublished articles.
Responds with internally generated code for the source which can be used as bibcode in other methods.
"""
data = self._post(
data = self._request(
"POST",
"/api/v1/admin/source",
model.CreateSourceRequestSchema(title=title, authors=authors, year=year),
dataclasses.asdict(model.CreateSourceRequestSchema(title=title, authors=authors, year=year)),
)

return model.CreateSourceResponseSchema(**data["data"]).code
Expand All @@ -63,9 +84,10 @@ def create_table(self, table_description: model.CreateTableRequestSchema) -> int
"""
Create new table with raw data from the source.
"""
data = self._post(
data = self._request(
"POST",
"/api/v1/admin/table",
table_description,
dataclasses.asdict(table_description),
)

return model.CreateTableResponseSchema(**data["data"]).id
Expand All @@ -74,7 +96,8 @@ def add_data(self, table_id: int, data: pandas.DataFrame) -> None:
"""
Add new data to the table created in `create_table` method.
"""
_ = self._post(
_ = self._request(
"POST",
"/api/v1/admin/table/data",
model.AddDataRequestSchema(table_id, data.to_dict("records")),
dataclasses.asdict(model.AddDataRequestSchema(table_id, data.to_dict("records"))),
)

0 comments on commit 358e634

Please sign in to comment.