Skip to content

Commit d48cae0

Browse files
committed
Refactor to use 'microsalt' consistently across the codebase and improve database initialization
1 parent 08806d1 commit d48cae0

File tree

14 files changed

+207
-163
lines changed

14 files changed

+207
-163
lines changed

.github/pull_request_template.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ _Test routine to verify the stability of the PR:_
2525
- _`conda activate S_microSALT`_
2626
- _(SITUATIONAL) `export MICROSALT_CONFIG=/home/proj/dropbox/microSALT.json`_
2727
- _Select a relevant subset of the following:_
28-
- _`microSALT analyse project MIC3109`_
29-
- _`microSALT analyse project MIC4107`_
30-
- _`microSALT analyse project MIC4109`_
31-
- _`microSALT analyse project ACC5551`_
28+
- _`microsalt analyse project MIC3109`_
29+
- _`microsalt analyse project MIC4107`_
30+
- _`microsalt analyse project MIC4109`_
31+
- _`microsalt analyse project ACC5551`_
3232

3333
_Verify that the results for projects MIC3109, MIC4107, MIC4109 & ACC5551 are consistent with the results attached to AMSystem doc 1490, Microbial_WGS.xlsx_
3434

.github/workflows/run_tests.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ on: [push]
44

55

66
env:
7-
CONDA_PREFIX: /home/runner/work/microSALT/microSALT/tests
7+
CONDA_PREFIX: /home/runner/work/microSALT/microsalt/tests
88
COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }}
99

1010
jobs:
@@ -35,7 +35,6 @@ jobs:
3535

3636
- name: Install Dependencies
3737
run: |
38-
mkdir ~/.microSALT && cp configExample.json ~/.microSALT/config.json
3938
poetry install --no-interaction --all-extras
4039
4140
- name: Lint with flake8

microSALT/server/app.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88

99
from flask import Flask
1010
from microsalt.server.views import bp
11-
from microsalt.store.database import initialize_database
12-
11+
from microsalt.store.orm_models import db
1312
logger = logging.getLogger("main_logger")
1413

1514
APP: Flask = None
@@ -18,10 +17,10 @@ def initialize_app(config: dict) -> None:
1817
global APP
1918
APP = Flask(__name__, template_folder="templates")
2019
APP.config.setdefault("SQLALCHEMY_DATABASE_URI", "sqlite:///:memory:")
21-
APP.config.setdefault("SQLALCHEMY_BINDS", None)
20+
APP.config.setdefault("SQLALCHEMY_BINDS", {})
2221
APP.config.setdefault("SQLALCHEMY_TRACK_MODIFICATIONS", False)
23-
initialize_database(config)
2422
_setup_app_from_config(APP, config)
23+
db.init_app(APP)
2524
APP.register_blueprint(bp)
2625

2726
def get_app() -> Flask:
@@ -40,6 +39,8 @@ def create_path(path: str, logger: logging.Logger):
4039
def handle_config_entry(entry, value, logger):
4140

4241
if isinstance(value, str) and "/" in value and entry not in ["genologics"]:
42+
if os.path.abspath(value) == "/path":
43+
logger.error(f"Path for {entry} is not set.")
4344
if not value.startswith("/"):
4445
sys.exit(-1)
4546
create_path(os.path.abspath(value), logger)

microSALT/server/views.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,22 @@
1313
log = logging.getLogger("werkzeug")
1414
log.setLevel(logging.CRITICAL)
1515

16+
def inspect_db_schema(session):
17+
"""Inspect the database schema and log the tables and columns."""
18+
19+
from sqlalchemy import inspect
20+
inspector = inspect(session.bind)
21+
tables = inspector.get_table_names()
22+
schema_info = {}
23+
for table in tables:
24+
columns = inspector.get_columns(table)
25+
schema_info[table] = [column['name'] for column in columns]
26+
return schema_info
27+
1628
@bp.route("/")
1729
def start_page():
1830
session = get_session()
31+
schema_info = inspect_db_schema(session)
1932
projects = session.query(Projects).all()
2033
return render_template("start_page.html", projects=projects)
2134

microSALT/store/database.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from sqlalchemy import create_engine, MetaData
1+
from sqlalchemy import SingletonThreadPool, create_engine, MetaData
22
from sqlalchemy.engine.base import Engine
33
from sqlalchemy.orm import Session, scoped_session, sessionmaker
44

@@ -9,7 +9,7 @@
99
def initialize_database(config: dict):
1010
global ENGINE, SESSION
1111
ENGINE = create_engine(
12-
config["database"]["SQLALCHEMY_DATABASE_URI"], connect_args={"check_same_thread": False, "timeout": 15}
12+
config["database"]["SQLALCHEMY_DATABASE_URI"], poolclass=SingletonThreadPool, connect_args={"check_same_thread": False, "timeout": 15}
1313
)
1414
session_factory = sessionmaker(bind=ENGINE)
1515
SESSION = scoped_session(session_factory)

