Skip to content

Commit e8d51a8

Browse files
committed
Add application setup and configuration handling for microSALT
1 parent f1c8750 commit e8d51a8

File tree

8 files changed

+148
-161
lines changed

8 files changed

+148
-161
lines changed

microSALT/__init__.py

Lines changed: 0 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -1,154 +1,2 @@
1-
import collections
2-
import logging
3-
import json
4-
import os
5-
import pathlib
6-
import re
7-
import subprocess
8-
import sys
9-
10-
from flask import Flask
11-
from distutils.sysconfig import get_python_lib
121

132
__version__ = "4.1.0"
14-
15-
app = Flask(__name__, template_folder="server/templates")
16-
app.config.setdefault("SQLALCHEMY_DATABASE_URI", "sqlite:///:memory:")
17-
app.config.setdefault("SQLALCHEMY_BINDS", None)
18-
app.config.setdefault("SQLALCHEMY_TRACK_MODIFICATIONS", False)
19-
20-
# Keep track of microSALT installation
21-
wd = os.path.dirname(os.path.realpath(__file__))
22-
23-
# Load configuration
24-
preset_config = ""
25-
logger = ""
26-
default = os.path.join(os.environ["HOME"], ".microSALT/config.json")
27-
28-
if "MICROSALT_CONFIG" in os.environ:
29-
try:
30-
envvar = os.environ["MICROSALT_CONFIG"]
31-
with open(envvar, "r") as conf:
32-
preset_config = json.load(conf)
33-
except Exception as e:
34-
print("Config error: {}".format(str(e)))
35-
pass
36-
elif os.path.exists(default):
37-
try:
38-
with open(os.path.abspath(default), "r") as conf:
39-
preset_config = json.load(conf)
40-
except Exception as e:
41-
print("Config error: {}".format(str(e)))
42-
pass
43-
44-
# Config dependent section:
45-
if preset_config != "":
46-
try:
47-
# Load flask info
48-
app.config.update(preset_config["database"])
49-
50-
# Add `folders` configuration
51-
app.config["folders"] = preset_config.get("folders", {})
52-
53-
# Ensure PubMLST configuration is included
54-
55-
app.config["pubmlst"] = preset_config.get("pubmlst", {
56-
"client_id": "",
57-
"client_secret": ""
58-
})
59-
60-
app.config["pubmlst"] = preset_config.get("pubmlst", {"client_id": "", "client_secret": ""})
61-
62-
63-
# Add extrapaths to config
64-
preset_config["folders"]["expec"] = os.path.abspath(
65-
os.path.join(pathlib.Path(__file__).parent.parent, "unique_references/ExPEC.fsa")
66-
)
67-
# Check if release install exists
68-
for entry in os.listdir(get_python_lib()):
69-
if "microSALT-" in entry:
70-
preset_config["folders"]["expec"] = os.path.abspath(
71-
os.path.join(os.path.expandvars("$CONDA_PREFIX"), "expec/ExPEC.fsa")
72-
)
73-
break
74-
preset_config["folders"]["adapters"] = os.path.abspath(
75-
os.path.join(
76-
os.path.expandvars("$CONDA_PREFIX"),
77-
"share/trimmomatic/adapters/",
78-
)
79-
)
80-
81-
# Initialize logger
82-
logger = logging.getLogger("main_logger")
83-
logger.setLevel(logging.INFO)
84-
ch = logging.StreamHandler()
85-
ch.setLevel(logging.INFO)
86-
ch.setFormatter(logging.Formatter("%(levelname)s - %(message)s"))
87-
logger.addHandler(ch)
88-
89-
# Create paths mentioned in config
90-
db_file = re.search(
91-
"sqlite:///(.+)",
92-
preset_config["database"]["SQLALCHEMY_DATABASE_URI"],
93-
).group(1)
94-
for entry in preset_config.keys():
95-
if entry != "_comment":
96-
if (
97-
isinstance(preset_config[entry], str)
98-
and "/" in preset_config[entry]
99-
and entry not in ["genologics"]
100-
):
101-
if not preset_config[entry].startswith("/"):
102-
sys.exit(-1)
103-
unmade_fldr = os.path.abspath(preset_config[entry])
104-
if not pathlib.Path(unmade_fldr).exists():
105-
os.makedirs(unmade_fldr)
106-
logger.info("Created path {}".format(unmade_fldr))
107-
108-
# level two
109-
elif isinstance(preset_config[entry], collections.Mapping):
110-
for thing in preset_config[entry].keys():
111-
if (
112-
isinstance(preset_config[entry][thing], str)
113-
and "/" in preset_config[entry][thing]
114-
and entry not in ["genologics"]
115-
):
116-
# Special string, mangling
117-
if thing == "log_file":
118-
unmade_fldr = os.path.dirname(preset_config[entry][thing])
119-
bash_cmd = "touch {}".format(preset_config[entry][thing])
120-
proc = subprocess.Popen(bash_cmd.split(), stdout=subprocess.PIPE)
121-
output, error = proc.communicate()
122-
elif thing == "SQLALCHEMY_DATABASE_URI":
123-
unmade_fldr = os.path.dirname(db_file)
124-
bash_cmd = "touch {}".format(db_file)
125-
proc = subprocess.Popen(bash_cmd.split(), stdout=subprocess.PIPE)
126-
output, error = proc.communicate()
127-
if proc.returncode != 0:
128-
logger.error(
129-
"Database writing failed! Invalid user access detected!"
130-
)
131-
sys.exit(-1)
132-
else:
133-
unmade_fldr = preset_config[entry][thing]
134-
if not pathlib.Path(unmade_fldr).exists():
135-
os.makedirs(unmade_fldr)
136-
logger.info("Created path {}".format(unmade_fldr))
137-
138-
fh = logging.FileHandler(os.path.expanduser(preset_config["folders"]["log_file"]))
139-
fh.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
140-
logger.addHandler(fh)
141-
142-
# Integrity check database
143-
cmd = "sqlite3 {0}".format(db_file)
144-
cmd = cmd.split()
145-
cmd.append("pragma integrity_check;")
146-
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE)
147-
output, error = proc.communicate()
148-
if not "ok" in str(output):
149-
logger.error("Database integrity failed! Lock-state detected!")
150-
sys.exit(-1)
151-
152-
except Exception as e:
153-
print("Config error: {}".format(str(e)))
154-
pass

