MAJOR: solves problem related to ELABFTW_API_URL variable

if no value was specified for such variable (or .env was missing)
EAU would be set to None and get stuck in a prompt loop

solved by turning EAU into a required variable in APIHandler
(and editing a lot of code through all of src/)
This commit is contained in:
2026-05-14 17:24:02 +02:00
parent 1ce381f341
commit 5bc636299a
3 changed files with 56 additions and 36 deletions

View File

@@ -23,11 +23,11 @@ class APIHandler:
# TO-DO: remove static url. # TO-DO: remove static url.
def __init__(self, api_key="", ELABFTW_API_URL=None): def __init__(self, api_key="", ELABFTW_API_URL=None):
"""Init method, api_key suggested but not required (empty by default).""" """Init method, api_key suggested but not required (empty by default)."""
if not ELABFTW_API_URL: # if not ELABFTW_API_URL:
load_dotenv() # load_dotenv()
ELABFTW_API_URL = os.getenv("ELABFTW_API_URL") or input( # ELABFTW_API_URL = os.getenv("ELABFTW_API_URL") or input(
"Enter a valid eLabFTW API URL (ends with '/api/v2)': " # "Enter a valid eLabFTW API URL (ends with '/api/v2)': "
) # )
self.api_key = api_key self.api_key = api_key
self.auth = {"Authorization": api_key} self.auth = {"Authorization": api_key}
self.content = {"Content-Type": "application/json"} self.content = {"Content-Type": "application/json"}

View File