microSALT/store/db_manipulator.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def __init__(self, config, log):
3737
self.logger = log
3838
self.engine = get_engine()
3939
self.session = get_session()
40-
self.metadata = MetaData(self.engine)
40+
self.metadata = MetaData()
4141
self.profiles = Profiles(self.metadata, self.config, self.logger).tables
4242
self.novel = Novel(self.metadata, self.config, self.logger).tables
4343
# Turns off pymysql deprecation warnings until they can update their code
@@ -139,10 +139,8 @@ def add_rec(self, data_dict: Dict[str, str], tablename: str, force=False):
139139
tablename
140140
)
141141
)
142-
pk_values = list()
143-
for item in pk_list:
144-
pk_values.append(data_dict[item])
145-
existing = self.session.query(table).get(pk_values)
142+
pk_values = {item: data_dict[item] for item in pk_list}
143+
existing = self.session.query(table).filter_by(**pk_values).first()
146144
# Add record
147145
if not existing or force:
148146
newobj = table()

microSALT/store/models.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@
44
#!/usr/bin/env python
55

66
import os
7-
from sqlalchemy import *
8-
7+
from sqlalchemy import MetaData
98

109
class Profiles:
11-
def __init__(self, metadata, config, log):
10+
def __init__(self, metadata: MetaData, config: dict, log):
1211
self.tables = dict()
1312
self.metadata = metadata
1413
self.config = config
@@ -24,7 +23,7 @@ def __init__(self, metadata, config, log):
2423
)
2524
)
2625

27-
def add_table(self, file):
26+
def add_table(self, file: str):
2827
try:
2928
with open(
3029
"{}/{}".format(self.config["folders"]["profiles"], file), "r"
@@ -55,7 +54,7 @@ def add_table(self, file):
5554

5655

5756
class Novel:
58-
def __init__(self, metadata, config, log):
57+
def __init__(self, metadata: MetaData, config: dict, log):
5958
self.tables = dict()
6059
self.metadata = metadata
6160
self.config = config
@@ -71,7 +70,7 @@ def __init__(self, metadata, config, log):
7170
)
7271
)
7372

