Skip to content

Commit 16b0baf

Browse files
authored
Add get routes for reflections/orientations (#138)
* added get routes * removed types folder, and made docstrings * fix small pydocstyle errors
1 parent 930e098 commit 16b0baf

File tree

6 files changed

+327
-3
lines changed

6 files changed

+327
-3
lines changed

src/diffcalc_api/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""API to expose diffcalc-core methods."""
22

3-
from . import config, database, openapi, server
3+
from . import config, database, openapi, server, types
44
from ._version_git import __version__
55

6-
__all__ = ["__version__", "server", "config", "database", "openapi"]
6+
__all__ = ["__version__", "server", "config", "database", "types", "openapi"]

src/diffcalc_api/models/response.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from pydantic import BaseModel
55

66
from diffcalc_api.models.ub import HklModel, MiscutModel, SphericalCoordinates, XyzModel
7+
from diffcalc_api.types import Orientation, Reflection
78

89

910
class InfoResponse(BaseModel):
@@ -80,3 +81,31 @@ class MiscutResponse(BaseModel):
8081
"""
8182

8283
payload: MiscutModel
84+
85+
86+
class ReflectionResponse(BaseModel):
87+
"""Response for any operation returning a reflection."""
88+
89+
payload: Reflection
90+
91+
class Config:
92+
"""Necessary config to make validation easier.
93+
94+
As this is a response model, there is no need to enforce validation.
95+
"""
96+
97+
orm_mode = True
98+
99+
100+
class OrientationResponse(BaseModel):
101+
"""Response for any operation returning an orientation."""
102+
103+
payload: Orientation
104+
105+
class Config:
106+
"""Necessary config to make validation easier.
107+
108+
As this is a response model, there is no need to enforce validation.
109+
"""
110+
111+
orm_mode = True

src/diffcalc_api/routes/ub.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
ArrayResponse,
1515
InfoResponse,
1616
MiscutResponse,
17+
OrientationResponse,
1718
ReciprocalSpaceResponse,
19+
ReflectionResponse,
1820
SphericalResponse,
1921
StringResponse,
2022
)
@@ -33,6 +35,7 @@
3335
)
3436
from diffcalc_api.services import ub as service
3537
from diffcalc_api.stores.protocol import HklCalcStore, get_store
38+
from diffcalc_api.types import Orientation, Position, Reflection
3639

3740
router = APIRouter(prefix="/ub", tags=["ub"])
3841

