Skip to content

Commit a70ce76

Browse files
authored
Merge pull request #3 from cauldron/simple
Add simpler version of client library
2 parents 090741c + e00035e commit a70ce76

11 files changed

Lines changed: 2181 additions & 0 deletions

File tree

pyst_client/example/Simple client library guide.ipynb

Lines changed: 1063 additions & 0 deletions
Large diffs are not rendered by default.

pyst_client/simple/__init__.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
__all__ = (
2+
"settings",
3+
"ConceptScheme",
4+
"Concept",
5+
"Correspondence",
6+
"Relationship",
7+
"RelationshipVerbs",
8+
"AssociationKind",
9+
"Association",
10+
)
11+
12+
from py_semantic_taxonomy.domain.constants import AssociationKind, RelationshipVerbs
13+
14+
from pyst_client.simple.classes.association import Association
15+
from pyst_client.simple.classes.concept import Concept
16+
from pyst_client.simple.classes.concept_scheme import ConceptScheme
17+
from pyst_client.simple.classes.correspondence import Correspondence
18+
from pyst_client.simple.classes.relationship import Relationship
19+
from pyst_client.simple.settings import settings

pyst_client/simple/classes/__init__.py

Whitespace-only changes.
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import webbrowser
2+
from urllib.parse import urljoin
3+
4+
import orjson
5+
from py_semantic_taxonomy.domain.url_utils import get_full_api_path
6+
7+
from pyst_client.simple.client import APIError, get_read_client, get_write_client
8+
from pyst_client.simple.settings import settings
9+
10+
11+
class APIBase:
12+
@property
13+
def api_url(self) -> str:
14+
return urljoin(settings.server_url, self.api_path)
15+
16+
@property
17+
def web_url(self) -> str:
18+
return urljoin(settings.server_url, self.web_path)
19+
20+
def open_new_tab(self) -> None:
21+
webbrowser.open_new_tab(self.web_url)
22+
23+
def json(self) -> bytes:
24+
return orjson.dumps(self.to_json_ld())
25+
26+
def save(self, already_exists: bool = False) -> None:
27+
client = get_write_client()
28+
if not already_exists:
29+
try:
30+
return client.post(
31+
self.api_path,
32+
data=self.json(),
33+
)
34+
except APIError as err:
35+
if err.status_code != 409:
36+
raise err
37+
return client.put(
38+
self.api_path,
39+
data=[self.json()],
40+
)
41+
42+
def delete(self) -> None:
43+
client = get_write_client()
44+
return client.delete(
45+
self.api_path,
46+
)
47+
48+
@classmethod
49+
def _get_many(
50+
cls,
51+
url_label: str,
52+
params: dict = {},
53+
timeout: int = 5,
54+
) -> list[dict]:
55+
return get_read_client().get(
56+
get_full_api_path(url_label),
57+
params=params,
58+
timeout=timeout,
59+
)
60+
61+
@classmethod
62+
def _get_one(
63+
cls,
64+
url_label: str,
65+
iri: str,
66+
timeout: int = 5,
67+
) -> dict:
68+
return get_read_client().get(
69+
get_full_api_path(url_label, iri=iri),
70+
timeout=timeout,
71+
)
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
import uuid
2+
3+
import orjson
4+
import structlog
5+
from py_semantic_taxonomy.adapters.routers import request_dto as req
6+
from py_semantic_taxonomy.domain import entities
7+
from py_semantic_taxonomy.domain.constants import XKOS, AssociationKind
8+
from py_semantic_taxonomy.domain.url_utils import get_full_api_path
9+
10+
from pyst_client.simple.classes.api_base import APIBase
11+
from pyst_client.simple.classes.concept import Concept
12+
from pyst_client.simple.classes.correspondence import Correspondence
13+
from pyst_client.simple.client import get_write_client
14+
from pyst_client.simple.settings import can_write, settings
15+
16+
logger = structlog.get_logger("pyst-client")
17+
18+
19+
class Association(entities.Association, APIBase):
20+
def open_new_tab(self) -> None:
21+
raise NotImplementedError
22+
23+
@property
24+
def api_path(self) -> str:
25+
return get_full_api_path("association", iri=self.id_)
26+
27+
@classmethod
28+
def get_one(
29+
cls,
30+
iri: str,
31+
timeout: int = 5,
32+
) -> "Association":
33+
return cls.from_json_ld(
34+
orjson.loads(cls._get_one("association", iri, timeout=timeout).text)
35+
)
36+
37+
@classmethod
38+
def get_many(
39+
cls,
40+
correspondence: Correspondence | str | None = None,
41+
source_concept: Concept | str | None = None,
42+
target_concept: Concept | str | None = None,
43+
kind: AssociationKind | None = None,
44+
timeout: int = 5,
45+
) -> list["Concept"]:
46+
params = {
47+
"correspondence_iri": (
48+
correspondence.id_
49+
if isinstance(correspondence, Correspondence)
50+
else correspondence
51+
),
52+
"source_concept_iri": (
53+
source_concept.id_
54+
if isinstance(source_concept, Concept)
55+
else source_concept
56+
),
57+
"target_concept_iri": (
58+
target_concept.id_
59+
if isinstance(target_concept, Concept)
60+
else target_concept
61+
),
62+
"kind": kind or AssociationKind.simple,
63+
}
64+
return [
65+
cls.from_json_ld(obj)
66+
for obj in orjson.loads(
67+
cls._get_many(
68+
"association_all",
69+
params={
70+
key: value for key, value in params.items() if value is not None
71+
},
72+
timeout=timeout,
73+
).text
74+
)
75+
]
76+
77+
@classmethod
78+
@can_write
79+
def create(
80+
cls,
81+
*,
82+
correspondence: Correspondence,
83+
source_concepts: list[Concept | dict],
84+
target_concepts: list[Concept | dict],
85+
extra: dict | None = None,
86+
id_: str | None = None,
87+
) -> "Concept":
88+
if id_ is None:
89+
id_ = "{}{}{}/{}".format(
90+
settings.creation_base_url,
91+
"" if settings.creation_base_url.endswith("/") else "/",
92+
correspondence.notations[0]["@value"],
93+
uuid.uuid4().hex,
94+
)
95+
extra = extra or {}
96+
extra[f"{XKOS}Correspondence"] = correspondence.id_
97+
assoc = cls(
98+
id_=id_,
99+
types=[f"{XKOS}ConceptAssociation"],
100+
source_concepts=[
101+
({"@id": obj.id_} if isinstance(obj, Concept) else obj)
102+
for obj in source_concepts
103+
],
104+
target_concepts=[
105+
({"@id": obj.id_} if isinstance(obj, Concept) else obj)
106+
for obj in target_concepts
107+
],
108+
kind=(
109+
AssociationKind.simple
110+
if len(source_concepts) == 1
111+
else AssociationKind.conditional
112+
),
113+
extra=extra or {},
114+
)
115+
req.Association(**assoc.to_json_ld())
116+
return assoc
117+
118+
def save(self) -> None:
119+
client = get_write_client()
120+
client.post(
121+
self.api_path,
122+
data=self.json(),
123+
)
124+
# Add association to correspondence
125+
if f"{XKOS}Correspondence" in self.extra:
126+
client.post(
127+
get_full_api_path("made_of"),
128+
data=orjson.dumps(
129+
entities.MadeOf(
130+
id_=self.extra[f"{XKOS}Correspondence"],
131+
made_ofs=[{"@id": self.id_}],
132+
).to_json_ld()
133+
),
134+
)
135+
else:
136+
logger.warning(
137+
"Unable to automatically add this association to a `Correspondence` object"
138+
)
139+
140+
def delete(self) -> None:
141+
client = get_write_client()
142+
return client.delete(
143+
self.api_path,
144+
)
145+
if f"{XKOS}Correspondence" in self.extra:
146+
client.request(
147+
url=get_full_api_path("made_of"),
148+
method="DELETE",
149+
content=orjson.dumps(
150+
entities.MadeOf(
151+
id_=self.extra[f"{XKOS}Correspondence"],
152+
made_ofs=[{"@id": self.id_}],
153+
).to_json_ld()
154+
),
155+
)
156+
else:
157+
logger.warning(
158+
"Unable to automatically remove this association from a `Correspondence` object"
159+
)

0 commit comments

Comments
 (0)