microSALT/app/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .app import _setup_app

microSALT/app/app.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import collections
2+
import logging
3+
import json
4+
import os
5+
from pathlib import Path
6+
import re
7+
import subprocess
8+
import sys
9+
10+
from flask import Flask
11+
from distutils.sysconfig import get_python_lib
12+
13+
from microSALT.exc.exc import MissingConfigError
14+
15+
16+
def load_config():
17+
if "MICROSALT_CONFIG" not in os.environ:
18+
raise MissingConfigError(
19+
"No config file found! Please set the environment variable MICROSALT_CONFIG to the path of the config file."
20+
)
21+
try:
22+
with open(os.environ["MICROSALT_CONFIG"], "r") as conf:
23+
return json.load(conf)
24+
except Exception as e:
25+
print(f"Config error: {str(e)}")
26+
27+
raise MissingConfigError(
28+
"No config file found! Please set the environment variable MICROSALT_CONFIG to the path of the config file."
29+
)
30+
31+
32+
def initialize_logger(preset_config):
33+
logger = logging.getLogger("main_logger")
34+
logger.setLevel(logging.INFO)
35+
ch = logging.StreamHandler()
36+
ch.setLevel(logging.INFO)
37+
ch.setFormatter(logging.Formatter("%(levelname)s - %(message)s"))
38+
logger.addHandler(ch)
39+
40+
if "folders" in preset_config and "log_file" in preset_config["folders"]:
41+
fh = logging.FileHandler(os.path.expanduser(preset_config["folders"]["log_file"]))
42+
fh.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
43+
logger.addHandler(fh)
44+
45+
return logger
46+
47+
48+
def create_path(path: str, logger: logging.Logger):
49+
if not Path(path).exists():
50+
os.makedirs(path)
51+
logger.info(f"Created path {path}")
52+
53+
54+
def handle_config_entry(entry, value, logger):
55+
if isinstance(value, str) and "/" in value and entry not in ["genologics"]:
56+
if not value.startswith("/"):
57+
sys.exit(-1)
58+
create_path(os.path.abspath(value), logger)
59+
elif isinstance(value, collections.Mapping):
60+
for sub_entry, sub_value in value.items():
61+
handle_config_entry(sub_entry, sub_value, logger)
62+
63+
64+
def create_paths(preset_config, logger):
65+
66+
for entry, value in preset_config.items():
67+
if entry == "_comment":
68+
continue
69+
handle_config_entry(entry, value, logger)
70+
71+
72+
def check_database_integrity(db_file, logger):
73+
cmd = f"sqlite3 {db_file} pragma integrity_check;"
74+
process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
75+
output, error = process.communicate()
76+
if "ok" not in str(output):
77+
logger.error("Database integrity failed! Lock-state detected!")
78+
sys.exit(-1)
79+
80+
81+
def _setup_app() -> Flask:
82+
app = Flask("microSALT", template_folder="server/templates")
83+
app.config.setdefault("SQLALCHEMY_DATABASE_URI", "sqlite:///:memory:")
84+
app.config.setdefault("SQLALCHEMY_BINDS", None)
85+
app.config.setdefault("SQLALCHEMY_TRACK_MODIFICATIONS", False)
86+
87+
# Load configuration
88+
preset_config = load_config()
89+
90+
# Config dependent section:
91+
if preset_config:
92+
try:
93+
_set_config_from_preset(app, preset_config)
94+
except Exception as e:
95+
print(f"Config error: {str(e)}")
96+
97+
return app
98+
99+
100+
def _set_config_from_preset(app: Flask, preset_config: dict):
101+
# Load flask info
102+
app.config.update(preset_config["database"])
103+
104+
# Add `folders` configuration
105+
app.config["folders"] = preset_config.get("folders", {})
106+
107+
# Ensure PubMLST configuration is included
108+
app.config["pubmlst"] = preset_config.get("pubmlst", {"client_id": "", "client_secret": ""})
109+
110+
# Add extrapaths to config
111+
preset_config["folders"]["expec"] = os.path.abspath(
112+
os.path.join(Path(__file__).parent.parent, "unique_references/ExPEC.fsa")
113+
)
114+
# Check if release install exists
115+
for entry in os.listdir(get_python_lib()):
116+
if "microSALT-" in entry:
117+
preset_config["folders"]["expec"] = os.path.abspath(
118+
os.path.join(os.path.expandvars("$CONDA_PREFIX"), "expec/ExPEC.fsa")
119+
)
120+
break
121+
preset_config["folders"]["adapters"] = os.path.abspath(
122+
os.path.join(
123+
os.path.expandvars("$CONDA_PREFIX"),
124+
"share/trimmomatic/adapters/",
125+
)
126+
)
127+
128+
# Initialize logger
129+
logger = initialize_logger(preset_config)
130+
131+
# Create paths mentioned in config
132+
create_paths(preset_config, logger)
133+
134+
# Integrity check database
135+
db_file = re.search(
136+
"sqlite:///(.+)",
137+
preset_config["database"]["SQLALCHEMY_DATABASE_URI"],
138+
)[1]
139+
check_database_integrity(db_file, logger)

