Measurements

The measurements table contains a row for each measurement, which shows the relevant metadata. All measurements are referenced by an unique ID (ms_id) and are correlated with a molecule from the molecules table. Further each measurement entry belongs to a subtable which is classified by the method chosen for the measurement (e.g. cwEPR or trEPR…). The subtable contains metadata that is specific for the method and is referenced by the same ms_id as the main table. In the archive folder at /data/M{ms_id} a directory with all files concerning the measurement (e.g. raw data, scripts…) are saved. In the following you will learn how to set up a new measurement in the database and how to create the correlated directory, how to find and work with a specific measurement, update it or delete an existing measurement.

Creation of a new entry

First of all a new entry shall be added to the measurements table and the data-archive. As an example we will add a transient EPR spectra of our example molecule PER-TEMPO.

  1. Store your raw data at any folder. Save the path without suffix to a variable:

    raw_data = "/path/to/your/folder/trEPR_PER_TEMPO
    
  2. Find the ID of the corresponding molecule:

    from specatalog.crud_db import read as r
    
    per_tempo_filter = r.TDPFilter(name="PER-co-NO1")
    per_tempo_molecule = r.run_query(per_tempo_filter)[0]
    print(per_tempo_molecule.id)
    >>> 2
    
  3. Set up the MeasurementModel dependent on the method.

    The required fields for each method can be found in the documentation of the module creation_pydantic_measurements.:

    from specatalog.models import creation_pydantic_measurements as ms
    from datetime import date
    
    new_measurement = ms.TREPRModel(molecular_id=1,
                                    temperature=80,
                                    solvent="toluene",
                                    date=date(2025, 12, 24),
                                    measured_by="your_name",
                                    device="ELEXSYS",
                                    frequenc_band="Q",
                                    attenuation="20dB",
                                    exciation_wl=530)
    

    Note

    Some of the fields have a special type which is neiter a number nor a string but a class from allowed_values.py. For example the parameter solvent needs to be from the class Solvents. In that case only the values that are specified in allowed_values.py will be accepted. For more details see the documentation about Allowed values.

  4. Use the function create_new_measurement to add the entry to the table of the database:

    from specatalog.crud_db import create
    
    measurement = create.create_new_measurement(new_measurement)
    
  5. Use the information from the variable measurement to set up the directory in the database.

    The measurement object contains the attribute measurement.id. The ID of the measurement can be used to name the directory correctly. It is recommended to use the functions from the module measurement_management:

    import specatalog.data_management.measurement_management as mm
    from specatalog.main import BASE_PATH
    
    mm.create_measurement_dir(BASE_PATH, measurement.id)  # create new directory with subfolders
    mm.raw_data_to_folder(raw_data, "bruker_bes3t", BASE_PATH, measurement.id)  # raw data are copied to /data/M{ms_id}/raw
    mm.raw_data_to_hdf5(BASE_PATH, measurement.id, "bruker_bes3t")  # The raw spectra are added to the measurement.h5-file as arrays
    

    Note

    The functions raw_data_to_foler and raw_data_to_hdf5 can be used only to specific formats. At the moment the following formats are supported:

    • "bruker_bes3t": for sets of Bruker files with the suffixes .DSC and .DTA and optional .YGF.

    • "cw_epr": for sets of Bruker files with the suffixes .DSC and .DTA which contain 2D-cwEPR datasets.

    • "uvvis_ulm": for UVvis files measured at the spectrometer located at Ulm with the suffix .txt.

    • "uvvis_freiburg": for UVvis files measured at the spectrometer located at Freiburg with the suffix .txt.

    For all other formats the functions will not work. The files for the measurement have to be copied manually to /data/M{ms_id}/raw and have to be loaded and stored in the measurement.h5-file (for more information see the documentation about Measurement.h5 file).

Tip

To ensure consistency it is important to make sure all of the steps 1-5 have been run so that a measurement directory exists for for each database entry with the same ID. To make this workflow easier the function create_full_measurement from the helpers package can be used:

from specatalog.helpers.full_entry import create_full_measurement

# creates the database entry and creates/ fills the measurement-directory
fm = create_full_measurement(new_measurement, BASE_PATH, raw_data, "bruker_bes3t")

print(fm.success)
>>> True
print(fm.measurement_id)
>>> {ms_id}

Query the database

It is possible to filter the database for measurements with specific properties. This may become necessary in order to get an overview over the measurements that have been done or for finding out the ms_id of a specific measurement to work with. As an example we want to query the database for all trEPR measurements that have been done so far in frozen solution for the molecule with mol_id = 1 by you.

  1. Set up a filter model

    An empty filter model includes all possible fields. You can fill the model with your desired properties. All possible FilterModels and an explanation of their fields can be found here.:

    from specatalog.crud_db import read as r
    
    filter_model = r.TREPRFilter(molecular_id=1, temperature__le=80, measured_by="your name")
    
  2. (Optional) Set up an ordering model

    If you want to sort your results you can construct an ordering model, where you can choose for each parameter if the list of results shall be ordered ascending ("asc") or descending ("descending"`). An OrderingModel is not required but in our example we sort the results with respect to the measurement data:

    ordering_model = r.TREPROrdering(date="asc")
    
  3. Run the query using the function run_query.

    Now you can run the query and print the results. The printed results have the structure: (name of the data folder: method, name of the molecule):

    results = r.run_query(filter_model, ordering_model)
    
    print(results)  # print the list with all results
    >>> (M2: TREPR; PDI4-co-NO4)
    
    print(results[0].id)
    >>> 2
    
    print(results[0].molecule.name
    >>> PER-co-NO1
    

    The function returns a list of measurement objects. You cann call the values of the attributes like the ID or the molecule. The molecule is again an object where you can call all attributes.

Update measurements

If you have to change information on a measurement it is possible to update the database entries. As an example you have done some some analysis on your spectral data of the measurement with the ms_id=1 that you have saved in the archive folder. Now you can set the entry for evaluated to true in order to mark the dataset as evaluated.

  1. Load the measurement.

    We will load the entry with the ID=1 by querying the database for the measurement with id=1 and choosing the first (and only) result from the list.:

    from specatalog.crud_db import read as r
    ms_1 = r.run_query(r.MeasurementFilter(id=1))[0]
    
  2. Build an update filter

    Analoguos to the models for filtering UpdateModels exist. Each attribute of an object of the class UpdateModel can be set to a new attribute (of the correct type):

    from specatalog.crud_db import update as up
    update_evaluated = up.MeasurementUpdate(evaluated=True)
    
  3. Update the database entry by using the function update_model.:

    up.update_model(ms_1, update_evaluated)
    

Delete measurements

A measurement with the ID ms_id, can be easily deleted. Make sure to delete the database entry and the corresponding folder in the archive directory:

from specatalog.crud_db import delete as de
from specatalog.data_management import measurement_management as mm
from specatalog.main import BASE_PATH

# deletion from database
de.delete_measurement(ms_id=14)

# deletion from directory
mm.delete_measurement(BASE_PATH, 14)

Tip

To ensure consistency between the archive folder and the database you can also use the function delete_full_measurement from the helpers package

from specatalog.helpers.full_entry import delete_full_measurement

# deletes the database entry and the measurement-directory
result = delete_full_measurement(BASE_PATH, 1)
print(result.success)
>>> True