@@ -62,6 +65,48 @@ async def get_ub_status(
6265
#######################################################################################
6366

6467

68+
@router.get("/{name}/reflection", response_model=ReflectionResponse)
69+
async def get_reflection(
70+
name: str,
71+
store: HklCalcStore = Depends(get_store),
72+
collection: Optional[str] = Query(default=None, example="B07"),
73+
tag: Optional[str] = Query(default=None, example="refl1"),
74+
idx: Optional[int] = Query(default=None),
75+
):
76+
"""Get a reflection from the UBCalculation object.
77+
78+
Both tag and idx cannot be provided. One or the other must be provided.
79+
80+
Args:
81+
name: the name of the hkl object to access within the store
82+
store: accessor to the hkl object
83+
collection: collection within which the hkl object resides
84+
tag: optional tag to access the reflection
85+
idx: optional index to access the reflection
86+
87+
Returns:
88+
ReflectionResponse
89+
payload containing the reflection.
90+
"""
91+
if (tag is None) and (idx is None):
92+
raise NoTagOrIdxProvidedError()
93+
94+
if (tag is not None) and (idx is not None):
95+
raise BothTagAndIdxProvidedError()
96+
97+
ref = await service.get_reflection(name, store, collection, tag, idx)
98+
99+
reflection = Reflection(
100+
ref.h,
101+
ref.k,
102+
ref.l,
103+
Position(**ref.pos.asdict),
104+
ref.energy,
105+
ref.tag,
106+
)
107+
return ReflectionResponse(payload=reflection)
108+
109+
65110
@router.post("/{name}/reflection", response_model=InfoResponse)
66111
async def add_reflection(
67112
name: str,
@@ -163,6 +208,50 @@ async def delete_reflection(
163208
#######################################################################################
164209

165210

211+
@router.get("/{name}/orientation", response_model=OrientationResponse)
212+
async def get_orientation(
213+
name: str,
214+
store: HklCalcStore = Depends(get_store),
215+
collection: Optional[str] = Query(default=None, example="B07"),
216+
tag: Optional[str] = Query(default=None, example="refl1"),
217+
idx: Optional[int] = Query(default=None),
218+
):
219+
"""Get an orientation from the UBCalculation object.
220+
221+
Both tag and idx cannot be provided. One or the other must be provided.
222+
223+
Args:
224+
name: the name of the hkl object to access within the store
225+
store: accessor to the hkl object
226+
collection: collection within which the hkl object resides
227+
tag: optional tag to access the orientation
228+
idx: optional index to access the orientation
229+
230+
Returns:
231+
OrientationResponse
232+
payload containing the orientation.
233+
"""
234+
if (tag is None) and (idx is None):
235+
raise NoTagOrIdxProvidedError()
236+
237+
if (tag is not None) and (idx is not None):
238+
raise BothTagAndIdxProvidedError()
239+
240+
orient = await service.get_orientation(name, store, collection, tag, idx)
241+
242+
orientation = Orientation(
243+
orient.h,
244+
orient.k,
245+
orient.l,
246+
orient.x,
247+
orient.y,
248+
orient.z,
249+
Position(**orient.pos.asdict),
250+
orient.tag if orient.tag is not None else "",
251+
)
252+
return OrientationResponse(payload=orientation)
253+
254+
166255
@router.post("/{name}/orientation", response_model=InfoResponse)
167256
async def add_orientation(
168257
name: str,

src/diffcalc_api/services/ub.py

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import numpy as np
66
from diffcalc.hkl.geometry import Position
77
from diffcalc.ub.calc import UBCalculation
8+
from diffcalc.ub.reference import Orientation, Reflection
89

910
from diffcalc_api.errors.ub import (
1011
InvalidIndexError,
@@ -48,6 +49,45 @@ async def get_ub_status(
4849
#######################################################################################
4950

5051

52+
async def get_reflection(
53+
name: str,
54+
store: HklCalcStore,
55+
collection: Optional[str],
56+
tag: Optional[str],
57+
idx: Optional[int],
58+
) -> Reflection:
59+
"""Get a reflection from the UBCalculation object.
60+
61+
Both tag and idx cannot be provided. One or the other must be provided.
62+
This is enforced in the route for this function, see diffcalc_api/routes/ub.py.
63+
An error is thrown if the reflection does not exist, i.e. cannot be retrieved.
64+
65+
Args:
66+
name: the name of the hkl object to access within the store
67+
store: accessor to the hkl object
68+
collection: collection within which the hkl object resides
69+
tag: optional tag to access the reflection
70+
idx: optional index to access the reflection
71+
72+
Returns:
73+
Reflection
74+
A reflection object, as defined in diffcalc_api.types.
75+
"""
76+
hklcalc = await store.load(name, collection)
77+
ubcalc: UBCalculation = hklcalc.ubcalc
78+
79+
retrieve: Union[int, str] = (
80+
tag if tag is not None else (idx if idx is not None else 0)
81+
)
82+
83+
try:
84+
reflection = ubcalc.get_reflection(retrieve)
85+
except (IndexError, ValueError):
86+
raise ReferenceRetrievalError(retrieve, "reflection")
87+
88+
return reflection
89+
90+
5191
async def add_reflection(
5292
name: str,
5393
params: AddReflectionParams,
@@ -167,6 +207,45 @@ async def delete_reflection(
167207
#######################################################################################
168208

169209

210+
async def get_orientation(
211+
name: str,
212+
store: HklCalcStore,
213+
collection: Optional[str],
214+
tag: Optional[str],
215+
idx: Optional[int],
216+
) -> Orientation:
217+
"""Get an orientation from the UBCalculation object.
218+
219+
Both tag and idx cannot be provided. One or the other must be provided.
220+
This is enforced in the route for this function, see diffcalc_api/routes/ub.py.
221+
An error is thrown if the orientation does not exist, i.e. cannot be retrieved.
222+
223+
Args:
224+
name: the name of the hkl object to access within the store
225+
store: accessor to the hkl object
226+
collection: collection within which the hkl object resides
227+
tag: optional tag to access the orientation
228+
idx: optional index to access the orientation
229+
230+
Returns:
231+
Orientation
232+
An orientation object, as defined in diffcalc_api.types.
233+
"""
234+
hklcalc = await store.load(name, collection)
235+
ubcalc: UBCalculation = hklcalc.ubcalc
236+
237+
retrieve: Union[int, str] = (
238+
tag if tag is not None else (idx if idx is not None else 0)
239+
)
240+
241+
try:
242+
orientation = ubcalc.get_orientation(retrieve)
243+
except (IndexError, ValueError):
244+
raise ReferenceRetrievalError(retrieve, "orientation")
245+
246+
return orientation
247+
248+
170249
async def add_orientation(
171250
name: str,
172251
params: AddOrientationParams,
@@ -226,7 +305,7 @@ async def edit_orientation(
226305
try:
227306
orientation = hklcalc.ubcalc.get_orientation(retrieve)
228307
except (IndexError, ValueError):
229-
raise ReferenceRetrievalError(retrieve, "reflection")
308+
raise ReferenceRetrievalError(retrieve, "orientation")
230309

231310
inputs = {
232311
"idx": retrieve,

src/diffcalc_api/types.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""Exposes types of diffcalc-core objects."""
2+
3+
from dataclasses import dataclass
4+
from typing import Optional
5+
6+
7+
@dataclass
8+
class Position:
9+
"""Diffractometer angles in degrees.
10+
11+
Similar to diffcalc.hkl.geometry.Position
12+
"""
13+
14+
mu: float
15+
delta: float
16+
nu: float
17+
eta: float
18+
chi: float
19+
phi: float
20+
21+
22+
@dataclass
23+
class Orientation:
24+
"""Reference orientation of the sample.
25+
26+
Similar to diffcalc.ub.reference.Orientation
27+
"""
28+
29+
h: float
30+
k: float
31+
l: float
32+
x: float
33+
y: float
34+
z: float
35+
pos: Position
36+
tag: Optional[str]
37+
38+
39+
@dataclass
40+
class Reflection:
41+
"""Reference reflection of the sample.
42+
43+
Similar to diffcalc.ub.reference.Reflection
44+
"""
45+
46+
h: float
47+
k: float
48+
l: float
49+
pos: Position
50+
energy: float
51+
tag: Optional[str]

0 commit comments

Comments
 (0)