microSALT/cli.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,8 @@
66
import click
77
import json
88
import os
9-
import re
10-
import subprocess
119
import sys
12-
import yaml
1310

14-
from pkg_resources import iter_entry_points
1511
from microSALT import __version__, preset_config, logger, wd
1612
from microSALT.utils.scraper import Scraper
1713
from microSALT.utils.job_creator import Job_Creator
@@ -145,7 +141,6 @@ def analyse(
145141
"""Sequence analysis, typing and resistance identification"""
146142
# Run section
147143
pool = []
148-
trimmed = not untrimmed
149144
set_cli_config(config)
150145
ctx.obj["config"]["regex"]["mail_recipient"] = email
151146
ctx.obj["config"]["dry"] = dry

microSALT/exc/__init__.py

Whitespace-only changes.

microSALT/exc/exc.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
class MissingConfigError(Exception):
3+
def __init__(self, message):
4+
super().__init__(message)

microSALT/store/orm_models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@
33

44
#!/usr/bin/env python
55

6+
from flask import Flask
67
from flask_sqlalchemy import SQLAlchemy
78
from sqlalchemy import *
89
from sqlalchemy.orm import relationship
910

10-
from microSALT import app
11+
from microSALT.app import _setup_app
1112

13+
app: Flask = _setup_app()
1214
db = SQLAlchemy(app)
1315

1416

setup.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
from microSALT import __version__
33
from setuptools import setup, find_packages
44

5-
version = __version__
6-
75
try:
86
with open("requirements.txt", "r") as f:
97
install_requires = [x.strip() for x in f.readlines()]
@@ -12,7 +10,7 @@
1210

1311
setup(
1412
name="microSALT",
15-
version=version,
13+
version=__version__,
1614
long_description=__doc__,
1715
url="https://github.com/Clinical-Genomics/microSALT",
1816
author="Isak Sylvin",

0 commit comments

Comments
 (0)