first attempt to refactor the test code into something more elegant
This commit is contained in:
139
src/classes.py
139
src/classes.py
@@ -1,14 +1,137 @@
|
||||
class Header:
|
||||
import os, json, requests
|
||||
from APIHandler import APIHandler
|
||||
|
||||
class Layer:
|
||||
'''
|
||||
Class to standardize the format of the headers of our http requests.
|
||||
Layer(layer_data) - where layer_data is a Python dictionary.
|
||||
|
||||
Meant to be used for eLabFTW Experiments of the "PLD Deposition" category.
|
||||
|
||||
eLabFTW experiments contain most of the data required by the NeXus file - although every layer is on a different eLab entry;
|
||||
unfortunately, some data like the target's chemical formula must be retrieved through additional HTTP requests.
|
||||
Attributes 'target_elabid', 'rheed_system_elabid' and 'laser_system_elabid' contain elabid's for these resources, which are all items.
|
||||
'''
|
||||
def __init__(self, apikey=""):
|
||||
'''Init method, apikey suggested but not required (empty by default).'''
|
||||
self.auth = {"Authorization" : apikey}
|
||||
self.content = {"Content-Type" : "application/json"}
|
||||
self.dump = {**self.auth, **self.content}
|
||||
def __init__(self, layer_data):
|
||||
try:
|
||||
self.extra = layer_data["metadata_decoded"]["extra_fields"]
|
||||
self.target_elabid = self.extra["Target"]["value"] # elabid
|
||||
self.rheed_system_elabid = self.extra["RHEED System"]["value"] # elabid
|
||||
self.laser_system_elabid = self.extra["Laser System"]["value"] # elabid
|
||||
self.start_time = layer_data.get("created_at")
|
||||
self.operator = layer_data.get("fullname")
|
||||
self.description = layer_data.get("body")
|
||||
self.deposition_time = self.extra["Duration"]["value"]
|
||||
self.repetition_rate = self.extra["Repetition rate"]["value"]
|
||||
try:
|
||||
self.number_of_pulses = (float(self.deposition_time) * float(self.repetition_rate)).__floor__()
|
||||
except ValueError:
|
||||
# Since number_of_pulses is required, if it can't be calculated raise error:
|
||||
raise ValueError("""
|
||||
Fatal: either Duration or Repetition Rate are empty or invalid.
|
||||
This has to be an error, since these fields are required by the NeXus standard.
|
||||
Please edit your eLabFTW entry and retry.
|
||||
""")
|
||||
self.temperature = self.extra["Heater temperature"]["value"] # Note: this field used to have a trailing space in its name
|
||||
self.process_pressure = self.extra["Process pressure"]["value"] # Note: this field used to have a trailing space in its name
|
||||
self.heating_method = self.extra["Heating Method"]["value"]
|
||||
self.layer_thickness = self.extra["Thickness"]["value"]
|
||||
self.buffer_gas = self.extra["Buffer gas"]["value"]
|
||||
self.heater_target_distance = self.extra["Heater-target distance"]["value"]
|
||||
self.laser_fluence = self.extra["Laser Intensity"]["value"] # here fluence = intensity
|
||||
self.laser_spot_area = self.extra["Spot Area"]["value"]
|
||||
try:
|
||||
self.laser_energy = (float(self.laser_fluence) * float(self.laser_spot_area)).__round__(3)
|
||||
except ValueError:
|
||||
# Since laser_energy is NOT required, if it can't be calculated warn user but allow the software to continue execution:
|
||||
print("""
|
||||
Warning: either Laser Intensity or Spot Area are empty or invalid.
|
||||
If you think this is an error, please edit your eLabFTW entry and retry.
|
||||
Setting Laser Energy to NoneType.
|
||||
""")
|
||||
# Placeholder
|
||||
self.laser_energy = None
|
||||
# Laser rasternig section
|
||||
self.laser_rastering_geometry = self.extra["Laser Rastering Geometry"]["value"]
|
||||
self.laser_rastering_positions = self.extra["Laser Rastering Position"]["value"]
|
||||
self.laser_rastering_velocities = self.extra["Laser Rastering Speed"]["value"]
|
||||
# Pre annealing section
|
||||
self.pre_annealing_ambient_gas = self.extra["Buffer gas Pre"]["value"]
|
||||
self.pre_annealing_pressure = self.extra["Process pressure Pre"]["value"]
|
||||
self.pre_annealing_temperature = self.extra["Heater temperature Pre"]["value"]
|
||||
self.pre_annealing_duration = self.extra["Duration Pre"]["value"]
|
||||
# Post annealing section
|
||||
self.post_annealing_ambient_gas = self.extra["Buffer gas PA"]["value"]
|
||||
self.post_annealing_pressure = self.extra["Process pressure PA"]["value"]
|
||||
self.post_annealing_temperature = self.extra["Heater temperature PA"]["value"]
|
||||
self.post_annealing_duration = self.extra["Duration PA"]["value"]
|
||||
# Rejected but suggested by the NeXus standard:
|
||||
#self.laser_rastering_coefficients = None
|
||||
except KeyError as k:
|
||||
# Some keys are not required and can be called through the .get() method - which is permissive and allows null values;
|
||||
# Other keys are required so if they can't be called (invalid or null) raise error and stop execution of the program:
|
||||
raise KeyError(f"The provided dictionary lacks a \"{k}\" key. Check the deposition layer entry on eLabFTW and make sure you used the correct Experiment template.")
|
||||
|
||||
class Entrypoint:
|
||||
'''
|
||||
Entrypoint(sample_data) - where sample_data is a Python dictionary.
|
||||
|
||||
Meant to be used for eLabFTW Resources of the "Sample" category.
|
||||
|
||||
The entrypoint is the starting point of the process of resolving the data chain.
|
||||
The entrypoint must be a dictionary containing the data of a sample, created directly from the JSON of the item endpoint on eLabFTW - which can be done through the function get_entry_from_elabid.
|
||||
'''
|
||||
def __init__(self, sample_data):
|
||||
try:
|
||||
self.extra = sample_data["metadata_decoded"]["extra_fields"]
|
||||
self.linked_items = sample_data["items_links"]
|
||||
self.batch_elabid = self.extra["Substrate batch"]["value"]
|
||||
self.linked_experiments = sample_data["related_experiments_links"]
|
||||
self.linked_experiments_elabid = [ i["entityid"] for i in self.linked_experiments ]
|
||||
except KeyError as k:
|
||||
# Some keys are not required and can be called through the .get() method - which is permissive and allows null values;
|
||||
# Other keys are required so if they can't be called (invalid or null) raise error and stop execution of the program:
|
||||
raise KeyError(f"The provided dictionary lacks a \"{k}\" key. Check the sample entry on eLabFTW and make sure you used the correct Resource template.")
|
||||
# Non-required attributes:
|
||||
self.name = sample_data.get("title") or None # error prevention is more important than preventing empty fields here
|
||||
|
||||
class Material:
|
||||
'''
|
||||
Material(material_data) - where material_data is a Python dictionary.
|
||||
|
||||
Meant to be used for eLabFTW Resources of either the "PLD Target" or the "Substrate" categories.
|
||||
|
||||
Both a PLD Target and a Substrate are materials made of a certain compound, of which we want to know:
|
||||
* Name and formula;
|
||||
* Shape and dimensions;
|
||||
* Misc.
|
||||
'''
|
||||
def __init__(self, material_data):
|
||||
try:
|
||||
self.extra = material_data["metadata_decoded"]["extra_fields"]
|
||||
self.compound_elabid = self.extra["Compound"]["value"]
|
||||
except KeyError as k:
|
||||
# Some keys are not required and can be called through the .get() method - which is permissive and allows null values;
|
||||
# Other keys are required so if they can't be called (invalid or null) raise error and stop execution of the program:
|
||||
raise KeyError(f"The provided dictionary lacks a \"{k}\" key. Check the target/substrate entry on eLabFTW and make sure you used the correct Resource template.")
|
||||
def get_compound_data(self, apikey):
|
||||
raw_compound_data = APIHandler(apikey).get_entry_from_elabid(self.compound_elabid, entryType="items")
|
||||
name = raw_compound_data["title"]
|
||||
extra = raw_compound_data["metadata_decoded"]["extra_fields"]
|
||||
formula = extra.get("Chemical formula")
|
||||
cas = extra.get("CAS number ") or { "value": None }
|
||||
compound_data = {
|
||||
"name" : name,
|
||||
"chemical_formula" : formula.get("value"),
|
||||
"cas_number" : cas.get("value")
|
||||
}
|
||||
return compound_data
|
||||
def get_compound_formula(self, apikey):
|
||||
formula = self.get_compound_data(apikey).get("chemical_formula")
|
||||
return formula
|
||||
|
||||
|
||||
|
||||
if __name__=="__main__":
|
||||
head = Header("MyApiKey-123456789abcdef")
|
||||
print(f"Example header: {head.dump()}")
|
||||
print(f"Example header:\n\t{head.dump}\n")
|
||||
print("Warning: you're not supposed to be running this as the main program.")
|
||||
Reference in New Issue
Block a user