@@ -134,7 +134,7 @@ class Layer:
self.start_time = layer_data.get("created_at") or None self.start_time = layer_data.get("created_at") or None
self.description = layer_data.get("body") or None self.description = layer_data.get("body") or None
def get_instruments(self, api_key): def get_instruments(self, api_key, ELABFTW_API_URL):
""" """
Retruns a dictionary of all the instruments used to create the layer. Retruns a dictionary of all the instruments used to create the layer.
The format of the dictionary is: The format of the dictionary is:
@@ -144,18 +144,20 @@ class Layer:
"rheed_system": str "rheed_system": str
} }
Arg: api_key: str: A valid API key for the eLabFTW instance where the data is stored, with permissions to access the relevant entries. Args:
api_key: str: A valid API key for the eLabFTW instance where the data is stored, with permissions to access the relevant entries.
eLabFTW's API keys are well documented here: https://doc.elabftw.net/docs/usage/api/. eLabFTW's API keys are well documented here: https://doc.elabftw.net/docs/usage/api/.
If you don't have an API key and are uncapable of creating one, contact your eLabFTW administrator. If you don't have an API key and are uncapable of creating one, contact your eLabFTW administrator.
Or RTFM and create one yourself, it's not that hard. Or RTFM and create one yourself, it's not that hard.
ELABFTW_API_URL: str: URL for the API root endpoint of the eLabFTW instance. Ends with '/api/v2' - no trailing slash.
""" """
raw_lasersys_data = APIHandler(api_key).get_entry_from_elabid( raw_lasersys_data = APIHandler(api_key, ELABFTW_API_URL).get_entry_from_elabid(
self.laser_system_elabid, entryType="items" self.laser_system_elabid, entryType="items"
) )
raw_chamber_data = APIHandler(api_key).get_entry_from_elabid( raw_chamber_data = APIHandler(api_key, ELABFTW_API_URL).get_entry_from_elabid(
self.chamber_elabid, entryType="items" self.chamber_elabid, entryType="items"
) )
raw_rheedsys_data = APIHandler(api_key).get_entry_from_elabid( raw_rheedsys_data = APIHandler(api_key, ELABFTW_API_URL).get_entry_from_elabid(
self.rheed_system_elabid, entryType="items" self.rheed_system_elabid, entryType="items"
) )
instruments_used = { instruments_used = {
@@ -248,6 +250,7 @@ class Entrypoint:
* linked_experiments_elabid: list: List of eLabFTW internal id's of the experiments linked to the entrypoint. * linked_experiments_elabid: list: List of eLabFTW internal id's of the experiments linked to the entrypoint.
""" """
try: try:
self.name = sample_data["title"]
self.extra = sample_data["metadata_decoded"]["extra_fields"] self.extra = sample_data["metadata_decoded"]["extra_fields"]
self.linked_items = sample_data["items_links"] # dict self.linked_items = sample_data["items_links"] # dict
self.batch_elabid = self.extra["Substrate batch"]["value"] # elabid self.batch_elabid = self.extra["Substrate batch"]["value"] # elabid
@@ -262,11 +265,6 @@ class Entrypoint:
raise KeyError( 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.' 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
# although I don't think it's even possible to fuck up this bad...
class Material: class Material:
@@ -310,7 +308,7 @@ class Material:
f'The provided dictionary lacks a "{k}" key. Check the target/substrate entry on eLabFTW and make sure you used the correct Resource template.' 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): def get_compound_data(self, apikey, ELABFTW_API_URL):
""" """
Returns a dictionary with the relevant data on the compound of which the material is made. Returns a dictionary with the relevant data on the compound of which the material is made.
The format of the dictionary is: The format of the dictionary is:
@@ -320,12 +318,14 @@ class Material:
"cas_number": str "cas_number": str
} }
Arg: api_key: str: A valid API key for the eLabFTW instance where the data is stored, with permissions to access the relevant entries. Args:
api_key: str: A valid API key for the eLabFTW instance where the data is stored, with permissions to access the relevant entries.
eLabFTW's API keys are well documented here: https://doc.elabftw.net/docs/usage/api/. eLabFTW's API keys are well documented here: https://doc.elabftw.net/docs/usage/api/.
If you don't have an API key and are uncapable of creating one, contact your eLabFTW administrator. If you don't have an API key and are uncapable of creating one, contact your eLabFTW administrator.
Or RTFM and create one yourself, it's not that hard. Or RTFM and create one yourself, it's not that hard.
ELABFTW_API_URL: str: URL for the API root endpoint of the eLabFTW instance. Ends with '/api/v2' - no trailing slash.
""" """
raw_compound_data = APIHandler(apikey).get_entry_from_elabid( raw_compound_data = APIHandler(apikey, ELABFTW_API_URL).get_entry_from_elabid(
self.compound_elabid, entryType="items" self.compound_elabid, entryType="items"
) )
name = raw_compound_data["title"] name = raw_compound_data["title"]
@@ -339,8 +339,20 @@ class Material:
} }
return compound_data return compound_data
def get_compound_formula(self, apikey): def get_compound_formula(self, apikey, ELABFTW_API_URL):
formula = self.get_compound_data(apikey).get("chemical_formula") """
Returns a string with the chemical formula of the compound.
Args:
api_key: str: A valid API key for the eLabFTW instance where the data is stored, with permissions to access the relevant entries.
eLabFTW's API keys are well documented here: https://doc.elabftw.net/docs/usage/api/.
If you don't have an API key and are uncapable of creating one, contact your eLabFTW administrator.
Or RTFM and create one yourself, it's not that hard.
ELABFTW_API_URL: str: URL for the API root endpoint of the eLabFTW instance. Ends with '/api/v2' - no trailing slash.
"""
formula = self.get_compound_data(apikey, ELABFTW_API_URL).get(
"chemical_formula"
)
return formula return formula
@@ -454,7 +466,8 @@ if __name__ == "__main__":
# print(f"Example header:\n\t{head.header}\n") # print(f"Example header:\n\t{head.header}\n")
# print("Warning: you're not supposed to be running this as the main program.") # print("Warning: you're not supposed to be running this as the main program.")
api_key = getpass("Paste API key here [no echo]: ") api_key = getpass("Paste API key here [no echo]: ")
handler = APIHandler(api_key=api_key) ELABFTW_API_URL = input("Enter a valid eLabFTW API URL (ends with '/api/v2)': ")
handler = APIHandler(api_key, ELABFTW_API_URL)
exp58 = handler.get_entry_from_elabid(elabid=58, entryType="experiments") exp58 = handler.get_entry_from_elabid(elabid=58, entryType="experiments")
layer58 = Layer(exp58) layer58 = Layer(exp58)
print(layer58.list_attachments()) print(layer58.list_attachments())

View File