74-
def add_table(self, file):
73+
def add_table(self, file: str):
7574
try:
7675
with open(
7776
"{}/{}".format(self.config["folders"]["profiles"], file), "r"
@@ -98,4 +97,4 @@ def add_table(self, file):
9897
p = eval(header)
9998
self.tables[file] = p
10099
except Exception as e:
101-
self.logger.error("Unable to open profile file {}".format(file))
100+
self.logger.error("Unable to open profile file {}".format(file))

microSALT/store/orm_models.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55

66
from flask_sqlalchemy import SQLAlchemy
77
from sqlalchemy import *
8-
from sqlalchemy.orm import relationship
8+
from sqlalchemy.orm import relationship, DeclarativeBase
9+
10+
class Base(DeclarativeBase):
11+
pass
912

1013
db = SQLAlchemy()
1114

@@ -56,9 +59,7 @@ class Seq_types(db.Model):
5659
__tablename__ = "seq_types"
5760
samples = relationship("Samples", back_populates="seq_types")
5861

59-
CG_ID_sample = db.Column(
60-
db.String(15), ForeignKey("samples.CG_ID_sample"), primary_key=True
61-
)
62+
CG_ID_sample = db.Column(db.String(15), ForeignKey("samples.CG_ID_sample"), primary_key=True)
6263
loci = db.Column(db.String(10), primary_key=True)
6364
allele = db.Column(db.SmallInteger)
6465
contig_name = db.Column(db.String(20), primary_key=True)
@@ -78,9 +79,7 @@ class Resistances(db.Model):
7879
__tablename__ = "resistances"
7980
samples = relationship("Samples", back_populates="resistances")
8081

81-
CG_ID_sample = db.Column(
82-
db.String(15), ForeignKey("samples.CG_ID_sample"), primary_key=True
83-
)
82+
CG_ID_sample = db.Column(db.String(15), ForeignKey("samples.CG_ID_sample"), primary_key=True)
8483
gene = db.Column(db.String(50), primary_key=True)
8584
instance = db.Column(db.String(30), primary_key=True)
8685
contig_name = db.Column(db.String(20), primary_key=True)
@@ -101,9 +100,7 @@ class Expacs(db.Model):
101100
__tablename__ = "expacs"
102101
samples = relationship("Samples", back_populates="expacs")
103102

104-
CG_ID_sample = db.Column(
105-
db.String(15), ForeignKey("samples.CG_ID_sample"), primary_key=True
106-
)
103+
CG_ID_sample = db.Column(db.String(15), ForeignKey("samples.CG_ID_sample"), primary_key=True)
107104
gene = db.Column(db.String(50), primary_key=True)
108105
instance = db.Column(db.String(30), primary_key=True)
109106
contig_name = db.Column(db.String(20), primary_key=True)
@@ -143,9 +140,7 @@ class Reports(db.Model):
143140
__tablename__ = "reports"
144141
projects = relationship("Projects", back_populates="reports")
145142

146-
CG_ID_project = db.Column(
147-
db.String(15), ForeignKey("projects.CG_ID_project"), primary_key=True
148-
)
143+
CG_ID_project = db.Column(db.String(15), ForeignKey("projects.CG_ID_project"), primary_key=True)
149144
steps_aggregate = db.Column(db.String(100))
150145
date = db.Column(db.DateTime)
151146
version = db.Column(db.Integer, default=1, primary_key=True)

microSALT/utils/reporter.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -663,7 +663,7 @@ def mail(self):
663663

664664
def run_flask(self):
665665
"""Target function for running Flask"""
666-
self.exit_flag.clear() # Reset the flag when starting
666+
self.exit_flag.clear()
667667
while not self.exit_flag.is_set():
668668
try:
669669
self.app.run(host="127.0.0.1", port=5001, debug=False, use_reloader=False)
@@ -680,18 +680,18 @@ def start_web(self):
680680
self.logger.info("Webserver already running.")
681681
return
682682

683-
self.exit_flag.clear() # Ensure flag is reset before starting
683+
self.exit_flag.clear()
684684
self.server = threading.Thread(target=self.run_flask, daemon=True)
685685
self.server.start()
686686
self.logger.info("Started webserver on http://127.0.0.1:5000/")
687-
time.sleep(0.15) # Allow time for server to initialize
687+
time.sleep(0.15)
688688

689689
def kill_flask(self):
690690
"""Stops the Flask web server."""
691691
if self.server and self.server.is_alive():
692692
self.logger.info("Closing webserver on http://127.0.0.1:5000/")
693-
self.exit_flag.set() # Signal the Flask loop to exit
694-
self.server.join(timeout=2) # Wait for the thread to exit
693+
self.exit_flag.set()
694+
self.server.join(timeout=2)
695695
self.server = None
696696

697697
def restart_web(self):

tests/conftest.py

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import json
22
import pytest
3-
import tempfile
3+
import os
44
import shutil
5+
import tempfile
56

67
from flask import Flask
78
from pathlib import Path
89
from typing import Generator
910

10-
from microSALT.cli import initialize_logger
11-
from microSALT.server.app import initialize_app, get_app
12-
from microSALT.store.database import initialize_database, drop_all_tables
13-
from microSALT.store.db_manipulator import DB_Manipulator
14-
from microSALT.utils.reporter import Reporter
11+
from microsalt.cli import initialize_logger
12+
from microsalt.server.app import initialize_app, get_app
13+
from microsalt.store.database import initialize_database, drop_all_tables
14+
from microsalt.store.db_manipulator import DB_Manipulator
15+
from microsalt.utils.reporter import Reporter
1516

1617

1718
def unpack_db_json(fixture_dir, filename):
@@ -25,6 +26,7 @@ def unpack_db_json(fixture_dir, filename):
2526
def app(config) -> Generator[Flask, None, None]:
2627
initialize_app(config)
2728
yield get_app()
29+
2830

2931

3032
@pytest.fixture(scope="session")
@@ -53,6 +55,11 @@ def temp_dirs() -> Generator[dict, None, None]:
5355
"expec": tempfile.mkdtemp(),
5456
}
5557
yield temp_dirs
58+
for path in temp_dirs.values():
59+
if os.path.isdir(path):
60+
shutil.rmtree(path)
61+
elif os.path.isfile(path):
62+
os.remove(path)
5663

5764

5865
@pytest.fixture(scope="session")
@@ -253,18 +260,15 @@ def exp_config() -> dict:
253260
}
254261

255262

256-
@pytest.fixture(scope="session")
263+
@pytest.fixture(scope="function")
257264
def reporter(
258265
app: Flask, config, dbm, logger, sampleinfo_sample, temp_dirs
259266
) -> Generator[Reporter, None, None]:
260-
reporter = Reporter(
267+
yield Reporter(
261268
app=app,
262269
config=config,
263270
dbm=dbm,
264271
log=logger,
265272
output=temp_dirs["reports"],
266273
sampleinfo=sampleinfo_sample,
267274
)
268-
reporter.start_web()
269-
yield reporter
270-
reporter.kill_flask()

0 commit comments

Comments
 (0)