From 0600ee313f02694c1eadaabb17b2254938949347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20tom=20W=C3=B6rden?= <h.tomwoerden@indiscale.com> Date: Thu, 4 Apr 2024 10:40:11 +0200 Subject: [PATCH] WIP rename --- src/caosadvancedtools/cache.py | 2 +- src/caosadvancedtools/cfood.py | 8 +-- src/caosadvancedtools/crawler.py | 72 ++++++++++--------- src/caosadvancedtools/datamodel_problems.py | 8 +-- src/caosadvancedtools/guard.py | 2 +- src/caosadvancedtools/loadFiles.py | 13 +++- .../scifolder/experiment_cfood.py | 15 ++-- .../scifolder/publication_cfood.py | 10 ++- .../scifolder/result_table_cfood.py | 18 +++-- .../scifolder/simulation_cfood.py | 13 ++-- .../scifolder/software_cfood.py | 13 ++-- src/caosadvancedtools/scifolder/utils.py | 1 - src/caosadvancedtools/scifolder/withreadme.py | 7 +- .../serverside/generic_analysis.py | 4 +- src/caosadvancedtools/serverside/helper.py | 7 +- src/caosadvancedtools/utils.py | 4 +- 16 files changed, 102 insertions(+), 95 deletions(-) diff --git a/src/caosadvancedtools/cache.py b/src/caosadvancedtools/cache.py index cf74e330..0ffc7282 100644 --- a/src/caosadvancedtools/cache.py +++ b/src/caosadvancedtools/cache.py @@ -33,7 +33,7 @@ from abc import ABC, abstractmethod from copy import deepcopy from hashlib import sha256 -import caosdb as db +import linkahead as db from lxml import etree diff --git a/src/caosadvancedtools/cfood.py b/src/caosadvancedtools/cfood.py index c0da4f01..7c71f8a4 100644 --- a/src/caosadvancedtools/cfood.py +++ b/src/caosadvancedtools/cfood.py @@ -43,10 +43,10 @@ import warnings from abc import ABCMeta, abstractmethod from datetime import datetime -import caosdb as db -from caosdb.common.models import Entity -from caosdb.exceptions import (BadQueryError, EmptyUniqueQueryError, - QueryNotUniqueError, TransactionError) +import linkahead as db +from linkahead.common.models import Entity +from linkahead.exceptions import (BadQueryError, EmptyUniqueQueryError, + QueryNotUniqueError, TransactionError) from .datamodel_problems import DataModelProblems from .guard import global_guard as guard diff --git a/src/caosadvancedtools/crawler.py b/src/caosadvancedtools/crawler.py index 5e84bc8a..6655919e 100644 --- a/src/caosadvancedtools/crawler.py +++ b/src/caosadvancedtools/crawler.py @@ -41,14 +41,14 @@ match. This occurs in basically three steps: import logging import os -import subprocess import traceback import uuid from datetime import datetime from sqlite3 import IntegrityError +from xml.sax.saxutils import escape -import caosdb as db -from caosdb.exceptions import BadQueryError +import linkahead as db +from linkahead.exceptions import BadQueryError, TransactionError from .cache import IdentifiableCache, UpdateCache, get_pretty_xml from .cfood import RowCFood, add_files, get_ids_for_entities_with_names @@ -67,7 +67,7 @@ def separated(text): return "-"*60 + "\n" + text -def apply_list_of_updates(to_be_updated, update_flags={}, +def apply_list_of_updates(to_be_updated, update_flags=None, update_cache=None, run_id=None): """Updates the `to_be_updated` Container, i.e., pushes the changes to CaosDB after removing possible duplicates. If a chace is provided, uauthorized @@ -87,6 +87,8 @@ def apply_list_of_updates(to_be_updated, update_flags={}, Id with which the pending updates are cached. Only meaningful if `update_cache` is provided. Default is None. """ + if update_flags is None: + update_flags = {} if len(to_be_updated) == 0: return @@ -132,7 +134,7 @@ def apply_list_of_updates(to_be_updated, update_flags={}, ) logger.debug(traceback.format_exc()) logger.debug(e) - except Exception as e: + except TransactionError as e: DataModelProblems.evaluate_exception(e) @@ -220,25 +222,27 @@ class Crawler(object): new_cont = db.Container.from_xml(new) ids = [] tmp = db.Container() - update_incomplete = False + # TODO what was this for? + # update_incomplete = False # remove duplicate entities for el in new_cont: if el.id not in ids: ids.append(el.id) tmp.append(el) else: - update_incomplete = True + pass + # update_incomplete = True new_cont = tmp - if new_cont[0].version: + if new_cont[0].version: # pylint: disable=no-member valids = db.Container() nonvalids = db.Container() for ent in new_cont: remote_ent = db.Entity(id=ent.id).retrieve() - if ent.version == remote_ent.version: + if ent.version == remote_ent.version: # pylint: disable=no-member valids.append(ent) else: - update_incomplete = True + # update_incomplete = True nonvalids.append(remote_ent) valids.update(unique=False) logger.info("Successfully updated {} records!".format( @@ -317,10 +321,10 @@ class Crawler(object): logger.debug(e) # TODO: Generally: in which cases should exceptions be raised? When is # errors_occured set to True? The expected behavior must be documented. - except Exception as e: + except TransactionError as e: try: DataModelProblems.evaluate_exception(e) - except BaseException: + except BaseException: # pylint: disable=broad-exception-caught pass logger.debug("Failed during execution of {}!".format( Cfood.__name__)) @@ -349,13 +353,12 @@ class Crawler(object): logger.info("Cannot access {}. However, it might be needed for" " the correct execution".format(e.filename)) remove_cfoods.append(cfood) - except Exception as e: + except TransactionError as e: try: DataModelProblems.evaluate_exception(e) - except BaseException: + except BaseException: # pylint: disable=broad-exception-caught pass - logger.debug("Failed during execution of {}!".format( - Cfood.__name__)) + logger.debug("Failed during execution of {}!".format(cfood.__name__)) logger.debug(traceback.format_exc()) logger.debug(e) remove_cfoods.append(cfood) @@ -415,8 +418,8 @@ class Crawler(object): self.cache.insert_list(hashes, identifiables) def crawl(self, security_level=RETRIEVE, path=None): - self.run_id = uuid.uuid1() - logger.info("Run Id: " + str(self.run_id)) + run_id = uuid.uuid1() + logger.info("Run Id: " + str(run_id)) guard.set_level(level=security_level) logger.info("Scanning the objects to be treated...") @@ -435,17 +438,17 @@ class Crawler(object): cfood.to_be_updated, cfood.update_flags, update_cache=self.update_cache, - run_id=self.run_id) + run_id=run_id) except FileNotFoundError as e: logger.info("Cannot access {}. However, it might be needed for" " the correct execution".format(e.filename)) except DataInconsistencyError as e: logger.debug(traceback.format_exc()) logger.debug(e) - except Exception as e: + except TransactionError as e: try: DataModelProblems.evaluate_exception(e) - except Exception: + except Exception: # pylint: disable=broad-exception-caught pass logger.info("Failed during execution of {}!".format( cfood.__class__.__name__)) @@ -457,7 +460,7 @@ class Crawler(object): errors_occured = True tbs.append(e) - pending_changes = self.update_cache.get_updates(self.run_id) + pending_changes = self.update_cache.get_updates(run_id) if pending_changes: # Sending an Email with a link to a form to authorize updates is @@ -465,7 +468,7 @@ class Crawler(object): if "SHARED_DIR" in os.environ: filename = Crawler.save_form([el[3] - for el in pending_changes], path, self.run_id) + for el in pending_changes], path, run_id) Crawler.send_mail([el[3] for el in pending_changes], filename) for i, el in enumerate(pending_changes): @@ -477,7 +480,7 @@ ____________________\n""".format(i+1, len(pending_changes)) + str(el[3])) logger.info("There where unauthorized changes (see above). An " "email was sent to the curator.\n" "You can authorize the updates by invoking the crawler" - " with the run id: {rid}\n".format(rid=self.run_id)) + " with the run id: {rid}\n".format(rid=run_id)) if len(DataModelProblems.missing) > 0: err_msg = ("There were problems with one or more RecordType or " @@ -488,8 +491,9 @@ ____________________\n""".format(i+1, len(pending_changes)) + str(el[3])) logger.error(err_msg) logger.error('Crawler finished with Datamodel Errors') elif errors_occured: - msg = "There were fatal errors during execution, please " - "contact the system administrator!" + msg = ("There were fatal errors during execution, please " + "contact the system administrator!" + ) if self.debug_file: msg += "\nPlease provide the following path:\n{}".format( @@ -516,7 +520,6 @@ ____________________\n""".format(i+1, len(pending_changes)) + str(el[3])) path: the path defining the subtree that is crawled """ - from xml.sax.saxutils import escape # TODO move path related stuff to sss_helper form = """ @@ -593,7 +596,7 @@ ____________________\n""".format(i+1, len(pending_changes)) + str(el[3])) randname = os.path.basename(os.path.abspath(directory)) filepath = os.path.abspath(os.path.join(directory, filename)) filename = os.path.join(randname, filename) - with open(filepath, "w") as f: + with open(filepath, "w", encoding="utf8") as f: f.write(form) return filename @@ -673,7 +676,7 @@ carefully and if the changes are ok, click on the following link: guard.safe_insert(missing, unique=False, flags={"force-missing-obligatory": "ignore"}) inserted.append(ent) - except Exception as e: + except TransactionError as e: DataModelProblems.evaluate_exception(e) if len(existing) > 0: info = "Identified the following existing entities:\n" @@ -774,14 +777,13 @@ class FileCrawler(Crawler): path if path.endswith("/") else path + "/") + "**'" q_info = "Sending the following query: '" + query_str + "'\n" files = db.execute_query(query_str) - logger.info( - q_info + "Found {} files that need to be processed.".format( - len(files))) + logger.info(q_info + f"Found {len(files)} files that need to be processed.") return files class TableCrawler(Crawler): + """Crawler for table like data.""" def __init__(self, table, unique_cols, recordtype, **kwargs): """ @@ -796,6 +798,7 @@ class TableCrawler(Crawler): # TODO I do not like this yet, but I do not see a better way so far. class ThisRowCF(RowCFood): + """CFood for rows.""" def __init__(self, item): super().__init__(item, unique_cols, recordtype) @@ -821,7 +824,6 @@ def get_value(prop): if isinstance(prop.value, db.Entity): return prop.value.id - elif isinstance(prop.value, datetime): + if isinstance(prop.value, datetime): return prop.value.isoformat() - else: - return prop.value + return prop.value diff --git a/src/caosadvancedtools/datamodel_problems.py b/src/caosadvancedtools/datamodel_problems.py index df5b7e56..e96bf799 100644 --- a/src/caosadvancedtools/datamodel_problems.py +++ b/src/caosadvancedtools/datamodel_problems.py @@ -27,10 +27,10 @@ be inserted by hand or gueesed from possible exceptions when inserting or updating entities with missing parents and/or properties. """ -from caosdb.exceptions import (EntityDoesNotExistError, - TransactionError, - UnqualifiedParentsError, - UnqualifiedPropertiesError) +from linkahead.exceptions import (EntityDoesNotExistError, + TransactionError, + UnqualifiedParentsError, + UnqualifiedPropertiesError) class DataModelProblems(object): diff --git a/src/caosadvancedtools/guard.py b/src/caosadvancedtools/guard.py index aa37448d..16cf7304 100644 --- a/src/caosadvancedtools/guard.py +++ b/src/caosadvancedtools/guard.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. -import caosdb as db +import linkahead as db RETRIEVE = 0 INSERT = 1 diff --git a/src/caosadvancedtools/loadFiles.py b/src/caosadvancedtools/loadFiles.py index 405b3d13..26b6c1c0 100755 --- a/src/caosadvancedtools/loadFiles.py +++ b/src/caosadvancedtools/loadFiles.py @@ -129,7 +129,14 @@ def create_re_for_file_list(files, localroot, remoteroot): def loadpath(path, include, exclude, prefix, dryrun, forceAllowSymlinks, caosdbignore=None, - localpath=None): + localpath=None) -> dict: + """ +Returns +------- + +inserted: dict + A dict with the files to be included for each of the ``include`` elements. + """ if caosdbignore: # create list of files and create regular expression for small chunks @@ -146,6 +153,7 @@ def loadpath(path, include, exclude, prefix, dryrun, forceAllowSymlinks, caosdbi else: includes = [include] + inserted = {} # if no caosdbignore file is used, this iterates over a single include for include in includes: if dryrun: @@ -177,8 +185,9 @@ def loadpath(path, include, exclude, prefix, dryrun, forceAllowSymlinks, caosdbi logger.info( f"Made new files accessible: {len(files)}, combined size: {convert_size(totalsize)} ") + inserted[include] = files - return + return inserted def main(argv=None): diff --git a/src/caosadvancedtools/scifolder/experiment_cfood.py b/src/caosadvancedtools/scifolder/experiment_cfood.py index 38606b5f..502314ad 100644 --- a/src/caosadvancedtools/scifolder/experiment_cfood.py +++ b/src/caosadvancedtools/scifolder/experiment_cfood.py @@ -16,16 +16,16 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. +from typing import TYPE_CHECKING, Tuple + import caosdb as db -from caosadvancedtools.cfood import (AbstractFileCFood, assure_has_description, - assure_has_parent, assure_has_property, - assure_object_is_in_list, get_entity) -from caosadvancedtools.read_md_header import get_header +from caosadvancedtools.cfood import (AbstractFileCFood, assure_has_property, + assure_object_is_in_list) from .generic_pattern import full_pattern from .utils import parse_responsibles, reference_records_corresponding_to_files from .withreadme import DATAMODEL as dm -from .withreadme import RESULTS, REVISIONOF, SCRIPTS, WithREADME, get_glob +from .withreadme import RESULTS, REVISIONOF, WithREADME, get_glob class ExperimentCFood(AbstractFileCFood, WithREADME): @@ -40,6 +40,9 @@ class ExperimentCFood(AbstractFileCFood, WithREADME): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.project = db.Record() + self.experiment = db.Record() + self.people = [] WithREADME.__init__(self) self.name_map = {}, @@ -52,7 +55,7 @@ class ExperimentCFood(AbstractFileCFood, WithREADME): self.find_referenced_files([RESULTS]) @staticmethod - def create_identifiable_experiment(match): + def create_identifiable_experiment(match) -> Tuple[db.Record, db.Record]: # create the project identifiable name = ExperimentCFood.name_beautifier( match.group("project_identifier")) diff --git a/src/caosadvancedtools/scifolder/publication_cfood.py b/src/caosadvancedtools/scifolder/publication_cfood.py index fc78e5b7..b4bc70b8 100644 --- a/src/caosadvancedtools/scifolder/publication_cfood.py +++ b/src/caosadvancedtools/scifolder/publication_cfood.py @@ -16,18 +16,14 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. -import os -from itertools import chain - import caosdb as db from caosadvancedtools.cfood import (AbstractFileCFood, assure_object_is_in_list, fileguide, - get_entity) + ) from caosadvancedtools.read_md_header import get_header -from caosadvancedtools.utils import find_records_that_reference_ids from .generic_pattern import date_suffix_pattern, readme_pattern -from .utils import (get_files_referenced_by_field, parse_responsibles, +from .utils import (parse_responsibles, reference_records_corresponding_to_files) from .withreadme import DATAMODEL as dm from .withreadme import (RESULTS, REVISIONOF, SCRIPTS, SOURCES, WithREADME, @@ -57,6 +53,8 @@ class PublicationCFood(AbstractFileCFood, WithREADME): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.publication = db.Record() + self.people = [] WithREADME.__init__(self) def collect_information(self): diff --git a/src/caosadvancedtools/scifolder/result_table_cfood.py b/src/caosadvancedtools/scifolder/result_table_cfood.py index deaa2d00..71668d3a 100644 --- a/src/caosadvancedtools/scifolder/result_table_cfood.py +++ b/src/caosadvancedtools/scifolder/result_table_cfood.py @@ -20,17 +20,12 @@ import re import caosdb as db import pandas as pd -from caosadvancedtools.cfood import (AbstractFileCFood, assure_has_description, - assure_has_parent, assure_has_property, - assure_object_is_in_list, get_entity) -from caosadvancedtools.read_md_header import get_header +from caosadvancedtools.cfood import (AbstractFileCFood, + ) from ..cfood import assure_property_is, fileguide from .experiment_cfood import ExperimentCFood from .generic_pattern import date_pattern, date_suffix_pattern, project_pattern -from .utils import parse_responsibles, reference_records_corresponding_to_files -from .withreadme import DATAMODEL as dm -from .withreadme import RESULTS, REVISIONOF, SCRIPTS, WithREADME, get_glob # TODO similarities with TableCrawler @@ -48,6 +43,8 @@ class ResultTableCFood(AbstractFileCFood): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.recs = [] + self.experiment, self.project = (db.Record(), db.Record()) self.table = pd.read_csv(fileguide.access(self.crawled_path)) @staticmethod @@ -60,7 +57,7 @@ class ResultTableCFood(AbstractFileCFood): self.experiment, self.project = ( ExperimentCFood.create_identifiable_experiment(self.match)) - for idx, row in self.table.iterrows(): + for _, row in self.table.iterrows(): rec = db.Record() rec.add_parent(self.match.group("recordtype")) @@ -77,10 +74,11 @@ class ResultTableCFood(AbstractFileCFood): self.identifiables.extend([self.project, self.experiment]) def update_identifiables(self): - for ii, (idx, row) in enumerate(self.table.iterrows()): + for ii, (_, row) in enumerate(self.table.iterrows()): for col in row.index: match = re.match(ResultTableCFood.property_name_re, col) - assure_property_is(self.recs[ii], match.group("pname"), row.loc[col], to_be_updated=self.to_be_updated) + assure_property_is(self.recs[ii], match.group("pname"), row.loc[col], + to_be_updated=self.to_be_updated) assure_property_is(self.experiment, self.match.group("recordtype"), self.recs, to_be_updated=self.to_be_updated, datatype=db.LIST(self.match.group("recordtype"))) diff --git a/src/caosadvancedtools/scifolder/simulation_cfood.py b/src/caosadvancedtools/scifolder/simulation_cfood.py index c8f23f14..24f4fdaf 100644 --- a/src/caosadvancedtools/scifolder/simulation_cfood.py +++ b/src/caosadvancedtools/scifolder/simulation_cfood.py @@ -16,17 +16,13 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. -import os -from itertools import chain - import caosdb as db -from caosadvancedtools.cfood import (AbstractFileCFood, assure_has_parent, +from caosadvancedtools.cfood import (AbstractFileCFood, assure_has_property, - assure_object_is_in_list, get_entity) -from caosadvancedtools.read_md_header import get_header + assure_object_is_in_list) from .generic_pattern import full_pattern -from .utils import (get_files_referenced_by_field, parse_responsibles, +from .utils import (parse_responsibles, reference_records_corresponding_to_files) from .withreadme import DATAMODEL as dm from .withreadme import (RESULTS, REVISIONOF, SCRIPTS, SOURCES, WithREADME, @@ -41,6 +37,9 @@ class SimulationCFood(AbstractFileCFood, WithREADME): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.project = db.Record() + self.simulation = db.Record() + self.people = [] WithREADME.__init__(self) def collect_information(self): diff --git a/src/caosadvancedtools/scifolder/software_cfood.py b/src/caosadvancedtools/scifolder/software_cfood.py index 77fb4652..47abda23 100644 --- a/src/caosadvancedtools/scifolder/software_cfood.py +++ b/src/caosadvancedtools/scifolder/software_cfood.py @@ -17,20 +17,16 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <https://www.gnu.org/licenses/>. -import os -from itertools import chain import caosdb as db -from caosadvancedtools.cfood import (AbstractFileCFood, assure_has_parent, +from caosadvancedtools.cfood import (AbstractFileCFood, assure_has_property, assure_name_is, - assure_object_is_in_list, get_entity) + assure_object_is_in_list) from caosadvancedtools.guard import global_guard as guard -from caosadvancedtools.read_md_header import get_header from .generic_pattern import full_pattern -from .utils import get_files_referenced_by_field, parse_responsibles +from .utils import parse_responsibles from .withreadme import BINARIES -from .withreadme import DATAMODEL as dm from .withreadme import SOURCECODE, WithREADME @@ -43,6 +39,9 @@ class SoftwareCFood(AbstractFileCFood, WithREADME): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) + self.software = db.Record() + self.softwareversion = db.Record() + self.people = [] WithREADME.__init__(self) def collect_information(self): diff --git a/src/caosadvancedtools/scifolder/utils.py b/src/caosadvancedtools/scifolder/utils.py index 50e897c7..cbf87c4b 100644 --- a/src/caosadvancedtools/scifolder/utils.py +++ b/src/caosadvancedtools/scifolder/utils.py @@ -26,7 +26,6 @@ import pandas as pd from caosadvancedtools.cfood import assure_object_is_in_list, fileguide from caosadvancedtools.utils import (find_records_that_reference_ids, read_field_as_list, - return_field_or_property, string_to_person) logger = logging.getLogger("caosadvancedtools") diff --git a/src/caosadvancedtools/scifolder/withreadme.py b/src/caosadvancedtools/scifolder/withreadme.py index e1968ba4..2b9108bc 100644 --- a/src/caosadvancedtools/scifolder/withreadme.py +++ b/src/caosadvancedtools/scifolder/withreadme.py @@ -26,8 +26,7 @@ import caosdb as db from caosadvancedtools.cfood import (assure_has_description, assure_has_parent, assure_object_is_in_list, fileguide) from caosadvancedtools.read_md_header import get_header as get_md_header -from caosadvancedtools.table_importer import (win_path_converter, - win_path_list_converter) +from caosadvancedtools.table_importer import win_path_converter from caosadvancedtools.utils import return_field_or_property from .utils import (get_entity_ids_from_include_file, @@ -157,8 +156,8 @@ class WithREADME(object): for f in sublist] if len(flat_list) == 0: - LOGGER.warn("ATTENTION: the field {} does not reference any " - "known files".format(field.key)) + LOGGER.warning("ATTENTION: the field {} does not reference any " + "known files".format(field.key)) self.attached_filenames.extend(flat_list) # pylint: disable=no-member diff --git a/src/caosadvancedtools/serverside/generic_analysis.py b/src/caosadvancedtools/serverside/generic_analysis.py index 85d0c860..a0ff479d 100644 --- a/src/caosadvancedtools/serverside/generic_analysis.py +++ b/src/caosadvancedtools/serverside/generic_analysis.py @@ -87,8 +87,8 @@ import logging import os import sys -import caosdb as db -from caosdb.utils.server_side_scripting import run_server_side_script +import linkahead as db +from linkahead.utils.server_side_scripting import run_server_side_script logger = logging.getLogger(__name__) diff --git a/src/caosadvancedtools/serverside/helper.py b/src/caosadvancedtools/serverside/helper.py index ba75739e..a5f82988 100644 --- a/src/caosadvancedtools/serverside/helper.py +++ b/src/caosadvancedtools/serverside/helper.py @@ -30,7 +30,7 @@ import sys from email import message, policy, utils from tempfile import NamedTemporaryFile -import caosdb as db +import linkahead as db def wrap_bootstrap_alert(text, kind): @@ -223,6 +223,7 @@ def init_data_model(entities): "be a {}.".format(e.role, local_role)) raise DataModelError(e.name, info) except db.exceptions.EntityDoesNotExistError: + # pylint: disable-next=raise-missing-from raise DataModelError(e.name, "This entity does not exist.") return True @@ -242,11 +243,11 @@ def get_data(filename, default=None): Returns ------- - dict + out: dict Data from the given file. """ result = default.copy() if default is not None else {} - with open(filename, 'r') as fi: + with open(filename, "r", encoding="utf8") as fi: data = json.load(fi) result.update(data) diff --git a/src/caosadvancedtools/utils.py b/src/caosadvancedtools/utils.py index 4d6a4b36..d03583a1 100644 --- a/src/caosadvancedtools/utils.py +++ b/src/caosadvancedtools/utils.py @@ -27,8 +27,8 @@ import logging import os import pathlib -import caosdb as db -from caosdb.exceptions import TransactionError +import linkahead as db +from linkahead.exceptions import TransactionError logger = logging.getLogger(__name__) -- GitLab