diff --git a/dump.rdb b/dump.rdb deleted file mode 100644 index 167a55d..0000000 Binary files a/dump.rdb and /dev/null differ diff --git a/src/diffcalc_API/__init__.py b/src/diffcalc_API/__init__.py index ba95917..5b67131 100644 --- a/src/diffcalc_API/__init__.py +++ b/src/diffcalc_API/__init__.py @@ -1,4 +1,4 @@ -from . import config, errorDefinitions, fileHandling, server +from . import config, errorDefinitions, server from ._version_git import __version__ # __all__ defines the public API for the package. @@ -6,7 +6,6 @@ __all__ = [ "__version__", "server", - "fileHandling", "errorDefinitions", "config", ] diff --git a/src/diffcalc_API/fileHandling.py b/src/diffcalc_API/fileHandling.py deleted file mode 100644 index 284f9a3..0000000 --- a/src/diffcalc_API/fileHandling.py +++ /dev/null @@ -1,51 +0,0 @@ -import pickle -from pathlib import Path -from typing import Callable - -from diffcalc.hkl.calc import HklCalculation -from diffcalc.hkl.constraints import Constraints -from diffcalc.ub.calc import UBCalculation - -from diffcalc_API.config import savePicklesFolder -from diffcalc_API.errorDefinitions import attempting_to_overwrite, check_file_exists - - -def unpickleHkl(name: str) -> HklCalculation: - pickleFilePath = Path(savePicklesFolder) / name - check_file_exists(pickleFilePath, name) - - with open(pickleFilePath, "rb") as openedFile: - diffcalcObject: HklCalculation = pickle.load(openedFile) - - return diffcalcObject - - -def supplyPersist() -> Callable[[HklCalculation, str], Path]: - return pickleHkl - - -def pickleHkl(object: HklCalculation, pickleFileName: str) -> Path: - pickleFilePath = Path(savePicklesFolder) / pickleFileName - with open(pickleFilePath, "wb") as pickleFile: - pickle.dump(obj=object, file=pickleFile) - - return pickleFilePath - - -def createPickle(pickleFileName: str) -> Path: - attempting_to_overwrite(pickleFileName) - - UBcalc = UBCalculation(name=pickleFileName) - constraints = Constraints() - hkl = HklCalculation(UBcalc, constraints) - - pickleLocation = pickleHkl(hkl, pickleFileName) - return pickleLocation - - -def deletePickle(pickleFileName: str) -> Path: - pickleFilePath = Path(savePicklesFolder) / pickleFileName - check_file_exists(pickleFilePath, pickleFileName) - Path(pickleFilePath).unlink() - - return pickleFilePath diff --git a/src/diffcalc_API/routes/Constraints.py b/src/diffcalc_API/routes/Constraints.py index fa95477..c15593a 100644 --- a/src/diffcalc_API/routes/Constraints.py +++ b/src/diffcalc_API/routes/Constraints.py @@ -1,20 +1,19 @@ -from pathlib import Path -from typing import Callable, Dict, Union +from typing import Dict, Union -from diffcalc.hkl.calc import HklCalculation from fastapi import APIRouter, Body, Depends, Response -from diffcalc_API.fileHandling import supplyPersist, unpickleHkl from diffcalc_API.services import Constraints as service +from diffcalc_API.stores.pickling import get_store +from diffcalc_API.stores.protocol import HklCalcStore router = APIRouter(prefix="/constraints", tags=["constraints"]) @router.get("/{name}") -async def get_constraints_status( - name: str, hklCalc: HklCalculation = Depends(unpickleHkl) -): - return Response(content=str(hklCalc.constraints), media_type="application/text") +async def get_constraints(name: str, store: HklCalcStore = Depends(get_store)): + content = await service.get_constraints(name, store) + + return Response(content=content, media_type="application/text") @router.put("/{name}/set") @@ -23,10 +22,10 @@ async def set_constraints( constraintDict: Dict[str, Union[float, bool]] = Body( example={"qaz": 0, "alpha": 0, "eta": 0} ), - hklCalc: HklCalculation = Depends(unpickleHkl), - persist: Callable[[HklCalculation, str], Path] = Depends(supplyPersist), + store: HklCalcStore = Depends(get_store), ): - service.set_constraints(name, constraintDict, hklCalc, persist) + await service.set_constraints(name, constraintDict, store) + return {"message": f"constraints updated (replaced) for crystal {name}"} @@ -34,17 +33,11 @@ async def set_constraints( async def remove_constraint( name: str, property: str, - hklCalc: HklCalculation = Depends(unpickleHkl), - persist: Callable[[HklCalculation, str], Path] = Depends(supplyPersist), + store: HklCalcStore = Depends(get_store), ): - service.remove_constraint(name, property, hklCalc, persist) + await service.remove_constraint(name, property, store) - return { - "message": ( - f"unconstrained {property} for crystal {name}. " - f"Constraints are now: {hklCalc.constraints.asdict}" - ) - } + return {"message": f"unconstrained {property} for crystal {name}. "} @router.patch("/{name}/constrain/{property}") @@ -52,14 +45,8 @@ async def set_constraint( name: str, property: str, value: Union[float, bool] = Body(...), - hklCalc: HklCalculation = Depends(unpickleHkl), - persist: Callable[[HklCalculation, str], Path] = Depends(supplyPersist), + store: HklCalcStore = Depends(get_store), ): - service.set_constraint(name, property, value, hklCalc, persist) + await service.set_constraint(name, property, value, store) - return { - "message": ( - f"constrained {property} for crystal {name}. " - f"Constraints are now: {hklCalc.constraints.asdict}" - ) - } + return {"message": f"constrained {property} for crystal {name}. "} diff --git a/src/diffcalc_API/routes/HklCalculation.py b/src/diffcalc_API/routes/HklCalculation.py index 2162f0b..b22d0ce 100644 --- a/src/diffcalc_API/routes/HklCalculation.py +++ b/src/diffcalc_API/routes/HklCalculation.py @@ -1,16 +1,12 @@ -from pathlib import Path -from typing import Callable, Optional, Tuple, Union +from typing import Optional, Tuple, Union -import numpy as np -from diffcalc.hkl.calc import HklCalculation from fastapi import APIRouter, Depends, Query, Response -from diffcalc_API.fileHandling import supplyPersist, unpickleHkl from diffcalc_API.services import HklCalculation as service +from diffcalc_API.stores.pickling import get_store +from diffcalc_API.stores.protocol import HklCalcStore -router = APIRouter( - prefix="/calculate", tags=["hkl"], dependencies=[Depends(unpickleHkl)] -) +router = APIRouter(prefix="/calculate", tags=["hkl"]) singleConstraintType = Union[Tuple[str, float], str] @@ -22,13 +18,10 @@ async def calculate_UB( name: str, firstTag: Optional[Union[int, str]] = Query(default=None, example="refl1"), secondTag: Optional[Union[int, str]] = Query(default=None, example="plane"), - hklCalc: HklCalculation = Depends(unpickleHkl), - persist: Callable[[HklCalculation, str], Path] = Depends(supplyPersist), + store: HklCalcStore = Depends(get_store), ): - service.calculate_UB(name, firstTag, secondTag, hklCalc, persist) - return Response( - content=str(np.round(hklCalc.ubcalc.UB, 6)), media_type="application/text" - ) + content = await service.calculate_UB(name, firstTag, secondTag, store) + return Response(content=content, media_type="application/text") @router.get("/{name}/position/lab") @@ -36,10 +29,10 @@ async def lab_position_from_miller_indices( name: str, millerIndices: Tuple[float, float, float] = Query(example=[0, 0, 1]), wavelength: float = Query(..., example=1.0), - hklCalc: HklCalculation = Depends(unpickleHkl), + store: HklCalcStore = Depends(get_store), ): - positions = service.lab_position_from_miller_indices( - millerIndices, wavelength, hklCalc + positions = await service.lab_position_from_miller_indices( + name, millerIndices, wavelength, store ) return {"payload": positions} @@ -52,9 +45,9 @@ async def miller_indices_from_lab_position( ..., example=[7.31, 0, 10.62, 0, 0, 0] ), wavelength: float = Query(..., example=1.0), - hklCalc: HklCalculation = Depends(unpickleHkl), + store: HklCalcStore = Depends(get_store), ): - hkl = service.miller_indices_from_lab_position(pos, wavelength, hklCalc) + hkl = await service.miller_indices_from_lab_position(name, pos, wavelength, store) return {"payload": hkl} @@ -65,9 +58,9 @@ async def scan_hkl( stop: positionType = Query(..., example=(2, 0, 2)), inc: positionType = Query(..., example=(0.1, 0, 0.1)), wavelength: float = Query(..., example=1), - hklCalc: HklCalculation = Depends(unpickleHkl), + store: HklCalcStore = Depends(get_store), ): - scanResults = service.scan_hkl(start, stop, inc, wavelength, hklCalc) + scanResults = await service.scan_hkl(name, start, stop, inc, wavelength, store) return {"payload": scanResults} @@ -78,9 +71,9 @@ async def scan_wavelength( stop: float = Query(..., example=2.0), inc: float = Query(..., example=0.2), hkl: positionType = Query(..., example=(1, 0, 1)), - hklCalc: HklCalculation = Depends(unpickleHkl), + store: HklCalcStore = Depends(get_store), ): - scanResults = service.scan_wavelength(start, stop, inc, hkl, hklCalc) + scanResults = await service.scan_wavelength(name, start, stop, inc, hkl, store) return {"payload": scanResults} @@ -93,10 +86,10 @@ async def scan_constraint( inc: float = Query(..., example=1), hkl: positionType = Query(..., example=(1, 0, 1)), wavelength: float = Query(..., example=1.0), - hklCalc: HklCalculation = Depends(unpickleHkl), + store: HklCalcStore = Depends(get_store), ): - scanResults = service.scan_constraint( - constraint, start, stop, inc, hkl, wavelength, hklCalc + scanResults = await service.scan_constraint( + name, constraint, start, stop, inc, hkl, wavelength, store ) return {"payload": scanResults} diff --git a/src/diffcalc_API/routes/UBCalculation.py b/src/diffcalc_API/routes/UBCalculation.py index 765a0e8..0193dea 100644 --- a/src/diffcalc_API/routes/UBCalculation.py +++ b/src/diffcalc_API/routes/UBCalculation.py @@ -1,7 +1,5 @@ -from pathlib import Path -from typing import Callable, Tuple +from typing import Tuple -from diffcalc.hkl.calc import HklCalculation from fastapi import APIRouter, Body, Depends, Response from diffcalc_API.errors.UBCalculation import ( @@ -9,7 +7,6 @@ check_property_is_valid, ) from diffcalc_API.examples import UBCalculation as examples -from diffcalc_API.fileHandling import supplyPersist, unpickleHkl from diffcalc_API.models.UBCalculation import ( addOrientationParams, addReflectionParams, @@ -19,27 +16,25 @@ setLatticeParams, ) from diffcalc_API.services import UBCalculation as service +from diffcalc_API.stores.pickling import get_store +from diffcalc_API.stores.protocol import HklCalcStore -router = APIRouter( - prefix="/ub", - tags=["ub"], - dependencies=[Depends(unpickleHkl), Depends(supplyPersist)], -) +router = APIRouter(prefix="/ub", tags=["ub"]) @router.get("/{name}") -async def get_UB_status(name: str, hklCalc: HklCalculation = Depends(unpickleHkl)): - return Response(content=str(hklCalc.ubcalc), media_type="application/text") +async def get_UB(name: str, store: HklCalcStore = Depends(get_store)): + content = await service.get_UB(name, store) + return Response(content=content, media_type="application/text") @router.put("/{name}/reflection") async def add_reflection( name: str, params: addReflectionParams = Body(..., example=examples.addReflection), - hklCalc: HklCalculation = Depends(unpickleHkl), - persist: Callable[[HklCalculation, str], Path] = Depends(supplyPersist), + store: HklCalcStore = Depends(get_store), ): - service.add_reflection(name, params, hklCalc, persist) + await service.add_reflection(name, params, store) return {"message": f"added reflection for UB Calculation of crystal {name}"} @@ -47,26 +42,19 @@ async def add_reflection( async def edit_reflection( name: str, params: editReflectionParams = Body(..., example=examples.editReflection), - hklCalc: HklCalculation = Depends(unpickleHkl), - persist: Callable[[HklCalculation, str], Path] = Depends(supplyPersist), + store: HklCalcStore = Depends(get_store), ): - service.edit_reflection(name, params, hklCalc, persist) - return { - "message": ( - f"reflection with tag/index {params.tagOrIdx} edited to: " - f"{hklCalc.ubcalc.get_reflection(params.tagOrIdx)}." - ) - } + await service.edit_reflection(name, params, store) + return {"message": f"reflection with tag/index {params.tagOrIdx} edited. "} @router.delete("/{name}/reflection") async def delete_reflection( name: str, params: deleteParams = Body(..., example={"tagOrIdx": "refl1"}), - hklCalc: HklCalculation = Depends(unpickleHkl), - persist: Callable[[HklCalculation, str], Path] = Depends(supplyPersist), + store: HklCalcStore = Depends(get_store), ): - service.delete_reflection(name, params.tagOrIdx, hklCalc, persist) + await service.delete_reflection(name, params.tagOrIdx, store) # TODO Change this! return {"message": f"reflection with tag/index {params.tagOrIdx} deleted."} @@ -74,10 +62,9 @@ async def delete_reflection( async def add_orientation( name: str, params: addOrientationParams = Body(..., example=examples.addOrientation), - hklCalc: HklCalculation = Depends(unpickleHkl), - persist: Callable[[HklCalculation, str], Path] = Depends(supplyPersist), + store: HklCalcStore = Depends(get_store), ): - service.add_orientation(name, params, hklCalc, persist) + await service.add_orientation(name, params, store) return {"message": f"added orientation for UB Calculation of crystal {name}"} @@ -85,26 +72,19 @@ async def add_orientation( async def edit_orientation( name: str, params: editOrientationParams = Body(..., example=examples.editOrientation), - hklCalc: HklCalculation = Depends(unpickleHkl), - persist: Callable[[HklCalculation, str], Path] = Depends(supplyPersist), + store: HklCalcStore = Depends(get_store), ): - service.edit_orientation(name, params, hklCalc, persist) - return { - "message": ( - f"orientation with tag/index {params.tagOrIdx} edited to: " - f"{hklCalc.ubcalc.get_orientation(params.tagOrIdx)}." - ) - } + await service.edit_orientation(name, params, store) + return {"message": f"orientation with tag/index {params.tagOrIdx} edited."} @router.delete("/{name}/orientation") async def delete_orientation( name: str, params: deleteParams = Body(..., example={"tagOrIdx": "plane"}), - hklCalc: HklCalculation = Depends(unpickleHkl), - persist: Callable[[HklCalculation, str], Path] = Depends(supplyPersist), + store: HklCalcStore = Depends(get_store), ): - service.delete_orientation(name, params.tagOrIdx, hklCalc, persist) + await service.delete_orientation(name, params.tagOrIdx, store) return {"message": f"reflection with tag or index {params.tagOrIdx} deleted."} @@ -112,11 +92,10 @@ async def delete_orientation( async def set_lattice( name: str, params: setLatticeParams = Body(example=examples.setLattice), - hklCalc: HklCalculation = Depends(unpickleHkl), - persist: Callable[[HklCalculation, str], Path] = Depends(supplyPersist), + store: HklCalcStore = Depends(get_store), _=Depends(check_params_not_empty), ): - service.set_lattice(name, params, hklCalc, persist) + await service.set_lattice(name, params, store) return {"message": f"lattice has been set for UB calculation of crystal {name}"} @@ -125,9 +104,8 @@ async def modify_property( name: str, property: str, targetValue: Tuple[float, float, float] = Body(..., example=[1, 0, 0]), - hklCalc: HklCalculation = Depends(unpickleHkl), - persist: Callable[[HklCalculation, str], Path] = Depends(supplyPersist), + store: HklCalcStore = Depends(get_store), _=Depends(check_property_is_valid), ): - service.modify_property(name, property, targetValue, hklCalc, persist) + await service.modify_property(name, property, targetValue, store) return {"message": f"{property} has been set for UB calculation of crystal {name}"} diff --git a/src/diffcalc_API/server.py b/src/diffcalc_API/server.py index b38ba0a..16c9636 100644 --- a/src/diffcalc_API/server.py +++ b/src/diffcalc_API/server.py @@ -1,11 +1,11 @@ from diffcalc.util import DiffcalcException -from fastapi import FastAPI, Request, responses +from fastapi import Depends, FastAPI, Request, responses from diffcalc_API import errorDefinitions from diffcalc_API.errors.Constraints import responses as responsesConstraints from diffcalc_API.errors.HklCalculation import responses as responsesHkl from diffcalc_API.errors.UBCalculation import responses as responsesUb -from diffcalc_API.fileHandling import createPickle, deletePickle +from diffcalc_API.stores.pickling import get_store from . import routes @@ -57,14 +57,14 @@ async def server_exceptions_middleware(request: Request, call_next): @app.post("/{name}") -async def create_hkl_object(name: str): - pickleLocation = createPickle(name) +async def create_hkl_object(name: str, repo=Depends(get_store)): + await repo.create(name) - return {"message": f"file created at {pickleLocation}"} + return {"message": f"file for crystal {name} created"} @app.delete("/{name}") -async def delete_hkl_object(name: str): - pickleLocation = deletePickle(name) +async def delete_hkl_object(name: str, repo=Depends(get_store)): + await repo.delete(name) - return {"message": f"file at location {pickleLocation} deleted"} + return {"message": f"file for crystal {name} deleted"} diff --git a/src/diffcalc_API/services/Constraints.py b/src/diffcalc_API/services/Constraints.py index eb9d167..1a6e8ce 100644 --- a/src/diffcalc_API/services/Constraints.py +++ b/src/diffcalc_API/services/Constraints.py @@ -1,52 +1,58 @@ -from pathlib import Path -from typing import Callable, Dict, Union +from typing import Dict, Union -from diffcalc.hkl.calc import HklCalculation from diffcalc.hkl.constraints import Constraints from diffcalc_API.config import constraintsWithNoValue from diffcalc_API.errors.Constraints import check_constraint_exists +from diffcalc_API.stores.protocol import HklCalcStore -def set_constraints( +async def get_constraints(name: str, store: HklCalcStore) -> str: + hklCalc = await store.load(name) + return str(hklCalc.constraints) + + +async def set_constraints( name: str, constraintDict: Dict[str, Union[float, bool]], - hklCalc: HklCalculation, - persist: Callable[[HklCalculation, str], Path], -): + store: HklCalcStore, +) -> None: + hklCalc = await store.load(name) + booleanConstraints = set(constraintDict.keys()).intersection(constraintsWithNoValue) for constraint in booleanConstraints: constraintDict[constraint] = bool(constraintDict[constraint]) hklCalc.constraints = Constraints(constraintDict) - persist(hklCalc, name) - return + + await store.save(name, hklCalc) -def remove_constraint( +async def remove_constraint( name: str, property: str, - hklCalc: HklCalculation, - persist: Callable[[HklCalculation, str], Path], -): + store: HklCalcStore, +) -> None: + hklCalc = await store.load(name) + check_constraint_exists(property) setattr(hklCalc.constraints, property, None) - persist(hklCalc, name) - return + + await store.save(name, hklCalc) -def set_constraint( +async def set_constraint( name: str, property: str, value: Union[float, bool], - hklCalc: HklCalculation, - persist: Callable[[HklCalculation, str], Path], + store: HklCalcStore, ): - check_constraint_exists(property) + hklCalc = await store.load(name) + check_constraint_exists(property) if property in constraintsWithNoValue: value = bool(value) setattr(hklCalc.constraints, property, value) - persist(hklCalc, name) - return + + await store.save(name, hklCalc) diff --git a/src/diffcalc_API/services/HklCalculation.py b/src/diffcalc_API/services/HklCalculation.py index 37ac648..48f2df3 100644 --- a/src/diffcalc_API/services/HklCalculation.py +++ b/src/diffcalc_API/services/HklCalculation.py @@ -1,9 +1,7 @@ from itertools import product -from pathlib import Path -from typing import Callable, Dict, List, Optional, Tuple, Union +from typing import Dict, List, Optional, Tuple, Union import numpy as np -from diffcalc.hkl.calc import HklCalculation from diffcalc.hkl.geometry import Position from diffcalc_API.errors.HklCalculation import ( @@ -11,37 +9,45 @@ check_valid_miller_indices, check_valid_scan_bounds, ) +from diffcalc_API.stores.protocol import HklCalcStore PositionType = Tuple[float, float, float] -def lab_position_from_miller_indices( +async def lab_position_from_miller_indices( + name: str, millerIndices: Tuple[float, float, float], wavelength: float, - hklCalc: HklCalculation, + store: HklCalcStore, ) -> List[Tuple[Position, Dict[str, float]]]: + hklCalc = await store.load(name) + check_valid_miller_indices(millerIndices) allPositions = hklCalc.get_position(*millerIndices, wavelength) return combine_lab_position_results(allPositions) -def miller_indices_from_lab_position( +async def miller_indices_from_lab_position( + name: str, pos: Tuple[float, float, float, float, float, float], wavelength: float, - hklCalc: HklCalculation, + store: HklCalcStore, ): + hklCalc = await store.load(name) hklPosition = hklCalc.get_hkl(Position(*pos), wavelength) return tuple(np.round(hklPosition, 16)) -def scan_hkl( +async def scan_hkl( + name: str, start: PositionType, stop: PositionType, inc: PositionType, wavelength: float, - hklCalc: HklCalculation, + store: HklCalcStore, ): + hklCalc = await store.load(name) valueOfAxes = [ generate_axis(start[i], stop[i], inc[i]) if inc[i] != 0 else [0] for i in range(3) @@ -57,13 +63,15 @@ def scan_hkl( return results -def scan_wavelength( +async def scan_wavelength( + name: str, start: float, stop: float, inc: float, hkl: PositionType, - hklCalc: HklCalculation, + store: HklCalcStore, ): + hklCalc = await store.load(name) check_valid_scan_bounds(start, stop, inc) wavelengths = np.arange(start, stop + inc, inc) result = {} @@ -75,15 +83,17 @@ def scan_wavelength( return result -def scan_constraint( +async def scan_constraint( + name: str, constraint: str, start: float, stop: float, inc: float, hkl: PositionType, wavelength: float, - hklCalc: HklCalculation, + store: HklCalcStore, ): + hklCalc = await store.load(name) check_valid_scan_bounds(start, stop, inc) result = {} for value in np.arange(start, stop + inc, inc): @@ -108,12 +118,15 @@ def combine_lab_position_results(positions: List[Tuple[Position, Dict[str, float return result -def calculate_UB( +async def calculate_UB( name: str, firstTag: Optional[Union[int, str]], secondTag: Optional[Union[int, str]], - hklCalc: HklCalculation, - persist: Callable[[HklCalculation, str], Path], -): + store: HklCalcStore, +) -> str: + hklCalc = await store.load(name) + calculate_UB_matrix(hklCalc, firstTag, secondTag) - persist(hklCalc, name) + + await store.save(name, hklCalc) + return str(np.round(hklCalc.ubcalc.UB, 6)) diff --git a/src/diffcalc_API/services/UBCalculation.py b/src/diffcalc_API/services/UBCalculation.py index f0ca2ca..691200e 100644 --- a/src/diffcalc_API/services/UBCalculation.py +++ b/src/diffcalc_API/services/UBCalculation.py @@ -1,7 +1,5 @@ -from pathlib import Path -from typing import Callable, Tuple, Union +from typing import Tuple, Union -from diffcalc.hkl.calc import HklCalculation from diffcalc.hkl.geometry import Position from diffcalc_API.errors.UBCalculation import get_orientation, get_reflection @@ -12,14 +10,22 @@ editReflectionParams, setLatticeParams, ) +from diffcalc_API.stores.protocol import HklCalcStore -def add_reflection( +async def get_UB(name: str, store: HklCalcStore) -> str: + hklCalc = await store.load(name) + + return str(hklCalc.ubcalc) + + +async def add_reflection( name: str, params: addReflectionParams, - hklCalc: HklCalculation, - persist: Callable[[HklCalculation, str], Path], -): + store: HklCalcStore, +) -> None: + hklCalc = await store.load(name) + hklCalc.ubcalc.add_reflection( params.hkl, Position(*params.position), @@ -27,18 +33,17 @@ def add_reflection( params.tag, ) - persist(hklCalc, name) - return + await store.save(name, hklCalc) -def edit_reflection( +async def edit_reflection( name: str, params: editReflectionParams, - hklCalc: HklCalculation, - persist: Callable[[HklCalculation, str], Path], -): - reflection = get_reflection(hklCalc, params.tagOrIdx) + store: HklCalcStore, +) -> None: + hklCalc = await store.load(name) + reflection = get_reflection(hklCalc, params.tagOrIdx) hklCalc.ubcalc.edit_reflection( params.tagOrIdx, params.hkl if params.hkl else (reflection.h, reflection.k, reflection.l), @@ -46,30 +51,31 @@ def edit_reflection( params.energy if params.energy else reflection.energy, params.tagOrIdx if isinstance(params.tagOrIdx, str) else None, ) - persist(hklCalc, name) - return + + await store.save(name, hklCalc) -def delete_reflection( +async def delete_reflection( name: str, tagOrIdx: Union[str, int], - hklCalc: HklCalculation, - persist: Callable[[HklCalculation, str], Path], -): + store: HklCalcStore, +) -> None: + hklCalc = await store.load(name) + _ = get_reflection(hklCalc, tagOrIdx) hklCalc.ubcalc.del_reflection(tagOrIdx) - persist(hklCalc, name) - return + await store.save(name, hklCalc) -def add_orientation( + +async def add_orientation( name: str, params: addOrientationParams, - hklCalc: HklCalculation, - persist: Callable[[HklCalculation, str], Path], -): - position = Position(*params.position) if params.position else None + store: HklCalcStore, +) -> None: + hklCalc = await store.load(name) + position = Position(*params.position) if params.position else None hklCalc.ubcalc.add_orientation( params.hkl, params.xyz, @@ -77,18 +83,17 @@ def add_orientation( params.tag, ) - persist(hklCalc, name) - return + await store.save(name, hklCalc) -def edit_orientation( +async def edit_orientation( name: str, params: editOrientationParams, - hklCalc: HklCalculation, - persist: Callable[[HklCalculation, str], Path], -): - orientation = get_orientation(hklCalc, params.tagOrIdx) + store: HklCalcStore, +) -> None: + hklCalc = await store.load(name) + orientation = get_orientation(hklCalc, params.tagOrIdx) hklCalc.ubcalc.edit_orientation( params.tagOrIdx, params.hkl if params.hkl else (orientation.h, orientation.k, orientation.l), @@ -96,37 +101,39 @@ def edit_orientation( Position(params.position) if params.position else orientation.pos, params.tagOrIdx if isinstance(params.tagOrIdx, str) else None, ) - persist(hklCalc, name) - return + await store.save(name, hklCalc) -def delete_orientation( + +async def delete_orientation( name: str, tagOrIdx: Union[str, int], - hklCalc: HklCalculation, - persist: Callable[[HklCalculation, str], Path], -): + store: HklCalcStore, +) -> None: + hklCalc = await store.load(name) + _ = get_orientation(hklCalc, tagOrIdx) hklCalc.ubcalc.del_orientation(tagOrIdx) - persist(hklCalc, name) + await store.save(name, hklCalc) + + +async def set_lattice(name: str, params: setLatticeParams, store: HklCalcStore) -> None: + hklCalc = await store.load(name) -def set_lattice( - name: str, - params: setLatticeParams, - hklCalc: HklCalculation, - persist: Callable[[HklCalculation, str], Path], -): hklCalc.ubcalc.set_lattice(name=name, **params.dict()) - persist(hklCalc, name) + await store.save(name, hklCalc) -def modify_property( + +async def modify_property( name: str, property: str, targetValue: Tuple[float, float, float], - hklCalc: HklCalculation, - persist: Callable[[HklCalculation, str], Path], + store: HklCalcStore, ): + hklCalc = await store.load(name) + setattr(hklCalc.ubcalc, property, targetValue) - persist(hklCalc, name) + + await store.save(name, hklCalc) diff --git a/src/diffcalc_API/stores/__init__.py b/src/diffcalc_API/stores/__init__.py new file mode 100644 index 0000000..b2ba863 --- /dev/null +++ b/src/diffcalc_API/stores/__init__.py @@ -0,0 +1,3 @@ +from . import pickling, protocol + +__all__ = ["pickling", "protocol"] diff --git a/src/diffcalc_API/stores/pickling.py b/src/diffcalc_API/stores/pickling.py new file mode 100644 index 0000000..307ead6 --- /dev/null +++ b/src/diffcalc_API/stores/pickling.py @@ -0,0 +1,49 @@ +import pickle +from pathlib import Path + +from diffcalc.hkl.calc import HklCalculation +from diffcalc.hkl.constraints import Constraints +from diffcalc.ub.calc import UBCalculation + +from diffcalc_API.config import savePicklesFolder +from diffcalc_API.errorDefinitions import attempting_to_overwrite, check_file_exists +from diffcalc_API.stores.protocol import HklCalcStore + + +class PicklingHklCalcStore: + _root_directory: Path + + def __init__(self, root_directory: Path) -> None: + self._root_directory = root_directory + + async def create(self, name: str) -> None: + attempting_to_overwrite(name) + + UBcalc = UBCalculation(name=name) + constraints = Constraints() + hkl = HklCalculation(UBcalc, constraints) + + await self.save(name, hkl) + + async def delete(self, name: str) -> None: + pickleFilePath = Path(savePicklesFolder) / name + check_file_exists(pickleFilePath, name) + Path(pickleFilePath).unlink() + + async def save(self, name: str, calc: HklCalculation) -> None: + file_path = self._root_directory / name + with open(file_path, "wb") as stream: + pickle.dump(obj=calc, file=stream) + + async def load(self, name: str) -> HklCalculation: + file_path = self._root_directory / name + check_file_exists(file_path, name) + + with open(file_path, "rb") as openedFile: + diffcalcObject: HklCalculation = pickle.load(openedFile) + + return diffcalcObject + + +def get_store() -> HklCalcStore: + return PicklingHklCalcStore(Path(savePicklesFolder)) diff --git a/src/diffcalc_API/stores/protocol.py b/src/diffcalc_API/stores/protocol.py new file mode 100644 index 0000000..77f8c00 --- /dev/null +++ b/src/diffcalc_API/stores/protocol.py @@ -0,0 +1,21 @@ +from typing import Protocol + +from diffcalc.hkl.calc import HklCalculation + + +class HklCalcStore(Protocol): + """ + Protocol, or interface, for interacting with the Hkl object. + """ + + async def create(self, name: str) -> None: + ... + + async def delete(self, name: str) -> None: + ... + + async def save(self, name: str, calc: HklCalculation) -> None: + ... + + async def load(self, name: str) -> HklCalculation: + ... diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..5477431 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,3 @@ +from . import conftest, test_constraints, test_hklcalc, test_ubcalc + +__all__ = ["conftest", "test_constraints", "test_hklcalc", "test_ubcalc"] diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..ecf354b --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,18 @@ +from diffcalc.hkl.calc import HklCalculation + + +class FakeHklCalcStore: + def __init__(self, useHkl: HklCalculation): + self.hkl = useHkl + + async def create(self, name: str) -> None: + pass + + async def delete(self, name: str) -> None: + pass + + async def save(self, name: str, calc: HklCalculation) -> None: + pass + + async def load(self, name: str) -> HklCalculation: + return self.hkl diff --git a/tests/test_constraints.py b/tests/test_constraints.py index 6e3c57b..f1e87f5 100644 --- a/tests/test_constraints.py +++ b/tests/test_constraints.py @@ -1,5 +1,4 @@ import ast -from pathlib import Path import pytest from diffcalc.hkl.calc import HklCalculation @@ -8,24 +7,21 @@ from fastapi.testclient import TestClient from diffcalc_API.errors.Constraints import codes -from diffcalc_API.fileHandling import supplyPersist, unpickleHkl from diffcalc_API.server import app +from diffcalc_API.stores.pickling import get_store +from diffcalc_API.stores.protocol import HklCalcStore +from tests.conftest import FakeHklCalcStore dummyHkl = HklCalculation(UBCalculation(name="dummy"), Constraints()) -def dummy_unpickleHkl(name: str) -> HklCalculation: - return dummyHkl +def dummy_get_store() -> HklCalcStore: + return FakeHklCalcStore(dummyHkl) -def dummy_pickleHkl(object: HklCalculation, pickleFileName: str) -> Path: - return Path("/does/not/exist") - - -@pytest.fixture +@pytest.fixture(scope="session") def client() -> TestClient: - app.dependency_overrides[unpickleHkl] = dummy_unpickleHkl - app.dependency_overrides[supplyPersist] = lambda: dummy_pickleHkl + app.dependency_overrides[get_store] = dummy_get_store return TestClient(app) diff --git a/tests/test_hklcalc.py b/tests/test_hklcalc.py index 7448174..140214e 100644 --- a/tests/test_hklcalc.py +++ b/tests/test_hklcalc.py @@ -7,8 +7,10 @@ from fastapi.testclient import TestClient from diffcalc_API.errors.HklCalculation import codes -from diffcalc_API.fileHandling import unpickleHkl from diffcalc_API.server import app +from diffcalc_API.stores.pickling import get_store +from diffcalc_API.stores.protocol import HklCalcStore +from tests.conftest import FakeHklCalcStore dummyHkl = HklCalculation(UBCalculation(name="sixcircle"), Constraints()) @@ -23,13 +25,14 @@ dummyHkl.constraints = Constraints({"qaz": 0, "alpha": 0, "eta": 0}) -def dummy_unpickleHkl(name: str) -> HklCalculation: - return dummyHkl +def dummy_get_store() -> HklCalcStore: + return FakeHklCalcStore(dummyHkl) @pytest.fixture def client() -> TestClient: - app.dependency_overrides[unpickleHkl] = dummy_unpickleHkl + app.dependency_overrides[get_store] = dummy_get_store + return TestClient(app) diff --git a/tests/test_ubcalc.py b/tests/test_ubcalc.py index aff4b7d..b905f2a 100644 --- a/tests/test_ubcalc.py +++ b/tests/test_ubcalc.py @@ -1,5 +1,3 @@ -from pathlib import Path - import numpy as np import pytest from diffcalc.hkl.calc import HklCalculation @@ -9,24 +7,21 @@ from fastapi.testclient import TestClient from diffcalc_API.errors.UBCalculation import codes -from diffcalc_API.fileHandling import supplyPersist, unpickleHkl from diffcalc_API.server import app +from diffcalc_API.stores.pickling import get_store +from diffcalc_API.stores.protocol import HklCalcStore +from tests.conftest import FakeHklCalcStore dummyHkl = HklCalculation(UBCalculation(name="dummy"), Constraints()) -def dummy_unpickleHkl(name: str) -> HklCalculation: - return dummyHkl - - -def dummy_pickleHkl(object: HklCalculation, pickleFileName: str) -> Path: - return Path("/does/not/exist") +def dummy_get_store() -> HklCalcStore: + return FakeHklCalcStore(dummyHkl) -@pytest.fixture +@pytest.fixture(scope="session") def client() -> TestClient: - app.dependency_overrides[unpickleHkl] = dummy_unpickleHkl - app.dependency_overrides[supplyPersist] = lambda: dummy_pickleHkl + app.dependency_overrides[get_store] = dummy_get_store return TestClient(app)