@@ -20,7 +20,7 @@ def call_entrypoint_from_elabid(elabid):
Arg: elabid: int eLabFTW internal id of the selected resource. Arg: elabid: int eLabFTW internal id of the selected resource.
""" """
try: try:
sample_data = APIHandler(api_key).get_entry_from_elabid( sample_data = APIHandler(api_key, ELABFTW_API_URL).get_entry_from_elabid(
elabid, entryType="items" elabid, entryType="items"
) )
if not sample_data.get("category_title") == "Sample": if not sample_data.get("category_title") == "Sample":
@@ -44,7 +44,7 @@ def call_material_from_elabid(elabid):
arg: elabid: int eLabFTW internal id of the selected resource. arg: elabid: int eLabFTW internal id of the selected resource.
""" """
try: try:
material_data = APIHandler(api_key).get_entry_from_elabid( material_data = APIHandler(api_key, ELABFTW_API_URL).get_entry_from_elabid(
elabid, entryType="items" elabid, entryType="items"
) )
material_category = material_data.get("category_title") material_category = material_data.get("category_title")
@@ -75,7 +75,7 @@ def call_layers_from_list(elabid_list):
list_of_layers = [] list_of_layers = []
for elabid in elabid_list: for elabid in elabid_list:
try: try:
layer_data = APIHandler(api_key).get_entry_from_elabid( layer_data = APIHandler(api_key, ELABFTW_API_URL).get_entry_from_elabid(
elabid, entryType="experiments" elabid, entryType="experiments"
) )
if not layer_data.get("category_title") == "PLD Deposition": if not layer_data.get("category_title") == "PLD Deposition":
@@ -106,7 +106,7 @@ def call_proposal_from_elabid(elabid):
Arg: elabid: int eLabFTW internal id of the selected resource. Arg: elabid: int eLabFTW internal id of the selected resource.
""" """
try: try:
proposal_data = APIHandler(api_key).get_entry_from_elabid( proposal_data = APIHandler(api_key, ELABFTW_API_URL).get_entry_from_elabid(
elabid, entryType="items" elabid, entryType="items"
) )
proposal_category = proposal_data.get("category_title") proposal_category = proposal_data.get("category_title")
@@ -186,7 +186,7 @@ def deduplicate_instruments_from_layers(layers):
rheeds = [] rheeds = []
elegant_dict = {} elegant_dict = {}
for lyr in layers: for lyr in layers:
instruments = lyr.get_instruments(api_key) instruments = lyr.get_instruments(api_key, ELABFTW_API_URL)
lasers.append(instruments["laser_system"]) lasers.append(instruments["laser_system"])
chambers.append(instruments["deposition_chamber"]) chambers.append(instruments["deposition_chamber"])
rheeds.append(instruments["rheed_system"]) rheeds.append(instruments["rheed_system"])
@@ -356,7 +356,9 @@ def make_nexus_schema_dictionary(substrate_object, layers):
"sample": { "sample": {
"substrate": { "substrate": {
"name": substrate_object.name, "name": substrate_object.name,
"chemical_formula": substrate_object.get_compound_formula(api_key), "chemical_formula": substrate_object.get_compound_formula(
api_key, ELABFTW_API_URL
),
"orientation": substrate_object.orientation, "orientation": substrate_object.orientation,
"miscut_angle": { "miscut_angle": {
"value": substrate_object.miscut_angle, "value": substrate_object.miscut_angle,
@@ -384,7 +386,9 @@ def make_nexus_schema_dictionary(substrate_object, layers):
target_object = chain_layer_to_target(layer) target_object = chain_layer_to_target(layer)
target_dict = { target_dict = {
"name": target_object.name, "name": target_object.name,
"chemical_formula": target_object.get_compound_formula(api_key), "chemical_formula": target_object.get_compound_formula(
api_key, ELABFTW_API_URL
),
"description": target_object.description, "description": target_object.description,
"shape": target_object.shape, "shape": target_object.shape,
"dimensions": target_object.dimensions, "dimensions": target_object.dimensions,
@@ -772,7 +776,7 @@ def build_nexus_file(pld_fabrication, output_path="output/nffa-di_unnamed.h5"):
n = layer_dict["layer_number"] n = layer_dict["layer_number"]
rheed_data_file = layer_dict["data"][0] # first in the tuple rheed_data_file = layer_dict["data"][0] # first in the tuple
rheed_image_file = layer_dict["data"][1] # second in the tuple rheed_image_file = layer_dict["data"][1] # second in the tuple
handler = APIHandler(api_key) handler = APIHandler(api_key, ELABFTW_API_URL)
# TO-DO: maybe make a dedicated function??? # TO-DO: maybe make a dedicated function???
data_path = None data_path = None
@@ -877,7 +881,10 @@ if __name__ == "__main__":
or input("Enter elabid of your starting sample [default = 1111]: ") or input("Enter elabid of your starting sample [default = 1111]: ")
or 1111 or 1111
) )
handler = APIHandler(api_key) ELABFTW_API_URL = os.getenv("ELABFTW_API_URL") or input(
"Enter a valid eLabFTW API URL (ends with '/api/v2)': "
)
handler = APIHandler(api_key, ELABFTW_API_URL)
data = handler.get_entry_from_elabid(elabid) data = handler.get_entry_from_elabid(elabid)
sample = Entrypoint(data) sample = Entrypoint(data)
sample_name = sample.name.strip().replace( sample_name = sample.name.strip().replace(