From 406d1a7a70c68311e6c94d921e3320e8c84667d8 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Sat, 18 Dec 2021 22:14:15 -0500 Subject: [PATCH 01/37] :art: Add home tab with template cards for autometa results --- automappa/figures.py | 2 +- index.py | 19 +++++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/automappa/figures.py b/automappa/figures.py index b5d322f2..1db46e87 100644 --- a/automappa/figures.py +++ b/automappa/figures.py @@ -159,7 +159,7 @@ def get_scatterplot_2d( customdata=df[metadata_cols], text=dff.index, mode="markers", - opacity=0.65, + opacity=0.85, hovertemplate=hovertemplate, name=color_col_name, ) diff --git a/index.py b/index.py index 8afdcc51..c21de29b 100755 --- a/index.py +++ b/index.py @@ -17,18 +17,18 @@ get_contig_marker_counts, ) -from apps import mag_refinement, mag_summary +from apps import home, mag_refinement, mag_summary from app import app @app.callback(Output("tab_content", "children"), [Input("tabs", "active_tab")]) def render_content(active_tab): - if active_tab == "mag_refinement": - return mag_refinement.layout - elif active_tab == "mag_summary": - return mag_summary.layout - else: - return active_tab + layouts = { + "home": home.layout, + "mag_refinement": mag_refinement.layout, + "mag_summary": mag_summary.layout, + } + return layouts.get(active_tab, "home") def main(): @@ -129,6 +129,7 @@ def main(): "Data loaded. It may take a minute or two to construct all interactive graphs..." ) + home_tab = dbc.Tab(label="Home", tab_id="home") refinement_tab = dbc.Tab(label="MAG Refinement", tab_id="mag_refinement") summary_tab = dbc.Tab(label="MAG Summary", tab_id="mag_summary") @@ -140,7 +141,9 @@ def main(): dbc.Col(contig_marker_symbols_store), # Navbar dbc.Tabs( - id="tabs", children=[refinement_tab, summary_tab], className="nav-fill" + id="tabs", + children=[home_tab, refinement_tab, summary_tab], + className="nav-fill", ), html.Div(id="tab_content"), ], From 5e05bf9c1c0c51097b3883152a3a04a66d64d533 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Sat, 18 Dec 2021 22:17:31 -0500 Subject: [PATCH 02/37] :art: Add celery task for marker gene annotation --- automappa/celery_tasks.py | 59 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 automappa/celery_tasks.py diff --git a/automappa/celery_tasks.py b/automappa/celery_tasks.py new file mode 100644 index 00000000..3a232481 --- /dev/null +++ b/automappa/celery_tasks.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + + +import os +import glob + +from autometa.common.external import hmmscan +from celery import Celery, chain + +app = Celery("tasks", broker="pyamqp://guest@localhost//") + + +@app.task +def hmmdb_formatter(hmmdb) -> None: + hmmscan.hmmpress(hmmdb) + + +@app.task +def scanner(seqfile, hmmdb, out) -> str: + # NOTE: returns outfpath + # cmd = [ + # "hmmscan", + # "--cpu", + # "1", + # "--seed", + # "42", + # "--tblout", + # out, + # hmmdb, + # seqfile + # ] + # run_cmd = " ".join(cmd) + # os.system(run_cmd) + hmmscan.run( + orfs=seqfile, + hmmdb=hmmdb, + outfpath=out, + cpus=2, + parallel=True, + seed=42, + force=True, + ) + + +if __name__ == "__main__": + hmmdb_dir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/hmms" + orfs_dir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/orfs" + outdir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/hmmscan" + if not os.path.exists(outdir) or not os.path.isdir(outdir): + os.makedirs(outdir) + for seqfile in glob.glob(os.path.join(orfs_dir, "*.orfs.faa")): + hmmdb_filename = os.path.basename(seqfile).replace(".orfs.faa", ".hmm") + hmmdb = os.path.join(hmmdb_dir, hmmdb_filename) + if not os.path.exists(hmmdb): + continue + outfilename = os.path.basename(seqfile).replace(".faa", ".hmmscan.tsv") + out = os.path.join(outdir, outfilename) + hmmdb_formatter.s(hmmdb).apply_async() + scanner.s(seqfile, hmmdb, out).apply_async(countdown=2) From 49ff515e016e19a622611aefb3a910ab4f3434a9 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Mon, 20 Dec 2021 00:16:52 -0500 Subject: [PATCH 03/37] :art: Add home tab template with example cards... --- apps/home.py | 106 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 apps/home.py diff --git a/apps/home.py b/apps/home.py new file mode 100644 index 00000000..391afc67 --- /dev/null +++ b/apps/home.py @@ -0,0 +1,106 @@ +# -*- coding: utf-8 -*- + +from dash_bootstrap_components._components.CardFooter import CardFooter +import pandas as pd +import dash_daq as daq +from dash import dcc, html +from dash.dash_table import DataTable +from dash.dependencies import Input, Output, State +from dash.exceptions import PreventUpdate +from dash_extensions import Download +from dash_extensions.snippets import send_data_frame +import dash_bootstrap_components as dbc +from app import app +from plotly import graph_objects as go +import plotly.io as pio + + +pio.templates.default = "plotly_white" + + +######################################################################## +# LAYOUT +# ###################################################################### + +# https://dash-bootstrap-components.opensource.faculty.ai/docs/components/layout/ +# For best results, make sure you adhere to the following two rules when constructing your layouts: +# +# 1. Only use Row and Col inside a Container. +# 2. The immediate children of any Row component should always be Col components. +# 3. Your content should go inside the Col components. + + +# dbc.Card() NOTE: Titles, text and links +# Use the 'card-title', 'card-subtitle', and 'card-text' classes to add margins +# and spacing that have been optimized for cards to titles, subtitles and +# text respectively. + + +example_card = dbc.Card( + [ + dbc.CardHeader("{Sample Name}"), + dbc.CardBody( + [ + html.H4("{Filename}", className="card-title"), + html.H6("Last Updated: {timestamp}", className="card-subtitle"), + html.Br(), + html.Ul( + [ + html.Li("Uploaded: {timestamp}", className="card-text"), + html.Li("Checksum: {md5sum}", className="card-text"), + ] + ), + html.Hr(), + html.H4("Autometa", className="card-subtitle"), + html.Ul( + [ + html.Li("lengths - (done)"), + html.Li("gc-content - (done)"), + html.Li("coverage - (done)"), + html.Li("markers - (in progress)"), + html.Li("taxonomy - (in progress)"), + html.Li("kmers - (queued)"), + html.Li("binning - (queued)"), + html.Li("binning-summary - (queued)"), + html.Li("CheckM - (queued)"), + html.Li("GTDB-Tk - (queued)"), + html.Li("AntiSMASH - (queued)"), + ], + className="card-text", + ), + html.Div( + [ + dbc.Button("Refine MAGs"), + dbc.Button("MAG Summary"), + ], + className="d-grid gap-2 d-md-flex justify-content-md-center", + ), + ] + ), + dbc.CardFooter("Processing Status: {status}"), + ] +) + + +card_widths = 3 +row_example_cards = dbc.Row( + [ + dbc.Col(example_card, width=card_widths), + dbc.Col(example_card, width=card_widths), + dbc.Col(example_card, width=card_widths), + dbc.Col(example_card, width=card_widths), + ] +) + +layout = dbc.Container( + children=[ + html.Br(), + row_example_cards, + html.Br(), + row_example_cards, + html.Br(), + row_example_cards, + html.Br(), + ], + fluid=True, +) From c096c3e8c965cf1afc45bd04c4d3e576522f27e0 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Wed, 6 Apr 2022 11:25:50 -0400 Subject: [PATCH 04/37] :art::whale::green_heart: Add celery related files, commands, scripts .env --- .env | 2 ++ .gitignore | 3 +- Makefile | 12 +++++++ automappa/app/assets/site.webmanifest | 2 +- automappa/{celery_tasks.py => celery.py} | 18 +++++++++-- automappa/utils/tasks.py | 0 {automappa/utils => bin}/run_celery.sh | 0 {automappa/utils => bin}/run_web.sh | 0 docker-compose.yml | 41 ++++++++++++++++++++++++ environment.yml | 1 + 10 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 .env rename automappa/{celery_tasks.py => celery.py} (81%) delete mode 100644 automappa/utils/tasks.py rename {automappa/utils => bin}/run_celery.sh (100%) rename {automappa/utils => bin}/run_web.sh (100%) create mode 100644 docker-compose.yml diff --git a/.env b/.env new file mode 100644 index 00000000..0a5fe7f7 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +REDIS_URL='redis://redis:6379/0' +BROKER_URL='amqp://admin:mypass@rabbit//' \ No newline at end of file diff --git a/.gitignore b/.gitignore index bb853153..04d04eab 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ app/__pycache__ .DS_Store dist build -Automappa.egg-info \ No newline at end of file +Automappa.egg-info +.env \ No newline at end of file diff --git a/Makefile b/Makefile index 894f13e8..e7fbeed1 100644 --- a/Makefile +++ b/Makefile @@ -49,6 +49,18 @@ image: Dockerfile install: $(PYTHON_INTERPRETER) -m pip install . --ignore-installed --no-deps -vvv +## docker compose build from docker-compose.yml +build: docker-compose.yml + docker compose build + +## docker compose up from docker-compose.yml +up: docker-compose.yml + docker compose up + +## docker compose down from docker-compose.yml +down: docker-compose.yml + docker compose down + # Run Automappa on test data # test: test_data # $(PYTHON_INTERPRETER) index.py -i test/bins.tsv diff --git a/automappa/app/assets/site.webmanifest b/automappa/app/assets/site.webmanifest index 45dc8a20..c32e1f25 100644 --- a/automappa/app/assets/site.webmanifest +++ b/automappa/app/assets/site.webmanifest @@ -1 +1 @@ -{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file +{"name":"Automappa","short_name":"automappa","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/automappa/celery_tasks.py b/automappa/celery.py similarity index 81% rename from automappa/celery_tasks.py rename to automappa/celery.py index 3a232481..398c0970 100644 --- a/automappa/celery_tasks.py +++ b/automappa/celery.py @@ -6,16 +6,28 @@ from autometa.common.external import hmmscan from celery import Celery, chain +from dotenv import load_dotenv -app = Celery("tasks", broker="pyamqp://guest@localhost//") +load_dotenv() +REDIS_URL = os.environ.get('REDIS_URL') +BROKER_URL = os.environ.get('BROKER_URL') -@app.task +CELERY = Celery('tasks', backend=REDIS_URL, broker=BROKER_URL) + + +# TODO: Data loader +# TODO: Create 2d-scatterplot figure +# TODO: Marker symbols +# TODO: CheckM annotation +# TODO: kmer freq. analysis pipeline + +@CELERY.task def hmmdb_formatter(hmmdb) -> None: hmmscan.hmmpress(hmmdb) -@app.task +@CELERY.task def scanner(seqfile, hmmdb, out) -> str: # NOTE: returns outfpath # cmd = [ diff --git a/automappa/utils/tasks.py b/automappa/utils/tasks.py deleted file mode 100644 index e69de29b..00000000 diff --git a/automappa/utils/run_celery.sh b/bin/run_celery.sh similarity index 100% rename from automappa/utils/run_celery.sh rename to bin/run_celery.sh diff --git a/automappa/utils/run_web.sh b/bin/run_web.sh similarity index 100% rename from automappa/utils/run_web.sh rename to bin/run_web.sh diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..6ef0386d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,41 @@ +version: '3' + +services: + + redis: + image: redis:latest + hostname: redis + + rabbit: + hostname: rabbit + image: rabbitmq:latest + environment: + - RABBITMQ_DEFAULT_USER=admin + - RABBITMQ_DEFAULT_PASS=mypass + + web: + build: + context: . + dockerfile: Dockerfile + hostname: web + command: automappa + volumes: + - .:/automappa + ports: + - "5000:5000" + links: + - rabbit + - redis + + worker: + build: + context: . + dockerfile: Dockerfile + command: ./automappa/utils/run_celery.sh + volumes: + - .:/automappa + links: + - rabbit + - redis + depends_on: + - rabbit diff --git a/environment.yml b/environment.yml index f7d299ec..d12fde64 100644 --- a/environment.yml +++ b/environment.yml @@ -16,3 +16,4 @@ dependencies: - numpy - pandas - plotly + - python-dotenv From a02c21218a5071daf993dbd7d86a47ad06cb8f7b Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Wed, 6 Apr 2022 11:29:48 -0400 Subject: [PATCH 05/37] :fire::art: Remove unused imports in home.py --- automappa/apps/home.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/automappa/apps/home.py b/automappa/apps/home.py index 391afc67..f353edee 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -1,17 +1,7 @@ # -*- coding: utf-8 -*- -from dash_bootstrap_components._components.CardFooter import CardFooter -import pandas as pd -import dash_daq as daq -from dash import dcc, html -from dash.dash_table import DataTable -from dash.dependencies import Input, Output, State -from dash.exceptions import PreventUpdate -from dash_extensions import Download -from dash_extensions.snippets import send_data_frame +from dash import html import dash_bootstrap_components as dbc -from app import app -from plotly import graph_objects as go import plotly.io as pio From ed8c0d1c24eb11966966658c8d7c963c11f91014 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Tue, 12 Apr 2022 08:55:01 -0400 Subject: [PATCH 06/37] :art::memo::white_check_mark::green_heart::arrow_up: Add geom_median to env, move favicon assets :memo::art: Add Bioconda project url to setup.py :art: Add toast when browser storage limits are reached (issue #11) :art::memo: Move favicon to automappa/assets :arrow_up::art: Add geom-median to environment.yml :art: Add get_clusters_geom_medians(...) to celery tasks :arrow_up::art: Bump VERSION to 2.2.0 :see_no_evil: Add data to .gitignore --- .gitignore | 3 +- VERSION | 2 +- automappa/app/assets/site.webmanifest | 1 - .../assets/android-chrome-192x192.png | Bin .../assets/android-chrome-512x512.png | Bin .../{app => }/assets/apple-touch-icon.png | Bin automappa/{app => }/assets/favicon-16x16.png | Bin automappa/{app => }/assets/favicon-32x32.png | Bin automappa/{app => }/assets/favicon.ico | Bin automappa/assets/site.webmanifest | 1 + automappa/celery.py | 39 +++++++++++++++++- automappa/index.py | 18 +++++++- automappa/utils/figures.py | 3 +- environment.yml | 6 +++ setup.py | 6 ++- 15 files changed, 71 insertions(+), 8 deletions(-) delete mode 100644 automappa/app/assets/site.webmanifest rename automappa/{app => }/assets/android-chrome-192x192.png (100%) rename automappa/{app => }/assets/android-chrome-512x512.png (100%) rename automappa/{app => }/assets/apple-touch-icon.png (100%) rename automappa/{app => }/assets/favicon-16x16.png (100%) rename automappa/{app => }/assets/favicon-32x32.png (100%) rename automappa/{app => }/assets/favicon.ico (100%) create mode 100644 automappa/assets/site.webmanifest diff --git a/.gitignore b/.gitignore index 04d04eab..ccddb6b9 100644 --- a/.gitignore +++ b/.gitignore @@ -9,4 +9,5 @@ app/__pycache__ dist build Automappa.egg-info -.env \ No newline at end of file +.env +data \ No newline at end of file diff --git a/VERSION b/VERSION index 50aea0e7..e3a4f193 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.1.0 \ No newline at end of file +2.2.0 \ No newline at end of file diff --git a/automappa/app/assets/site.webmanifest b/automappa/app/assets/site.webmanifest deleted file mode 100644 index c32e1f25..00000000 --- a/automappa/app/assets/site.webmanifest +++ /dev/null @@ -1 +0,0 @@ -{"name":"Automappa","short_name":"automappa","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/automappa/app/assets/android-chrome-192x192.png b/automappa/assets/android-chrome-192x192.png similarity index 100% rename from automappa/app/assets/android-chrome-192x192.png rename to automappa/assets/android-chrome-192x192.png diff --git a/automappa/app/assets/android-chrome-512x512.png b/automappa/assets/android-chrome-512x512.png similarity index 100% rename from automappa/app/assets/android-chrome-512x512.png rename to automappa/assets/android-chrome-512x512.png diff --git a/automappa/app/assets/apple-touch-icon.png b/automappa/assets/apple-touch-icon.png similarity index 100% rename from automappa/app/assets/apple-touch-icon.png rename to automappa/assets/apple-touch-icon.png diff --git a/automappa/app/assets/favicon-16x16.png b/automappa/assets/favicon-16x16.png similarity index 100% rename from automappa/app/assets/favicon-16x16.png rename to automappa/assets/favicon-16x16.png diff --git a/automappa/app/assets/favicon-32x32.png b/automappa/assets/favicon-32x32.png similarity index 100% rename from automappa/app/assets/favicon-32x32.png rename to automappa/assets/favicon-32x32.png diff --git a/automappa/app/assets/favicon.ico b/automappa/assets/favicon.ico similarity index 100% rename from automappa/app/assets/favicon.ico rename to automappa/assets/favicon.ico diff --git a/automappa/assets/site.webmanifest b/automappa/assets/site.webmanifest new file mode 100644 index 00000000..45dc8a20 --- /dev/null +++ b/automappa/assets/site.webmanifest @@ -0,0 +1 @@ +{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} \ No newline at end of file diff --git a/automappa/celery.py b/automappa/celery.py index 398c0970..149b683c 100644 --- a/automappa/celery.py +++ b/automappa/celery.py @@ -1,12 +1,15 @@ #!/usr/bin/env python + import os import glob +import pandas as pd -from autometa.common.external import hmmscan +from geom_median.numpy import compute_geometric_median from celery import Celery, chain from dotenv import load_dotenv +from autometa.common.external import hmmscan load_dotenv() @@ -21,6 +24,7 @@ # TODO: Marker symbols # TODO: CheckM annotation # TODO: kmer freq. analysis pipeline +# TODO: scatterplot 2-d embedding views @CELERY.task def hmmdb_formatter(hmmdb) -> None: @@ -69,3 +73,36 @@ def scanner(seqfile, hmmdb, out) -> str: out = os.path.join(outdir, outfilename) hmmdb_formatter.s(hmmdb).apply_async() scanner.s(seqfile, hmmdb, out).apply_async(countdown=2) + + +def get_clusters_geom_medians(df: pd.DataFrame, cluster_col: str = "cluster", weight_col: str='length') -> pd.DataFrame: + """Compute each cluster's (`cluster_col`) geometric median weighted by contig length (`weight_col`) + + Parameters + ---------- + df : pd.DataFrame + Table containing x_1 and x_2 coordinates and `cluster_col` from embedding + cluster_col : str, optional + Value to use for cluster column, by default "cluster" + weight_col : str, optional + Column to use for weighting the geometric median computation, by default 'length' + + Returns + ------- + pd.DataFrame + index=range(cluster_1, cluster_n), cols=[cluster_col, x_1, x_2, termination, weighted] + `x_1` and `x_2` correspond to the computed geometric median values corresponding to the respective cluster. + `termination` is the reason of termination passed from the `compute_geometric_median` function + `weighted` denotes the value that was used for weighting the cluster's geometric median (`weight_col`) + + """ + medians = [] + for cluster, dff in df.groupby(cluster_col): + points = dff[['x_1', 'x_2']].to_numpy() + if weight_col: + weights = dff[weight_col].to_numpy() + out = compute_geometric_median(points=points, weights=weights) + median = out.median + medians.append({cluster_col: cluster, "x_1": median[0], "x_2": median[1], "termination": out.termination, "weighted":weight_col}) + medians_df = pd.DataFrame(medians) + return medians_df \ No newline at end of file diff --git a/automappa/index.py b/automappa/index.py index 33fda173..23e8b3f3 100755 --- a/automappa/index.py +++ b/automappa/index.py @@ -115,12 +115,25 @@ def main(): # Check dataset size for dcc.Store(...) with browser limits... # For details see: https://stackoverflow.com/a/61018107 and https://arty.name/localstorage.html - chrome_browser_quota = 5200000 + chrome_browser_quota = 5_200_000 dataset_chars = len(binning.to_json(orient="split")) if dataset_chars >= chrome_browser_quota: - logger.warning(f"{args.binning_main} exceeds browser storage limits ({dataset_chars} > {chrome_browser_quota}).") + logger.warning(f"{args.binning_main} exceeds browser storage limits ({dataset_chars:,} > {chrome_browser_quota:,}).") logger.warning("Persisting refinements is DISABLED!") + browser_storage_toast = dbc.Toast( + f"{args.binning_main} exceeds browser storage limits ({dataset_chars:,} > {chrome_browser_quota:,}).", + id="positioned-toast", + header="Persisting refinements DISABLED", + is_open=True, + dismissable=True, + icon="danger", + # top: 66 positions the toast below the navbar + style={"position": "fixed", "top": 66, "right": 10, "width": 350}, + ) + else: + browser_storage_toast = dbc.Toast(is_open=False) + # Metagenome Annotations Store metagenome_annotations_store = dcc.Store( id="metagenome-annotations", @@ -194,6 +207,7 @@ def main(): dbc.Tabs( id="tabs", children=[refinement_tab, summary_tab], className="nav-fill" ), + html.Div(browser_storage_toast), html.Div(id="tab-content"), ], fluid=True, diff --git a/automappa/utils/figures.py b/automappa/utils/figures.py index 163405ec..6529bb99 100644 --- a/automappa/utils/figures.py +++ b/automappa/utils/figures.py @@ -6,7 +6,6 @@ from dash.exceptions import PreventUpdate from plotly import graph_objects as go - def taxonomy_sankey(df: pd.DataFrame, selected_rank: str = "species") -> go.Figure: ranks = ["superkingdom", "phylum", "class", "order", "family", "genus", "species"] n_ranks = len(ranks[: ranks.index(selected_rank)]) @@ -113,6 +112,8 @@ def marker_size_scaler(x: pd.DataFrame, scale_by: str = "length") -> int: return x_scaled + + def get_scatterplot_2d( df, x_axis: str = "x_1", diff --git a/environment.yml b/environment.yml index d12fde64..a5e49682 100644 --- a/environment.yml +++ b/environment.yml @@ -16,4 +16,10 @@ dependencies: - numpy - pandas - plotly +<<<<<<< Updated upstream - python-dotenv +======= + - pip + - pip: + - geom-median +>>>>>>> Stashed changes diff --git a/setup.py b/setup.py index 3bc64bf0..93cb75f4 100644 --- a/setup.py +++ b/setup.py @@ -20,7 +20,7 @@ def read(fname): python_requires=">=3.7", version=version, packages=find_packages(exclude=["tests"]), - package_data={"": ["app/assets/*"]}, + package_data={"": ["assets/*"]}, entry_points={ "console_scripts": [ "automappa = automappa.__main__:main", @@ -32,6 +32,10 @@ def read(fname): long_description=long_description, long_description_content_type="text/markdown", url="https://github.com/WiscEvan/Automappa", + project_urls={ + "Bug Tracker": "https://github.com/WiscEvan/Automappa/issues", + "Bioconda": "https://anaconda.org/bioconda/automappa", + }, license="GNU Affero General Public License v3 or later (AGPLv3+)", classifiers=[ "Programming Language :: Python", From 399c2f004f55b42c79ae057bcfe4c7ae85653253 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Tue, 12 Apr 2022 17:59:41 -0400 Subject: [PATCH 07/37] :art: WIP addition of home tab, celery task-queue and services (redis/rabbitmq) for task-queue --- .dockerignore | 5 +++ .env | 4 +- Dockerfile | 12 +++-- automappa/{celery.py => tasks.py} | 74 +++++++++++++++++++++---------- bin/run_celery.sh | 4 +- bin/run_web.sh | 6 +-- docker-compose.yml | 13 +++--- environment.yml | 3 -- 8 files changed, 77 insertions(+), 44 deletions(-) create mode 100644 .dockerignore rename automappa/{celery.py => tasks.py} (76%) diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..b8c7321a --- /dev/null +++ b/.dockerignore @@ -0,0 +1,5 @@ +data +test +Automappa.egg-info +build +dist \ No newline at end of file diff --git a/.env b/.env index 0a5fe7f7..4e5652b4 100644 --- a/.env +++ b/.env @@ -1,2 +1,2 @@ -REDIS_URL='redis://redis:6379/0' -BROKER_URL='amqp://admin:mypass@rabbit//' \ No newline at end of file +CELERY_REDIS_URL='redis://redis:6379/0' +CELERY_BROKER_URL='amqp://admin:mypass@rabbit//' \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 3464d373..6cb5bf80 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,9 @@ FROM condaforge/miniforge3:latest COPY environment.yml ./environment.yml -RUN conda env update -n base -f=environment.yml \ - && conda clean --all --force-pkgs-dirs --yes +RUN conda install --prune --name base mamba --yes +RUN mamba env update --name base --file=./environment.yml \ + && mamba clean --all --force-pkgs-dirs --yes # Test command is functional COPY . /Automappa/ @@ -11,5 +12,8 @@ WORKDIR /Automappa/ RUN python -m pip install . --ignore-installed --no-deps -vvv RUN automappa -h -CMD [ "-h" ] -ENTRYPOINT [ "automappa" ] \ No newline at end of file +# Create an unprivileged user for running our Python code. +RUN adduser --disabled-password --gecos '' automappa + +# CMD [ "-h" ] +# ENTRYPOINT [ "automappa" ] \ No newline at end of file diff --git a/automappa/celery.py b/automappa/tasks.py similarity index 76% rename from automappa/celery.py rename to automappa/tasks.py index 149b683c..fca8cf85 100644 --- a/automappa/celery.py +++ b/automappa/tasks.py @@ -1,23 +1,35 @@ #!/usr/bin/env python - import os import glob import pandas as pd from geom_median.numpy import compute_geometric_median +from celery.utils.log import get_task_logger from celery import Celery, chain +from celery.result import AsyncResult + from dotenv import load_dotenv + from autometa.common.external import hmmscan +from autometa.common.kmers import normalize,embed,count load_dotenv() -REDIS_URL = os.environ.get('REDIS_URL') -BROKER_URL = os.environ.get('BROKER_URL') +CELERY_REDIS_URL = os.environ.get('CELERY_REDIS_URL') +CELERY_BROKER_URL = os.environ.get('CELERY_BROKER_URL') -CELERY = Celery('tasks', backend=REDIS_URL, broker=BROKER_URL) +queue = Celery(__name__, backend=CELERY_REDIS_URL, broker=CELERY_BROKER_URL) +logger = get_task_logger(__name__) + +def get_job(job_id): + ''' + To be called from automappa web app. + The job ID is passed and the celery job is returned. + ''' + return AsyncResult(job_id, app=queue) # TODO: Data loader # TODO: Create 2d-scatterplot figure @@ -26,12 +38,26 @@ # TODO: kmer freq. analysis pipeline # TODO: scatterplot 2-d embedding views -@CELERY.task +@queue.task +def get_embedding(assembly: str, norm_method: str= "am_clr", embed_method:str="densmap"): + counts = count( + assembly=assembly, + size=5, + out=None, + force=False, + verbose=True, + cpus=1, + ) + norm_df = normalize(counts, method=norm_method) + return embed(norm_df, method=embed_method, embed_dimensions=2) + + +@queue.task def hmmdb_formatter(hmmdb) -> None: hmmscan.hmmpress(hmmdb) -@CELERY.task +@queue.task def scanner(seqfile, hmmdb, out) -> str: # NOTE: returns outfpath # cmd = [ @@ -58,23 +84,6 @@ def scanner(seqfile, hmmdb, out) -> str: ) -if __name__ == "__main__": - hmmdb_dir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/hmms" - orfs_dir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/orfs" - outdir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/hmmscan" - if not os.path.exists(outdir) or not os.path.isdir(outdir): - os.makedirs(outdir) - for seqfile in glob.glob(os.path.join(orfs_dir, "*.orfs.faa")): - hmmdb_filename = os.path.basename(seqfile).replace(".orfs.faa", ".hmm") - hmmdb = os.path.join(hmmdb_dir, hmmdb_filename) - if not os.path.exists(hmmdb): - continue - outfilename = os.path.basename(seqfile).replace(".faa", ".hmmscan.tsv") - out = os.path.join(outdir, outfilename) - hmmdb_formatter.s(hmmdb).apply_async() - scanner.s(seqfile, hmmdb, out).apply_async(countdown=2) - - def get_clusters_geom_medians(df: pd.DataFrame, cluster_col: str = "cluster", weight_col: str='length') -> pd.DataFrame: """Compute each cluster's (`cluster_col`) geometric median weighted by contig length (`weight_col`) @@ -105,4 +114,21 @@ def get_clusters_geom_medians(df: pd.DataFrame, cluster_col: str = "cluster", we median = out.median medians.append({cluster_col: cluster, "x_1": median[0], "x_2": median[1], "termination": out.termination, "weighted":weight_col}) medians_df = pd.DataFrame(medians) - return medians_df \ No newline at end of file + return medians_df + + +if __name__ == "__main__": + hmmdb_dir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/hmms" + orfs_dir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/orfs" + outdir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/hmmscan" + if not os.path.exists(outdir) or not os.path.isdir(outdir): + os.makedirs(outdir) + for seqfile in glob.glob(os.path.join(orfs_dir, "*.orfs.faa")): + hmmdb_filename = os.path.basename(seqfile).replace(".orfs.faa", ".hmm") + hmmdb = os.path.join(hmmdb_dir, hmmdb_filename) + if not os.path.exists(hmmdb): + continue + outfilename = os.path.basename(seqfile).replace(".faa", ".hmmscan.tsv") + out = os.path.join(outdir, outfilename) + hmmdb_formatter.s(hmmdb).apply_async() + scanner.s(seqfile, hmmdb, out).apply_async(countdown=2) \ No newline at end of file diff --git a/bin/run_celery.sh b/bin/run_celery.sh index 360e7934..d698a400 100644 --- a/bin/run_celery.sh +++ b/bin/run_celery.sh @@ -1,3 +1,3 @@ -#!/bin/sh +#!/usr/bin/env bash cd automappa -su -m automappa -c "celery -A tasks worker --loglevel INFO" +su -m automappa -c "celery --app=tasks worker --loglevel INFO" diff --git a/bin/run_web.sh b/bin/run_web.sh index 13e48a29..cf5dd276 100644 --- a/bin/run_web.sh +++ b/bin/run_web.sh @@ -1,5 +1,5 @@ -#!/bin/sh +#!/usr/bin/env bash + # NOTE: see https://github.com/timlardner/Docker-FlaskCeleryRabbitRedis#part-4---using-docker-to-package-our-application # for more information on this script. -cd automappa -su -m automappa -c "automappa" +su -m automappa -c "python -m automappa.index" diff --git a/docker-compose.yml b/docker-compose.yml index 6ef0386d..dd8accfd 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,8 +6,8 @@ services: image: redis:latest hostname: redis - rabbit: - hostname: rabbit + rabbitmq: + hostname: rabbitmq image: rabbitmq:latest environment: - RABBITMQ_DEFAULT_USER=admin @@ -24,18 +24,19 @@ services: ports: - "5000:5000" links: - - rabbit + - rabbitmq - redis worker: build: context: . dockerfile: Dockerfile - command: ./automappa/utils/run_celery.sh + command: celery --app=automappa.tasks.queue worker --loglevel INFO volumes: - .:/automappa links: - - rabbit + - rabbitmq - redis depends_on: - - rabbit + - rabbitmq + - redis diff --git a/environment.yml b/environment.yml index a5e49682..b0f8f51f 100644 --- a/environment.yml +++ b/environment.yml @@ -16,10 +16,7 @@ dependencies: - numpy - pandas - plotly -<<<<<<< Updated upstream - python-dotenv -======= - pip - pip: - geom-median ->>>>>>> Stashed changes From 1df246ba00849a7156143197af371bc2516c08ee Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Thu, 14 Apr 2022 12:47:44 -0400 Subject: [PATCH 08/37] :art::bug: WIP beginning of using postgres db Add TODOs for WIP next steps... To be checked before merge into develop --- .env | 7 + Makefile | 8 +- TODO.md | 11 ++ automappa/app.py | 10 +- automappa/apps/home.py | 255 +++++++++++++++++++++++++++++-- automappa/apps/mag_refinement.py | 81 ++++++---- automappa/index.py | 9 +- automappa/tasks.py | 131 +++++++++++----- automappa/utils/figures.py | 195 +++++++++++++++++++---- automappa/utils/kmers.py | 59 +++++++ automappa/utils/serializers.py | 144 +++++++++++++++++ docker-compose.yml | 58 ++++--- environment.yml | 3 + 13 files changed, 831 insertions(+), 140 deletions(-) create mode 100644 TODO.md create mode 100644 automappa/utils/kmers.py create mode 100644 automappa/utils/serializers.py diff --git a/.env b/.env index 4e5652b4..c4c6a1d8 100644 --- a/.env +++ b/.env @@ -1,2 +1,9 @@ +UPLOAD_FOLDER_ROOT="${HOME}/.automappa/uploads" +POSTGRES_USER="admin" +POSTGRES_PASSWORD="mypass" +POSTGRES_DB="automappa" +POSTGRES_URL="postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}" +RABBITMQ_DEFAULT_USER="admin" +RABBITMQ_DEFAULT_PASS="mypass" CELERY_REDIS_URL='redis://redis:6379/0' CELERY_BROKER_URL='amqp://admin:mypass@rabbit//' \ No newline at end of file diff --git a/Makefile b/Makefile index e7fbeed1..51afa4fe 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ build: docker-compose.yml ## docker compose up from docker-compose.yml up: docker-compose.yml - docker compose up + docker compose --remove-orphans up ## docker compose down from docker-compose.yml down: docker-compose.yml @@ -76,13 +76,13 @@ test_environment: scripts/test_environment.py $(PYTHON_INTERPRETER) $< ## Set up python interpreter environment -create_environment: requirements.txt +create_environment: environment.yml ifeq (True,$(HAS_CONDA)) @echo ">>> Detected conda, creating conda environment." ifeq (3,$(findstring 3,$(PYTHON_INTERPRETER))) - conda create -c conda-forge --name $(PROJECT_NAME) python=3.7 --file=$< + mamba env create --name $(PROJECT_NAME) --file=$< else - conda create -c conda-forge --name $(PROJECT_NAME) python=3.7 --file=$< + mamba env create --name $(PROJECT_NAME) --file=$< endif @echo ">>> New conda env created. Activate with:\nsource activate $(PROJECT_NAME)" else diff --git a/TODO.md b/TODO.md new file mode 100644 index 00000000..c3a7fca4 --- /dev/null +++ b/TODO.md @@ -0,0 +1,11 @@ +# TODO (WIP) + +1. [ ] :art:::racehorse: Pass postgres datatable `table_name` back from `dash-uploader` callback +2. [ ] :racehorse::art: `dcc.Store(...)` datatable id in `dcc.Store(...)` +3. [ ] :racehorse::art: Populate `dcc.Store(...)` with user's existing datatables +4. [ ] :racehorse::art: Populate `data_table.DataTable` with clickable `dcc.Link` that navigates user to `mag_refinement.py` (will load data relevant to link, e.g. metagenome annotations, markers, etc.) +5. [ ] :racehorse: Retrieve datatable within `mag_refinement.py` and `mag_summary.py` +6. [ ] :art::bug: Finish addition of other embeddings within 2D scatterplot in `mag_refinement.py` +7. [ ] :carrot::racehorse: Add task-queue for ingesting uploaded data to postgres table +8. [ ] :carrot::racehorse: Add k-mer embedding tasks to task-queue +9. [ ] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) diff --git a/automappa/app.py b/automappa/app.py index 7c718371..ecea6759 100755 --- a/automappa/app.py +++ b/automappa/app.py @@ -1,10 +1,18 @@ +import os import dash import dash_bootstrap_components as dbc +import dash_uploader as du + +from dotenv import load_dotenv + +load_dotenv() app = dash.Dash( name=__name__, title="Automappa", external_stylesheets=[dbc.themes.LUX, dbc.icons.BOOTSTRAP], update_title="Automapping...", + suppress_callback_exceptions = True, ) -app.config.suppress_callback_exceptions = True + +du.configure_upload(app, os.environ.get("UPLOAD_FOLDER_ROOT")) diff --git a/automappa/apps/home.py b/automappa/apps/home.py index f353edee..af057d6b 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -1,12 +1,34 @@ # -*- coding: utf-8 -*- +import os +import logging +from pathlib import Path from dash import html +from dash import dcc +from dash.exceptions import PreventUpdate +from dash.dependencies import Input, Output, State import dash_bootstrap_components as dbc import plotly.io as pio +import dash_uploader as du + +from automappa.app import app +from automappa.utils.serializers import ( + store_binning_main, + store_markers, + store_metagenome, +) + +logging.basicConfig( + format="[%(levelname)s] %(name)s: %(message)s", + level=logging.DEBUG, +) + +logger = logging.getLogger(__name__) pio.templates.default = "plotly_white" +UPLOAD_FOLDER_ROOT = os.environ.get("UPLOAD_FOLDER_ROOT") ######################################################################## # LAYOUT @@ -25,10 +47,95 @@ # and spacing that have been optimized for cards to titles, subtitles and # text respectively. +binning_main_upload = du.Upload( + id="upload-binning-main-data", + text="Drag and Drop or Select binning-main file", + default_style={ + "width": "100%", + "height": "60px", + "lineHeight": "60px", + "borderWidth": "1px", + "borderStyle": "dashed", + "borderRadius": "5px", + "textAlign": "center", + "margin": "10px", + }, + max_files=1, + # 10240 MB = 10GB + max_file_size=10240, +) + +markers_upload = du.Upload( + id="upload-markers-data", + text="Drag and Drop or Select marker annotations file", + default_style={ + "width": "100%", + "height": "60px", + "lineHeight": "60px", + "borderWidth": "1px", + "borderStyle": "dashed", + "borderRadius": "5px", + "textAlign": "center", + "margin": "10px", + }, + max_files=1, + # 10240 MB = 10GB + max_file_size=10240, +) + +metagenome_upload = du.Upload( + id="upload-metagenome-data", + text="Drag and Drop or Select metagenome assembly", + default_style={ + "width": "100%", + "height": "60px", + "lineHeight": "60px", + "borderWidth": "1px", + "borderStyle": "dashed", + "borderRadius": "5px", + "textAlign": "center", + "margin": "10px", + }, + max_files=1, + # 10240 MB = 10GB + max_file_size=10240, +) + +upload_modal = html.Div( + [ + dbc.Button("Upload data", id="open-dismiss"), + dbc.Modal( + [ + dbc.ModalHeader( + dbc.ModalTitle("Upload Metagenome annotations"), close_button=False + ), + dbc.ModalBody( + [ + binning_main_upload, + markers_upload, + metagenome_upload, + html.Div(id="output-binning-main-data-upload"), + html.Div(id="output-markers-data-upload"), + html.Div(id="output-metagenome-data-upload"), + ] + ), + dbc.ModalFooter( + dbc.Button( + "Close", id="close-dismiss", style={"textAlign": "center"} + ) + ), + ], + id="modal-dismiss", + keyboard=False, + backdrop="static", + fullscreen=True, + ), + ], +) example_card = dbc.Card( [ - dbc.CardHeader("{Sample Name}"), + dbc.CardHeader(id="binning-main-samples-cardheader"), dbc.CardBody( [ html.H4("{Filename}", className="card-title"), @@ -71,26 +178,154 @@ ] ) - card_widths = 3 row_example_cards = dbc.Row( [ dbc.Col(example_card, width=card_widths), - dbc.Col(example_card, width=card_widths), - dbc.Col(example_card, width=card_widths), - dbc.Col(example_card, width=card_widths), ] ) +binning_main_upload_store = dcc.Store( + id="binning-main-upload-store", storage_type="local" +) + layout = dbc.Container( children=[ + binning_main_upload_store, + dbc.Row(upload_modal), html.Br(), row_example_cards, - html.Br(), - row_example_cards, - html.Br(), - row_example_cards, - html.Br(), ], fluid=True, ) + + +@app.callback( + Output("binning-main-samples-cardheader", "children"), + [Input("binning-main-upload-store", "modified_timestamp")], + [State("binning-main-upload-store", "data")], +) +def on_binning_main_upload_store_data(timestamp, data): + if timestamp is None: + raise PreventUpdate + if data is None: + return "No samples uploaded" + print(data) + samples_count = len(data) + if samples_count and samples_count == 1: + return f"{samples_count} sample uploaded" + return f"{samples_count} samples uploaded" + + +# TODO: Add Output('store-id', 'data') s.t. upload-id respective to files are available +# for later retrieval +# Chain callback... +# Output("binning-main-upload-store", "data") +# Input('output-binning-main-data-upload', 'children'), +@app.callback( + Output('output-binning-main-data-upload', 'children'), + # Output("binning-main-upload-store", "data"), + [Input('upload-binning-main-data', 'isCompleted')], + [State('upload-binning-main-data', 'fileNames'), + State('upload-binning-main-data', 'upload_id')], +) +def on_binning_main_upload(iscompleted, filenames, upload_id): + if not iscompleted: + return + + out = [] + if filenames is not None: + if upload_id: + root_folder = Path(UPLOAD_FOLDER_ROOT) / upload_id + else: + root_folder = Path(UPLOAD_FOLDER_ROOT) + + for filename in filenames: + file = root_folder / filename + out.append(file) + if len(out) > 1: + return html.Div(f"You must only upload one file at a time! {len(out)} uploaded...") + binning_main = out[0] + session_uid = os.path.basename(os.path.dirname(binning_main)) + info = store_binning_main(binning_main, session_uid) + return info + # # return info, {upload_id: binning_main} + # binning_main_filepath = str(binning_main.resolve()) + # if uploaded_data is None or upload_id not in uploaded_data: + # uploaded_data = {upload_id: [binning_main_filepath]} + # elif upload_id in uploaded_data: + # uploaded_data[upload_id].append(binning_main_filepath) + # return uploaded_data + + return html.Div("No Files Uploaded Yet!") + +@app.callback( + Output('output-markers-data-upload', 'children'), + [Input('upload-markers-data', 'isCompleted')], + [State('upload-markers-data', 'fileNames'), + State('upload-markers-data', 'upload_id')], +) +def on_markers_upload(iscompleted, filenames, upload_id): + if not iscompleted: + return + + out = [] + if filenames is not None: + if upload_id: + root_folder = Path(UPLOAD_FOLDER_ROOT) / upload_id + else: + root_folder = Path(UPLOAD_FOLDER_ROOT) + + for filename in filenames: + file = root_folder / filename + out.append(file) + if len(out) > 1: + return html.Div(f"You must only upload one file at a time! {len(out)} uploaded...") + markers_fpath = out[0] + session_uid = os.path.basename(os.path.dirname(markers_fpath)) + info = store_markers(markers_fpath, session_uid=session_uid) + return info + + return html.Div("No Files Uploaded Yet!") + +# For information on the dash_uploader component and callbacks... +# See https://github.com/np-8/dash-uploader#example-with-callback-and-other-options +@app.callback( + Output('output-metagenome-data-upload', 'children'), + [Input('upload-metagenome-data', 'isCompleted')], + [State('upload-metagenome-data', 'fileNames'), + State('upload-metagenome-data', 'upload_id')], +) +def on_metagenome_upload(iscompleted, filenames, upload_id): + if not iscompleted: + return + + out = [] + if filenames is not None: + if upload_id: + root_folder = Path(UPLOAD_FOLDER_ROOT) / upload_id + else: + root_folder = Path(UPLOAD_FOLDER_ROOT) + + for filename in filenames: + file = root_folder / filename + out.append(file) + if len(out) > 1: + return html.Div(f"You must only upload one file at a time! {len(out)} uploaded...") + filepath = out[0] + session_uid = os.path.basename(os.path.dirname(filepath)) + info = store_metagenome(filepath, session_uid=session_uid) + return info + + return html.Div("No Files Uploaded Yet!") + + +@app.callback( + Output("modal-dismiss", "is_open"), + [Input("open-dismiss", "n_clicks"), Input("close-dismiss", "n_clicks")], + [State("modal-dismiss", "is_open")], +) +def toggle_modal(n_open, n_close, is_open): + if n_open or n_close: + return not is_open + return is_open diff --git a/automappa/apps/mag_refinement.py b/automappa/apps/mag_refinement.py index eea91316..e7d653da 100644 --- a/automappa/apps/mag_refinement.py +++ b/automappa/apps/mag_refinement.py @@ -19,6 +19,7 @@ from automappa.app import app from automappa.utils.figures import ( + format_axis_title, get_scatterplot_2d, taxonomy_sankey, get_scatterplot_3d, @@ -43,6 +44,17 @@ ), ] +# Scatterplot 2D Legend Toggle +scatterplot_2d_legend_toggle = daq.ToggleSwitch( + id="scatterplot-2d-legend-toggle", + size=40, + color="#c5040d", + label="Legend", + labelPosition="top", + vertical=False, + value=True, +) + scatterplot_2d_xaxis_dropdown = [ html.Label("X-axis:"), dcc.Dropdown( @@ -73,6 +85,17 @@ ), ] +# Scatterplot 3D Legend Toggle +scatterplot_3d_legend_toggle = daq.ToggleSwitch( + id="scatterplot-3d-legend-toggle", + size=40, + color="#c5040d", + label="Legend", + labelPosition="top", + vertical=False, + value=True, +) + scatterplot_3d_zaxis_dropdown = [ html.Label("Z-axis:"), dcc.Dropdown( @@ -103,28 +126,6 @@ ), ] -# Scatterplot 2D Legend Toggle -scatterplot_2d_legend_toggle = daq.ToggleSwitch( - id="show-legend-toggle", - size=40, - color="#c5040d", - label="Legend", - labelPosition="top", - vertical=False, - value=True, -) - -# Scatterplot 3D Legend Toggle -scatterplot_3d_legend_toggle = daq.ToggleSwitch( - id="scatterplot-3d-legend-toggle", - size=40, - color="#c5040d", - label="Legend", - labelPosition="top", - vertical=False, - value=True, -) - # Download Refinements Button binning_refinements_download_button = [ dbc.Button( @@ -541,7 +542,7 @@ def update_mag_metrics_datatable_callback( Input("contig-marker-symbols-store", "data"), Input("x-axis-2d", "value"), Input("y-axis-2d", "value"), - Input("show-legend-toggle", "value"), + Input("scatterplot-2d-legend-toggle", "value"), Input("color-by-column", "value"), Input("hide-selections-toggle", "value"), ], @@ -561,9 +562,17 @@ def scatterplot_2d_figure_callback( markers = pd.read_json(contig_marker_symbols_json, orient="split").set_index( "contig" ) - color_by_col = "phylum" if color_by_col not in bin_df.columns else color_by_col + if color_by_col not in bin_df.columns: + for col in ["phylum", "class", "order", "family"]: + if col in bin_df.columns: + color_by_col = col + break + if color_by_col not in bin_df.columns: + raise ValueError( + f"No columns were found in binning-main that could be used to group traces. {color_by_col} not found in table..." + ) + # Subset metagenome-annotations by selections iff selections have been made - bin_df[color_by_col] = bin_df[color_by_col].fillna("unclustered") if hide_selection_toggle: refine_df = pd.read_json(refinement, orient="split").set_index("contig") refine_cols = [col for col in refine_df.columns if "refinement" in col] @@ -576,14 +585,24 @@ def scatterplot_2d_figure_callback( bin_df.drop( refined_contigs_index, axis="index", inplace=True, errors="ignore" ) - + # TODO: Refactor figure s.t. updates are applied in + # batches for respective styling,layout,traces, etc. + # TODO: Put figure or traces in store, get/update/select + # based on current contig selections fig = get_scatterplot_2d( bin_df, x_axis=xaxis_column, y_axis=yaxis_column, color_by_col=color_by_col, + fillna="unclustered", ) + with fig.batch_update(): + fig.layout.xaxis.title = format_axis_title(xaxis_column) + fig.layout.yaxis.title = format_axis_title(yaxis_column) + fig.layout.legend.title = color_by_col.title() + fig.layout.showlegend = show_legend + # Update markers with symbol and size corresponding to marker count fig.for_each_trace( lambda trace: trace.update( @@ -591,7 +610,6 @@ def scatterplot_2d_figure_callback( marker_size=markers.marker_size.loc[trace.text], ) ) - fig.update_layout(showlegend=show_legend) return fig @@ -609,9 +627,9 @@ def taxonomy_distribution_figure_callback( selected_rank: str, ) -> go.Figure: df = pd.read_json(annotations, orient="split") - if selected_contigs: - ctg_list = {point["text"] for point in selected_contigs["points"]} - df = df[df.contig.isin(ctg_list)] + if selected_contigs and selected_contigs["points"]: + contigs = {point["text"] for point in selected_contigs["points"]} + df = df[df.contig.isin[contigs]] fig = taxonomy_sankey(df, selected_rank=selected_rank) return fig @@ -636,7 +654,7 @@ def scatterplot_3d_figure_callback( df = pd.read_json(annotations, orient="split") color_by_col = "phylum" if color_by_col not in df.columns else color_by_col if not selected_contigs: - contigs = df.contig.tolist() + contigs = set(df.contig.tolist()) else: contigs = {point["text"] for point in selected_contigs["points"]} # Subset DataFrame by selected contigs @@ -647,6 +665,7 @@ def scatterplot_3d_figure_callback( else: # Other possible categorical columns all relate to taxonomy df[color_by_col] = df[color_by_col].fillna("unclassified") + fig = get_scatterplot_3d( df=df, x_axis="x_1", diff --git a/automappa/index.py b/automappa/index.py index f637064a..6d235c4e 100755 --- a/automappa/index.py +++ b/automappa/index.py @@ -28,7 +28,6 @@ logger = logging.getLogger(__name__) - @app.callback(Output("tab-content", "children"), [Input("tabs", "active_tab")]) def render_content(active_tab): layouts = { @@ -118,7 +117,9 @@ def main(): chrome_browser_quota = 5_200_000 dataset_chars = len(binning.to_json(orient="split")) if dataset_chars >= chrome_browser_quota: - logger.warning(f"{args.binning_main} exceeds browser storage limits ({dataset_chars:,} > {chrome_browser_quota:,}).") + logger.warning( + f"{args.binning_main} exceeds browser storage limits ({dataset_chars:,} > {chrome_browser_quota:,})." + ) logger.warning("Persisting refinements is DISABLED!") browser_storage_toast = dbc.Toast( @@ -185,7 +186,9 @@ def main(): ) if args.clear_store_data: - logger.info(f"Store data cleared. Now re-run automappa *without* --clear-store-data") + logger.info( + f"Store data cleared. Now re-run automappa *without* --clear-store-data" + ) exit() logger.info(f"binning shape:\t\t{binning.shape}") diff --git a/automappa/tasks.py b/automappa/tasks.py index fca8cf85..3869d52f 100644 --- a/automappa/tasks.py +++ b/automappa/tasks.py @@ -13,24 +13,26 @@ from dotenv import load_dotenv from autometa.common.external import hmmscan -from autometa.common.kmers import normalize,embed,count +from autometa.common.kmers import normalize, embed, count load_dotenv() -CELERY_REDIS_URL = os.environ.get('CELERY_REDIS_URL') -CELERY_BROKER_URL = os.environ.get('CELERY_BROKER_URL') +CELERY_REDIS_URL = os.environ.get("CELERY_REDIS_URL") +CELERY_BROKER_URL = os.environ.get("CELERY_BROKER_URL") queue = Celery(__name__, backend=CELERY_REDIS_URL, broker=CELERY_BROKER_URL) logger = get_task_logger(__name__) + def get_job(job_id): - ''' + """ To be called from automappa web app. The job ID is passed and the celery job is returned. - ''' + """ return AsyncResult(job_id, app=queue) + # TODO: Data loader # TODO: Create 2d-scatterplot figure # TODO: Marker symbols @@ -38,8 +40,11 @@ def get_job(job_id): # TODO: kmer freq. analysis pipeline # TODO: scatterplot 2-d embedding views + @queue.task -def get_embedding(assembly: str, norm_method: str= "am_clr", embed_method:str="densmap"): +def get_embedding( + assembly: str, norm_method: str = "am_clr", embed_method: str = "densmap" +): counts = count( assembly=assembly, size=5, @@ -52,39 +57,42 @@ def get_embedding(assembly: str, norm_method: str= "am_clr", embed_method:str="d return embed(norm_df, method=embed_method, embed_dimensions=2) -@queue.task -def hmmdb_formatter(hmmdb) -> None: - hmmscan.hmmpress(hmmdb) +# @queue.task +# def hmmdb_formatter(hmmdb) -> None: +# hmmscan.hmmpress(hmmdb) + + +# @queue.task +# def scanner(seqfile, hmmdb, out) -> str: +# # NOTE: returns outfpath +# # cmd = [ +# # "hmmscan", +# # "--cpu", +# # "1", +# # "--seed", +# # "42", +# # "--tblout", +# # out, +# # hmmdb, +# # seqfile +# # ] +# # run_cmd = " ".join(cmd) +# # os.system(run_cmd) +# hmmscan.run( +# orfs=seqfile, +# hmmdb=hmmdb, +# outfpath=out, +# cpus=2, +# parallel=True, +# seed=42, +# force=True, +# ) @queue.task -def scanner(seqfile, hmmdb, out) -> str: - # NOTE: returns outfpath - # cmd = [ - # "hmmscan", - # "--cpu", - # "1", - # "--seed", - # "42", - # "--tblout", - # out, - # hmmdb, - # seqfile - # ] - # run_cmd = " ".join(cmd) - # os.system(run_cmd) - hmmscan.run( - orfs=seqfile, - hmmdb=hmmdb, - outfpath=out, - cpus=2, - parallel=True, - seed=42, - force=True, - ) - - -def get_clusters_geom_medians(df: pd.DataFrame, cluster_col: str = "cluster", weight_col: str='length') -> pd.DataFrame: +def get_clusters_geom_medians( + df: pd.DataFrame, cluster_col: str = "cluster", weight_col: str = "length" +) -> pd.DataFrame: """Compute each cluster's (`cluster_col`) geometric median weighted by contig length (`weight_col`) Parameters @@ -107,16 +115,61 @@ def get_clusters_geom_medians(df: pd.DataFrame, cluster_col: str = "cluster", we """ medians = [] for cluster, dff in df.groupby(cluster_col): - points = dff[['x_1', 'x_2']].to_numpy() + points = dff[["x_1", "x_2"]].to_numpy() if weight_col: weights = dff[weight_col].to_numpy() out = compute_geometric_median(points=points, weights=weights) median = out.median - medians.append({cluster_col: cluster, "x_1": median[0], "x_2": median[1], "termination": out.termination, "weighted":weight_col}) + medians.append( + { + cluster_col: cluster, + "x_1": median[0], + "x_2": median[1], + "termination": out.termination, + "weighted": weight_col, + } + ) medians_df = pd.DataFrame(medians) return medians_df +@queue.task +def get_embedding_traces_df(df: pd.DataFrame) -> pd.DataFrame: + # 1. Compute all embeddings for assembly... + # 2. groupby cluster + # 3. Extract k-mer size, norm method, embed method + embedding_fpaths = glob.glob("data/nubbins/kmers/*5mers*am_clr.*2.tsv.gz") + embeddings = [] + for fp in embedding_fpaths: + df = pd.read_csv(fp, sep="\t", index_col="contig") + basename = os.path.basename(fp) + mers, norm_method, embed_method_dim, *__ = basename.split(".") + match = re.match("(\w+)(\d+)", embed_method_dim) + if match: + embed_method, embed_dim = match.groups() + df.rename( + columns={ + "x_1": f"{embed_method}_x_1", + "x_2": f"{embed_method}_x_2", + }, + inplace=True, + ) + embeddings.append(df) + embeddings_df = pd.concat(embeddings, axis=1) + + df = pd.read_csv("data/nubbins/nubbins.tsv", sep="\t") + main_df = df.drop(columns=["x_1", "x_2"]).set_index("contig").join(embeddings_df) + embed_traces = [] + for embed_method in ["trimap", "densmap", "bhsne", "umap", "sksne"]: + traces_df = get_scattergl_traces( + df, f"{embed_method}_x_1", f"{embed_method}_x_2", "cluster" + ) + traces_df.rename(columns={"trace": embed_method}, inplace=True) + embed_traces.append(traces_df) + embed_traces_df = pd.concat(embed_traces, axis=1) + return embed_traces_df + + if __name__ == "__main__": hmmdb_dir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/hmms" orfs_dir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/orfs" @@ -131,4 +184,4 @@ def get_clusters_geom_medians(df: pd.DataFrame, cluster_col: str = "cluster", we outfilename = os.path.basename(seqfile).replace(".faa", ".hmmscan.tsv") out = os.path.join(outdir, outfilename) hmmdb_formatter.s(hmmdb).apply_async() - scanner.s(seqfile, hmmdb, out).apply_async(countdown=2) \ No newline at end of file + scanner.s(seqfile, hmmdb, out).apply_async(countdown=2) diff --git a/automappa/utils/figures.py b/automappa/utils/figures.py index ff95e1fa..0b676cbc 100644 --- a/automappa/utils/figures.py +++ b/automappa/utils/figures.py @@ -1,11 +1,12 @@ #!/usr/bin/env python -from typing import List, Union +from typing import Dict, List, Tuple, Union import numpy as np import pandas as pd from dash.exceptions import PreventUpdate from plotly import graph_objects as go + def taxonomy_sankey(df: pd.DataFrame, selected_rank: str = "species") -> go.Figure: ranks = ["superkingdom", "phylum", "class", "order", "family", "genus", "species"] n_ranks = len(ranks[: ranks.index(selected_rank)]) @@ -13,7 +14,9 @@ def taxonomy_sankey(df: pd.DataFrame, selected_rank: str = "species") -> go.Figu for rank in ranks: if rank in dff: dff[rank] = dff[rank].map( - lambda x: f"{rank[0]}_{x}" if rank != "superkingdom" else f"d_{x}" + lambda taxon: f"{rank[0]}_{taxon}" + if rank != "superkingdom" + else f"d_{taxon}" ) label = [] for rank in ranks[:n_ranks]: @@ -112,32 +115,41 @@ def marker_size_scaler(x: pd.DataFrame, scale_by: str = "length") -> int: return x_scaled +def format_axis_title(axis_title: str) -> str: + """Format axis title depending on title text. Converts embed methods to uppercase then x_dim. + Parameters + ---------- + axis_title : str + axis title to format (used from `xaxis_column` and `yaxis_column` in `scatterplot_2d_figure_callback`) -def get_scatterplot_2d( - df, - x_axis: str = "x_1", - y_axis: str = "x_2", - color_by_col: str = "cluster", -) -> go.Figure: - fig = go.Figure( - layout=go.Layout( - scene=dict( - xaxis=dict(title=x_axis.title()), - yaxis=dict(title=y_axis.title()), - ), - legend=dict(x=1, y=1), - margin=dict(r=50, b=50, l=50, t=50), - hovermode="closest", - ), - ) + Returns + ------- + str + formatted axis title + """ + if "_x_" in axis_title: + col_list = axis_title.split("_") + embed_method = col_list[0] + embed_dim = "_".join(col_list[1:]) + formatted_axis_title = f"{embed_method.upper()} {embed_dim}" + else: + formatted_axis_title = axis_title.title() + return formatted_axis_title + + +def get_hovertemplate_and_customdata_cols( + x_axis: str, y_axis: str +) -> Tuple[str, List[str]]: # Hovertemplate - x_hover_label = f"{x_axis.title()}: " + "%{x:.2f}" - y_hover_label = f"{y_axis.title()}: " + "%{y:.2f}" + x_hover_title = format_axis_title(x_axis) + y_hover_title = format_axis_title(y_axis) + text_hover_label = "Contig: %{text}" coverage_label = "Coverage: %{customdata[0]:.2f}" gc_content_label = "GC%: %{customdata[1]:.2f}" length_label = "Length: %{customdata[2]:,} bp" - text_hover_label = "Contig: %{text}" + x_hover_label = f"{x_hover_title}: " + "%{x:.2f}" + y_hover_label = f"{y_hover_title}: " + "%{y:.2f}" hovertemplate = "
".join( [ text_hover_label, @@ -148,22 +160,151 @@ def get_scatterplot_2d( y_hover_label, ] ) - metadata_cols = ["coverage", "gc_content", "length"] - for color_col_name in df[color_by_col].unique(): + return hovertemplate, metadata_cols + + +def get_scattergl_traces( + df: pd.DataFrame, + x_axis: str, + y_axis: str, + color_by_col: str = "cluster", + fillna: str = "unclustered", +) -> pd.DataFrame: + """Generate scattergl 2D traces from `df` with index of `contig`, x and y corresponding to `x_axis` and `y_axis`, respectively with traces + being grouped by the `color_by_col`. If there exists `nan` values in the `color_by_col`, these may be filled with the value used in `fillna`. + + Parameters + ---------- + df : pd.DataFrame + * `index` = `contigs` + + binning table of columns: + + * f`{embed_method}_x_1` + * f`{embed_method}_x_2` + * `gc_content` + * `coverage` + * `length` + * `colory_by_col` (commonly used column is `cluster`) + + x_axis : str + column to use to supply to the `x` argument in `Scattergl(x=...)` + y_axis : str + column to use to supply to the `y` argument in `Scattergl(y=...)` + color_by_col : str, by default "cluster" + Column with which to group the traces + fillna : str, optional + value to replace `nan` in `color_by_col`, by default "unclustered" + + Returns + ------- + pd.DataFrame + index=`color_by_col`, column=`trace` + """ + hovertemplate, metadata_cols = get_hovertemplate_and_customdata_cols( + x_axis=x_axis, y_axis=y_axis + ) + traces = [] + for color_col_name in df[color_by_col].fillna(fillna).unique(): dff = df.loc[df[color_by_col].eq(color_col_name)] + customdata = dff[metadata_cols] if metadata_cols in dff.columns else [] trace = go.Scattergl( x=dff[x_axis], y=dff[y_axis], - customdata=dff[metadata_cols], + customdata=customdata, text=dff.index, mode="markers", opacity=0.85, hovertemplate=hovertemplate, name=color_col_name, ) - fig.add_trace(trace) - fig.update_layout(legend_title_text=color_by_col.title()) + traces.append({color_by_col: color_col_name, "trace": trace}) + return pd.DataFrame(traces).set_index(color_by_col) + + +def get_embedding_traces_df(df: pd.DataFrame) -> pd.DataFrame: + # 1. Compute all embeddings for assembly... + # 2. groupby cluster + # 3. Extract k-mer size, norm method, embed method + embedding_fpaths = glob.glob("data/nubbins/kmers/*5mers*am_clr.*2.tsv.gz") + embeddings = [] + for fp in embedding_fpaths: + df = pd.read_csv(fp, sep="\t", index_col="contig") + basename = os.path.basename(fp) + mers, norm_method, embed_method_dim, *__ = basename.split(".") + match = re.match("(\w+)(\d+)", embed_method_dim) + if match: + embed_method, embed_dim = match.groups() + df.rename( + columns={ + "x_1": f"{embed_method}_x_1", + "x_2": f"{embed_method}_x_2", + }, + inplace=True, + ) + embeddings.append(df) + embeddings_df = pd.concat(embeddings, axis=1) + + df = pd.read_csv("data/nubbins/nubbins.tsv", sep="\t") + main_df = df.drop(columns=["x_1", "x_2"]).set_index("contig").join(embeddings_df) + embed_traces = [] + for embed_method in ["trimap", "densmap", "bhsne", "umap", "sksne"]: + traces_df = get_scattergl_traces( + df, f"{embed_method}_x_1", f"{embed_method}_x_2", "cluster" + ) + traces_df.rename(columns={"trace": embed_method}, inplace=True) + embed_traces.append(traces_df) + embed_traces_df = pd.concat(embed_traces, axis=1) + return embed_traces_df + + +def get_scatterplot_2d( + df: pd.DataFrame, + x_axis: str, + y_axis: str, + embed_method: str, + color_by_col: str = "cluster", + fillna: str = "unclustered", +) -> go.Figure: + """Generate `go.Figure` of scattergl 2D traces + + Parameters + ---------- + df : pd.DataFrame + _description_ + x_axis : str + _description_ + y_axis : str + _description_ + embed_method : str + _description_ + color_by_col : str, optional + _description_, by default "cluster" + fillna : str, optional + _description_, by default "unclustered" + + Returns + ------- + go.Figure + _description_ + """ + layout = go.Layout( + legend=dict(x=1, y=1), + margin=dict(r=50, b=50, l=50, t=50), + hovermode="closest", + clickmode="event+select", + ) + fig = go.Figure(layout=layout) + traces_df = get_scattergl_traces( + df, + x_axis=x_axis, + y_axis=y_axis, + color_by_col=color_by_col, + fillna=fillna, + ) + # TODO: Update function to use embed_traces_df... + fig.add_traces(traces_df.trace.tolist()) return fig diff --git a/automappa/utils/kmers.py b/automappa/utils/kmers.py new file mode 100644 index 00000000..fb9ca31e --- /dev/null +++ b/automappa/utils/kmers.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +import glob +import os +import re + +# from autometa.common.kmers import count, normalize, embed, load +import pandas as pd +from plotly import graph_objects as go + +from automappa.utils.figures import get_scattergl_traces + + +embedding_fpaths = glob.glob("data/nubbins/kmers/*5mers*am_clr.*2.tsv.gz") + +embeddings = [] +for fp in embedding_fpaths: + df = pd.read_csv(fp, sep="\t", index_col="contig") + basename = os.path.basename(fp) + mers, norm_method, embed_method_dim, *__ = basename.split(".") + match = re.match("(\w+)(\d+)", embed_method_dim) + if match: + embed_method, embed_dim = match.groups() + df.rename( + columns={ + "x_1": f"{embed_method}_x_1", + "x_2": f"{embed_method}_x_2", + }, + inplace=True, + ) + embeddings.append(df) +embeddings_df = pd.concat(embeddings, axis=1) + +df = pd.read_csv("data/nubbins/nubbins.tsv", sep="\t") +main_df = df.drop(columns=["x_1", "x_2"]).set_index("contig").join(embeddings_df) + +embed_traces = [] +for embed_method in ["trimap", "densmap", "bhsne", "umap", "sksne"]: + traces_df = get_scattergl_traces( + main_df, f"{embed_method}_x_1", f"{embed_method}_x_2", "cluster" + ) + traces_df.rename(columns={"trace": embed_method}, inplace=True) + embed_traces.append(traces_df) + +traces = pd.concat(embed_traces, axis=1) + +fig = go.Figure() +fig.add_traces(traces.umap.tolist()) +# Update according to selected embed_method... +embed_method = "densmap" +fig.for_each_trace(lambda trace: trace.update(traces.loc[trace.name, embed_method])) +embed_method = "trimap" +fig.for_each_trace(lambda trace: trace.update(traces.loc[trace.name, embed_method])) +embed_method = "umap" +fig.for_each_trace(lambda trace: trace.update(traces.loc[trace.name, embed_method])) +embed_method = "sksne" +fig.for_each_trace(lambda trace: trace.update(traces.loc[trace.name, embed_method])) +embed_method = "bhsne" +fig.for_each_trace(lambda trace: trace.update(traces.loc[trace.name, embed_method])) diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py new file mode 100644 index 00000000..56802b05 --- /dev/null +++ b/automappa/utils/serializers.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python + +import os +import logging +import base64 +from datetime import datetime +import io +import pandas as pd + +from dash import html + +import psycopg2 +from sqlalchemy import create_engine + +from autometa.common.markers import load as load_markers +from autometa.common.metagenome import Metagenome + +logging.basicConfig( + format="[%(levelname)s] %(name)s: %(message)s", + level=logging.DEBUG, +) + +logger = logging.getLogger(__name__) + + + +POSTGRES_URL = os.environ.get("POSTGRES_URL") + +def parse_contig_annotations(contents: str, filename: str) -> pd.DataFrame: + content_type, content_string = contents.split(",") + decoded = base64.b64decode(content_string) + df = pd.DataFrame() + if "csv" in filename: + # Assume that the user uploaded a CSV file + df = pd.read_csv(io.StringIO(decoded.decode("utf-8"))) + elif "xls" in filename: + # Assume that the user uploaded an excel file + df = pd.read_excel(io.BytesIO(decoded)) + elif ".tsv" in filename: + # Assume that the user uploaded an excel file + df = pd.read_csv( + io.StringIO(decoded.decode("utf-8")), + sep="\t", + ) + if "contig" not in df.columns: + raise ValueError(f"contig not in {df.columns}") + if df.empty: + raise ValueError(f"{filename} is empty!") + return df + + +def store_binning_main(filepath: str, session_uid: str) -> html.Div: + try: + df = pd.read_csv(filepath, sep="\t") + engine = create_engine(url=POSTGRES_URL, echo=False) + table_name = f"{session_uid}-binning" + df.to_sql(table_name, engine, if_exists='replace', index=False) + logger.debug(f"{filepath} ({df.shape[0]:,} contigs) saved to datatable {table_name}") + except Exception as err: + logger.error(err) + return html.Div(["There was an error processing this file."]) + filename = os.path.basename(filepath) + timestamp = os.path.getmtime(filepath) + last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") + return html.Div( + [ + html.H5("binning-main annotations uploaded:"), + html.H6(f"filename: {filename}"), + html.H6(f"last modified: {last_modified}"), + html.H6(f"contigs: {df.shape[0]:,}, columns: {df.shape[1]}"), + html.Pre(f"table_name: {table_name}"), + ] + ) + +def read_binning_main(session_uid: str)->pd.DataFrame: + table_name = f"{session_uid}-binning" + return retrieve_datatable(table_name) + +def retrieve_datatable(table_name: str) -> pd.DataFrame: + engine = create_engine(url=POSTGRES_URL, echo=False) + if not engine.has_table(table_name): + tables = engine.table_names() + raise ValueError(f"{table_name} not in postgres database! available: {tables}") + engine.table_names() + df = pd.read_sql(table_name, engine).set_index("contig") + logger.debug(f"retrieved {df.shape[0]} contigs from {table_name} datatable") + return df + +def store_markers(filepath: str, session_uid: str) -> html.Div: + try: + df = load_markers(filepath) + engine = create_engine(url=POSTGRES_URL, echo=False) + table_name = f"{session_uid}-markers" + df.to_sql(table_name, engine, if_exists='replace', index=True) + logger.debug(f"{filepath} ({df.shape[0]:,} contigs) saved to datatable {table_name}") + except Exception as err: + logger.error(err) + return html.Div(["There was an error processing this file."]) + timestamp = os.path.getmtime(filepath) + last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") + filename = os.path.basename(filepath) + return html.Div( + [ + html.H5("markers annotations uploaded:"), + html.H6(f"filename: {filename}"), + html.H6(f"last modified: {last_modified}"), + html.H6(f"contigs: {df.shape[0]:,}, markers: {df.shape[1]}"), + html.Pre(f"table_name: {table_name}"), + ] + ) + + +def store_metagenome(filepath: str, session_uid: str) -> html.Div: + try: + metagenome = Metagenome(assembly=filepath) + logger.debug(f"{filepath} uploaded... saving to datatable...") + df = pd.DataFrame( + [{"contig":seqrecord.id, "sequence":str(seqrecord.seq)} for seqrecord in metagenome.seqrecords] + ) + engine = create_engine(url=POSTGRES_URL, echo=False) + table_name = f"{session_uid}-metagenome-seqrecords" + df.to_sql(table_name, engine, if_exists='replace', index=False) + logger.debug(f"{filepath} ({df.shape[0]:,} contigs) saved to datatable {table_name}") + except Exception as err: + logger.error(err) + return html.Div(["There was an error processing this file."]) + filename = os.path.basename(filepath) + timestamp = os.path.getmtime(filepath) + last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") + # TODO: Store parsed data + tables = engine.table_names() + return html.Div( + [ + html.H5("metagenome assembly uploaded:"), + html.H6(f"filename: {filename}"), + html.H6(f"last modified: {last_modified}"), + html.H6(f"nseqs: {metagenome.nseqs:,}, size: {metagenome.size:,} (bp)"), + html.Pre(f"all tables in pgdb: {', '.join(tables)}"), + ] + ) + + +if __name__ == "__main__": + pass diff --git a/docker-compose.yml b/docker-compose.yml index dd8accfd..4baab0ad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,17 +2,24 @@ version: '3' services: - redis: - image: redis:latest - hostname: redis - - rabbitmq: - hostname: rabbitmq - image: rabbitmq:latest - environment: - - RABBITMQ_DEFAULT_USER=admin - - RABBITMQ_DEFAULT_PASS=mypass + # redis: + # image: redis:latest + # hostname: redis + + postgres: + image: postgres + env_file: + - .env + ports: + - 5432:5432 + volumes: + - ./db-data/:/var/lib/postgresql/data/ + # rabbitmq: + # hostname: rabbitmq + # image: rabbitmq:latest + # env_file: + # - .env web: build: context: . @@ -24,19 +31,20 @@ services: ports: - "5000:5000" links: - - rabbitmq - - redis + # - rabbitmq + # - redis + - postgres - worker: - build: - context: . - dockerfile: Dockerfile - command: celery --app=automappa.tasks.queue worker --loglevel INFO - volumes: - - .:/automappa - links: - - rabbitmq - - redis - depends_on: - - rabbitmq - - redis + # worker: + # build: + # context: . + # dockerfile: Dockerfile + # command: celery --app=automappa.tasks.queue worker --loglevel INFO + # volumes: + # - .:/automappa + # links: + # - rabbitmq + # - redis + # depends_on: + # - rabbitmq + # - redis diff --git a/environment.yml b/environment.yml index b0f8f51f..91958ad8 100644 --- a/environment.yml +++ b/environment.yml @@ -16,7 +16,10 @@ dependencies: - numpy - pandas - plotly + - psycopg2 - python-dotenv + - sqlalchemy - pip - pip: - geom-median + - dash-uploader From 8a54e1252b5b7352194253e2817956dc642ebbad Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Mon, 18 Apr 2022 18:39:27 -0400 Subject: [PATCH 09/37] Refactor callback to viz DataTable and store samples-store State :art::bug: Prevent samples-store data from being overwritten on initial callback :art: samples-store data now persistent with local session :art: Refactor some variables in MAG refinement :art: WIP --- .gitignore | 3 +- Makefile | 2 +- TODO.md | 6 +- automappa/app.py | 2 +- automappa/apps/home.py | 327 +++++++++++++++++++++---------- automappa/apps/mag_refinement.py | 4 +- automappa/index.py | 209 ++++++++++---------- automappa/utils/serializers.py | 255 +++++++++++++++--------- docker-compose.yml | 28 +-- 9 files changed, 515 insertions(+), 321 deletions(-) diff --git a/.gitignore b/.gitignore index ccddb6b9..eb4e08d5 100644 --- a/.gitignore +++ b/.gitignore @@ -10,4 +10,5 @@ dist build Automappa.egg-info .env -data \ No newline at end of file +data +db-data \ No newline at end of file diff --git a/Makefile b/Makefile index 51afa4fe..f5ef8cde 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ build: docker-compose.yml ## docker compose up from docker-compose.yml up: docker-compose.yml - docker compose --remove-orphans up + docker compose up --remove-orphans ## docker compose down from docker-compose.yml down: docker-compose.yml diff --git a/TODO.md b/TODO.md index c3a7fca4..2191f86a 100644 --- a/TODO.md +++ b/TODO.md @@ -1,11 +1,11 @@ # TODO (WIP) -1. [ ] :art:::racehorse: Pass postgres datatable `table_name` back from `dash-uploader` callback -2. [ ] :racehorse::art: `dcc.Store(...)` datatable id in `dcc.Store(...)` +1. [x] :art:::racehorse: Pass postgres datatable `table_name` back from `dash-uploader` callback +2. [x] :racehorse::art: `dcc.Store(...)` datatable id in `dcc.Store(...)` 3. [ ] :racehorse::art: Populate `dcc.Store(...)` with user's existing datatables 4. [ ] :racehorse::art: Populate `data_table.DataTable` with clickable `dcc.Link` that navigates user to `mag_refinement.py` (will load data relevant to link, e.g. metagenome annotations, markers, etc.) 5. [ ] :racehorse: Retrieve datatable within `mag_refinement.py` and `mag_summary.py` 6. [ ] :art::bug: Finish addition of other embeddings within 2D scatterplot in `mag_refinement.py` 7. [ ] :carrot::racehorse: Add task-queue for ingesting uploaded data to postgres table 8. [ ] :carrot::racehorse: Add k-mer embedding tasks to task-queue -9. [ ] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) +9. [x] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) diff --git a/automappa/app.py b/automappa/app.py index ecea6759..a5cc4895 100755 --- a/automappa/app.py +++ b/automappa/app.py @@ -12,7 +12,7 @@ title="Automappa", external_stylesheets=[dbc.themes.LUX, dbc.icons.BOOTSTRAP], update_title="Automapping...", - suppress_callback_exceptions = True, + suppress_callback_exceptions=True, ) du.configure_upload(app, os.environ.get("UPLOAD_FOLDER_ROOT")) diff --git a/automappa/apps/home.py b/automappa/apps/home.py index af057d6b..3a3c7dc4 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -1,19 +1,22 @@ # -*- coding: utf-8 -*- +from datetime import datetime import os import logging from pathlib import Path -from dash import html -from dash import dcc +from dash import html, dcc +from dash.dash_table import DataTable from dash.exceptions import PreventUpdate from dash.dependencies import Input, Output, State import dash_bootstrap_components as dbc +import pandas as pd import plotly.io as pio import dash_uploader as du from automappa.app import app from automappa.utils.serializers import ( + convert_bytes, store_binning_main, store_markers, store_metagenome, @@ -114,9 +117,6 @@ binning_main_upload, markers_upload, metagenome_upload, - html.Div(id="output-binning-main-data-upload"), - html.Div(id="output-markers-data-upload"), - html.Div(id="output-metagenome-data-upload"), ] ), dbc.ModalFooter( @@ -133,6 +133,10 @@ ], ) +# html.Div(id="output-binning-main-data-upload"), +html.Div(id="output-markers-data-upload"), +html.Div(id="output-metagenome-data-upload"), + example_card = dbc.Card( [ dbc.CardHeader(id="binning-main-samples-cardheader"), @@ -188,136 +192,261 @@ binning_main_upload_store = dcc.Store( id="binning-main-upload-store", storage_type="local" ) +markers_upload_store = dcc.Store(id="markers-upload-store", storage_type="local") +metagenome_upload_store = dcc.Store(id="metagenome-upload-store", storage_type="local") +samples_store = dcc.Store(id="samples-store", storage_type="local") + +# samples_datatable = html.Div(id="samples-datatable") +samples_datatable = dcc.Loading( + id="loading-samples-datatable", + children=[html.Div(id="samples-datatable")], + type="dot", + color="#646569", + ), layout = dbc.Container( children=[ binning_main_upload_store, + markers_upload_store, + metagenome_upload_store, + samples_store, dbc.Row(upload_modal), html.Br(), - row_example_cards, + # row_example_cards, + dbc.Row(samples_datatable), ], fluid=True, ) @app.callback( - Output("binning-main-samples-cardheader", "children"), - [Input("binning-main-upload-store", "modified_timestamp")], - [State("binning-main-upload-store", "data")], + Output('samples-store', 'data'), + [ + Input("binning-main-upload-store", "modified_timestamp"), + Input("markers-upload-store", "modified_timestamp"), + Input("metagenome-upload-store", "modified_timestamp"), + ], + [ + State("binning-main-upload-store", "data"), + State("markers-upload-store", "data"), + State("metagenome-upload-store", "data"), + State('samples-store', 'data'), + ], ) -def on_binning_main_upload_store_data(timestamp, data): - if timestamp is None: +def on_binning_main_upload_store_data( + binning_uploads_timestamp, + markers_uploads_timestamp, + metagenome_uploads_timestamp, + binning_uploads, + markers_uploads, + metagenome_uploads, + samples_store_data, +): + if ( + binning_uploads is None + and markers_uploads is None + and metagenome_uploads is None + ): + raise PreventUpdate + if ( + binning_uploads_timestamp is None + and markers_uploads_timestamp is None + and metagenome_uploads_timestamp is None + ): raise PreventUpdate - if data is None: - return "No samples uploaded" - print(data) - samples_count = len(data) - if samples_count and samples_count == 1: - return f"{samples_count} sample uploaded" - return f"{samples_count} samples uploaded" - - -# TODO: Add Output('store-id', 'data') s.t. upload-id respective to files are available -# for later retrieval -# Chain callback... -# Output("binning-main-upload-store", "data") -# Input('output-binning-main-data-upload', 'children'), + # We need to ensure we prevent an update if there has not been one, otherwise all of our datastore + # gets removed... + binning_samples_df = pd.read_json(binning_uploads, orient="split") if binning_uploads else pd.DataFrame() + marker_samples_df = pd.read_json(markers_uploads, orient="split") if markers_uploads else pd.DataFrame() + metagenome_samples_df = pd.read_json(metagenome_uploads, orient="split") if metagenome_uploads else pd.DataFrame() + samples_store_df = pd.read_json(samples_store_data, orient='split') if samples_store_data else pd.DataFrame() + samples_df = pd.concat( + [ + samples_store_df, + binning_samples_df, + marker_samples_df, + metagenome_samples_df, + ] + ).drop_duplicates(subset=["table_id"]) + + logger.debug(f"{samples_df.shape[0]:,} samples retrieved from data upload stores") + + return samples_df.to_json(orient='split') + @app.callback( - Output('output-binning-main-data-upload', 'children'), - # Output("binning-main-upload-store", "data"), - [Input('upload-binning-main-data', 'isCompleted')], - [State('upload-binning-main-data', 'fileNames'), - State('upload-binning-main-data', 'upload_id')], + Output("samples-datatable", "children"), + [Input('samples-store', 'data')], + State('samples-store', 'data'), +) +def on_samples_store_data(samples_store_data, new_samples_store_data): + # if samples_store_data is None: + # # Show upload button...? + # raise PreventUpdate + + # import pdb;pdb.set_trace() + + samples_df = pd.read_json(samples_store_data, orient='split') + if new_samples_store_data is not None: + new_samples_df = pd.read_json(new_samples_store_data, orient='split') + samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates(subset=['table_id']) + + logger.debug(f"retrieved {samples_df.shape[0]:,} samples from samples store") + + if samples_df.empty: + raise PreventUpdate + + return DataTable( + data=samples_df.to_dict("records"), + columns=[{"id": col, "name": col, "editable": False} for col in samples_df.columns], + + ) + + +@app.callback( + Output("binning-main-upload-store", "data"), + [Input("upload-binning-main-data", "isCompleted")], + [ + State("upload-binning-main-data", "fileNames"), + State("upload-binning-main-data", "upload_id"), + ], ) def on_binning_main_upload(iscompleted, filenames, upload_id): if not iscompleted: return + if filenames is None: + return + if upload_id: + root_folder = Path(UPLOAD_FOLDER_ROOT) / upload_id + else: + root_folder = Path(UPLOAD_FOLDER_ROOT) + + uploaded_files = [] + for filename in filenames: + file = root_folder / filename + uploaded_files.append(file) + + if len(uploaded_files) > 1: + logger.error("You may only upload one file at a time!") + raise PreventUpdate + + filepath = uploaded_files[0] + filename = os.path.basename(filepath) + unit = "MB" + filesize = convert_bytes(os.path.getsize(filepath), unit) + timestamp = os.path.getmtime(filepath) + last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") + table_id = store_binning_main(filepath) + + return pd.DataFrame( + [ + { + "filetype": "binning_main", + "filename": filename, + f"filesize ({unit})": filesize, + "table_id": table_id, + "uploaded": last_modified, + "timestamp": timestamp, + } + ] + ).to_json(orient="split") - out = [] - if filenames is not None: - if upload_id: - root_folder = Path(UPLOAD_FOLDER_ROOT) / upload_id - else: - root_folder = Path(UPLOAD_FOLDER_ROOT) - - for filename in filenames: - file = root_folder / filename - out.append(file) - if len(out) > 1: - return html.Div(f"You must only upload one file at a time! {len(out)} uploaded...") - binning_main = out[0] - session_uid = os.path.basename(os.path.dirname(binning_main)) - info = store_binning_main(binning_main, session_uid) - return info - # # return info, {upload_id: binning_main} - # binning_main_filepath = str(binning_main.resolve()) - # if uploaded_data is None or upload_id not in uploaded_data: - # uploaded_data = {upload_id: [binning_main_filepath]} - # elif upload_id in uploaded_data: - # uploaded_data[upload_id].append(binning_main_filepath) - # return uploaded_data - - return html.Div("No Files Uploaded Yet!") @app.callback( - Output('output-markers-data-upload', 'children'), - [Input('upload-markers-data', 'isCompleted')], - [State('upload-markers-data', 'fileNames'), - State('upload-markers-data', 'upload_id')], + Output("markers-upload-store", "data"), + [Input("upload-markers-data", "isCompleted")], + [ + State("upload-markers-data", "fileNames"), + State("upload-markers-data", "upload_id"), + ], ) def on_markers_upload(iscompleted, filenames, upload_id): if not iscompleted: return + if filenames is None: + return + if upload_id: + root_folder = Path(UPLOAD_FOLDER_ROOT) / upload_id + else: + root_folder = Path(UPLOAD_FOLDER_ROOT) + + uploaded_files = [] + for filename in filenames: + file = root_folder / filename + uploaded_files.append(file) + if len(uploaded_files) > 1: + logger.error("You may only upload one file at a time!") + raise PreventUpdate + + filepath = uploaded_files[0] + filename = os.path.basename(filepath) + unit = "MB" + filesize = convert_bytes(os.path.getsize(filepath), unit) + timestamp = os.path.getmtime(filepath) + last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") + table_id = store_markers(filepath) + + return pd.DataFrame( + [ + { + "filetype": "markers", + "filename": filename, + f"filesize ({unit})": filesize, + "timestamp": timestamp, + "uploaded": last_modified, + "table_id": table_id, + } + ] + ).to_json(orient="split") - out = [] - if filenames is not None: - if upload_id: - root_folder = Path(UPLOAD_FOLDER_ROOT) / upload_id - else: - root_folder = Path(UPLOAD_FOLDER_ROOT) - - for filename in filenames: - file = root_folder / filename - out.append(file) - if len(out) > 1: - return html.Div(f"You must only upload one file at a time! {len(out)} uploaded...") - markers_fpath = out[0] - session_uid = os.path.basename(os.path.dirname(markers_fpath)) - info = store_markers(markers_fpath, session_uid=session_uid) - return info - - return html.Div("No Files Uploaded Yet!") # For information on the dash_uploader component and callbacks... # See https://github.com/np-8/dash-uploader#example-with-callback-and-other-options @app.callback( - Output('output-metagenome-data-upload', 'children'), - [Input('upload-metagenome-data', 'isCompleted')], - [State('upload-metagenome-data', 'fileNames'), - State('upload-metagenome-data', 'upload_id')], + Output("metagenome-upload-store", "data"), + [Input("upload-metagenome-data", "isCompleted")], + [ + State("upload-metagenome-data", "fileNames"), + State("upload-metagenome-data", "upload_id"), + ], ) def on_metagenome_upload(iscompleted, filenames, upload_id): if not iscompleted: return + if filenames is None: + return + if upload_id: + root_folder = Path(UPLOAD_FOLDER_ROOT) / upload_id + else: + root_folder = Path(UPLOAD_FOLDER_ROOT) + + uploaded_files = [] + for filename in filenames: + file = root_folder / filename + uploaded_files.append(file) + if len(uploaded_files) > 1: + logger.error("You may only upload one file at a time!") + raise PreventUpdate - out = [] - if filenames is not None: - if upload_id: - root_folder = Path(UPLOAD_FOLDER_ROOT) / upload_id - else: - root_folder = Path(UPLOAD_FOLDER_ROOT) - - for filename in filenames: - file = root_folder / filename - out.append(file) - if len(out) > 1: - return html.Div(f"You must only upload one file at a time! {len(out)} uploaded...") - filepath = out[0] - session_uid = os.path.basename(os.path.dirname(filepath)) - info = store_metagenome(filepath, session_uid=session_uid) - return info - - return html.Div("No Files Uploaded Yet!") + filepath = uploaded_files[0] + filename = os.path.basename(filepath) + unit = "MB" + filesize = convert_bytes(os.path.getsize(filepath), unit) + timestamp = os.path.getmtime(filepath) + last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") + table_id = store_metagenome(filepath) + + return pd.DataFrame( + [ + { + "filetype": "metagenome", + "filename": filename, + f"filesize ({unit})": filesize, + "timestamp": timestamp, + "uploaded": last_modified, + "table_id": table_id, + } + ] + ).to_json(orient="split") @app.callback( diff --git a/automappa/apps/mag_refinement.py b/automappa/apps/mag_refinement.py index e7d653da..1c982ed7 100644 --- a/automappa/apps/mag_refinement.py +++ b/automappa/apps/mag_refinement.py @@ -739,8 +739,8 @@ def mag_summary_length_boxplot_callback( Output("refinements-table", "children"), [Input("refinement-data", "data")], ) -def refinements_table_callback(df: "str | None") -> DataTable: - df = pd.read_json(df, orient="split") +def refinements_table_callback(refinement_store_data: "str | None") -> DataTable: + df = pd.read_json(refinement_store_data, orient="split") return DataTable( data=df.to_dict("records"), columns=[{"name": col, "id": col} for col in df.columns], diff --git a/automappa/index.py b/automappa/index.py index 6d235c4e..874db22c 100755 --- a/automappa/index.py +++ b/automappa/index.py @@ -43,27 +43,27 @@ def main(): description="Automappa: An interactive interface for exploration of metagenomes", formatter_class=argparse.RawTextHelpFormatter, ) - parser.add_argument( - "--binning-main", - help="Path to --binning-main output of Autometa binning/recruitment results", - type=str, - metavar="filepath", - required=True, - ) - parser.add_argument( - "--markers", - help="Path to Autometa-formatted markers table (may be taxon-specific)", - type=str, - metavar="filepath", - required=True, - ) - parser.add_argument( - "--fasta", - help="Path to metagenome.fasta", - type=str, - metavar="filepath", - required=False, - ) + # parser.add_argument( + # "--binning-main", + # help="Path to --binning-main output of Autometa binning/recruitment results", + # type=str, + # metavar="filepath", + # required=True, + # ) + # parser.add_argument( + # "--markers", + # help="Path to Autometa-formatted markers table (may be taxon-specific)", + # type=str, + # metavar="filepath", + # required=True, + # ) + # parser.add_argument( + # "--fasta", + # help="Path to metagenome.fasta", + # type=str, + # metavar="filepath", + # required=False, + # ) parser.add_argument( "--port", help="port to expose. (default: %(default)s)", @@ -106,84 +106,84 @@ def main(): ) args = parser.parse_args() - logger.info("Please wait a moment while all of the data is loaded.") + # logger.info("Please wait a moment while all of the data is loaded.") # Needed separately for binning refinement selections. - binning = pd.read_csv(args.binning_main, sep="\t", low_memory=False) + # binning = pd.read_csv(args.binning_main, sep="\t", low_memory=False) # Needed for completeness/purity calculations - markers = load_markers(args.markers).reset_index().copy() + # markers = load_markers(args.markers).reset_index().copy() # Check dataset size for dcc.Store(...) with browser limits... # For details see: https://stackoverflow.com/a/61018107 and https://arty.name/localstorage.html - chrome_browser_quota = 5_200_000 - dataset_chars = len(binning.to_json(orient="split")) - if dataset_chars >= chrome_browser_quota: - logger.warning( - f"{args.binning_main} exceeds browser storage limits ({dataset_chars:,} > {chrome_browser_quota:,})." - ) - logger.warning("Persisting refinements is DISABLED!") - - browser_storage_toast = dbc.Toast( - f"{args.binning_main} exceeds browser storage limits ({dataset_chars:,} > {chrome_browser_quota:,}).", - id="positioned-toast", - header="Persisting refinements DISABLED", - is_open=True, - dismissable=True, - icon="danger", - # top: 66 positions the toast below the navbar - style={"position": "fixed", "top": 66, "right": 10, "width": 350}, - ) - else: - browser_storage_toast = dbc.Toast(is_open=False) + # chrome_browser_quota = 5_200_000 + # dataset_chars = len(binning.to_json(orient="split")) + # if dataset_chars >= chrome_browser_quota: + # logger.warning( + # f"{args.binning_main} exceeds browser storage limits ({dataset_chars:,} > {chrome_browser_quota:,})." + # ) + # logger.warning("Persisting refinements is DISABLED!") + + # browser_storage_toast = dbc.Toast( + # f"{args.binning_main} exceeds browser storage limits ({dataset_chars:,} > {chrome_browser_quota:,}).", + # id="positioned-toast", + # header="Persisting refinements DISABLED", + # is_open=True, + # dismissable=True, + # icon="danger", + # # top: 66 positions the toast below the navbar + # style={"position": "fixed", "top": 66, "right": 10, "width": 350}, + # ) + # else: + # browser_storage_toast = dbc.Toast(is_open=False) # Metagenome Annotations Store - metagenome_annotations_store = dcc.Store( - id="metagenome-annotations", - storage_type=args.storage_type, - data=binning.to_json(orient="split"), - clear_data=args.clear_store_data, - ) - - # Kingdom Markers Store - markers_store = dcc.Store( - id="markers-store", - storage_type=args.storage_type, - data=markers.to_json(orient="split"), - clear_data=args.clear_store_data, - ) - # MAG Refinement Data Store - # NOTE: MAG refinement columns are enumerated (1-indexed) and prepended with 'refinement_' - if "cluster" not in binning.columns: - binning["cluster"] = "unclustered" - else: - binning["cluster"].fillna("unclustered", inplace=True) - - binning_cols = [ - col - for col in binning.columns - if "refinement_" in col or "cluster" in col or "contig" in col - ] - - refinement_data_store = dcc.Store( - id="refinement-data", - storage_type=args.storage_type, - data=binning[binning_cols].to_json(orient="split"), - clear_data=args.clear_store_data, - ) - - # Contig Marker Symbols Store - contig_marker_counts = get_contig_marker_counts( - binning.set_index("contig"), markers.set_index("contig") - ) - contig_marker_symbols = convert_marker_counts_to_marker_symbols( - contig_marker_counts - ).reset_index() - - contig_marker_symbols_store = dcc.Store( - id="contig-marker-symbols-store", - storage_type=args.storage_type, - data=contig_marker_symbols.to_json(orient="split"), - clear_data=args.clear_store_data, - ) + # metagenome_annotations_store = dcc.Store( + # id="metagenome-annotations", + # storage_type=args.storage_type, + # data=binning.to_json(orient="split"), + # clear_data=args.clear_store_data, + # ) + + # # Kingdom Markers Store + # markers_store = dcc.Store( + # id="markers-store", + # storage_type=args.storage_type, + # data=markers.to_json(orient="split"), + # clear_data=args.clear_store_data, + # ) + # # MAG Refinement Data Store + # # NOTE: MAG refinement columns are enumerated (1-indexed) and prepended with 'refinement_' + # if "cluster" not in binning.columns: + # binning["cluster"] = "unclustered" + # else: + # binning["cluster"].fillna("unclustered", inplace=True) + + # binning_cols = [ + # col + # for col in binning.columns + # if "refinement_" in col or "cluster" in col or "contig" in col + # ] + + # refinement_data_store = dcc.Store( + # id="refinement-data", + # storage_type=args.storage_type, + # data=binning[binning_cols].to_json(orient="split"), + # clear_data=args.clear_store_data, + # ) + + # # Contig Marker Symbols Store + # contig_marker_counts = get_contig_marker_counts( + # binning.set_index("contig"), markers.set_index("contig") + # ) + # contig_marker_symbols = convert_marker_counts_to_marker_symbols( + # contig_marker_counts + # ).reset_index() + + # contig_marker_symbols_store = dcc.Store( + # id="contig-marker-symbols-store", + # storage_type=args.storage_type, + # data=contig_marker_symbols.to_json(orient="split"), + # clear_data=args.clear_store_data, + # ) if args.clear_store_data: logger.info( @@ -191,11 +191,11 @@ def main(): ) exit() - logger.info(f"binning shape:\t\t{binning.shape}") - logger.info(f"markers shape:\t\t{markers.shape}") - logger.info( - "Data loaded. It may take a minute or two to construct all interactive graphs..." - ) + # logger.info(f"binning shape:\t\t{binning.shape}") + # logger.info(f"markers shape:\t\t{markers.shape}") + # logger.info( + # "Data loaded. It may take a minute or two to construct all interactive graphs..." + # ) home_tab = dbc.Tab(label="Home", tab_id="home") refinement_tab = dbc.Tab(label="MAG Refinement", tab_id="mag_refinement") @@ -203,17 +203,17 @@ def main(): app.layout = dbc.Container( [ - dbc.Col(markers_store), - dbc.Col(metagenome_annotations_store), - dbc.Col(refinement_data_store), - dbc.Col(contig_marker_symbols_store), + # dbc.Col(markers_store), + # dbc.Col(metagenome_annotations_store), + # dbc.Col(refinement_data_store), + # dbc.Col(contig_marker_symbols_store), # Navbar dbc.Tabs( id="tabs", children=[home_tab, refinement_tab, summary_tab], className="nav-fill", ), - html.Div(browser_storage_toast), + # html.Div(browser_storage_toast), html.Div(id="tab-content"), ], fluid=True, @@ -222,8 +222,9 @@ def main(): # TODO: Replace cli inputs (as well as updating title once file is uploaded...) # dcc.Upload(id='metagenome-annotations-upload', children=dbc.Button("Upload annotations")) # dcc.Upload(id='markers-upload', children=dbc.Button("Upload annotations")) - sample_name = os.path.basename(args.binning_main).replace(" ", "_").split(".")[0] - app.title = f"Automappa: {sample_name}" + # sample_name = os.path.basename(args.binning_main).replace(" ", "_").split(".")[0] + # app.title = f"Automappa: {sample_name}" + app.title = "Automappa" app.run_server(host=args.host, port=args.port, debug=args.debug) diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py index 56802b05..f860a04c 100644 --- a/automappa/utils/serializers.py +++ b/automappa/utils/serializers.py @@ -2,17 +2,15 @@ import os import logging -import base64 -from datetime import datetime -import io +from pathlib import Path +from typing import List import pandas as pd -from dash import html -import psycopg2 from sqlalchemy import create_engine from autometa.common.markers import load as load_markers +from autometa.common.utilities import calc_checksum from autometa.common.metagenome import Metagenome logging.basicConfig( @@ -23,122 +21,187 @@ logger = logging.getLogger(__name__) - POSTGRES_URL = os.environ.get("POSTGRES_URL") -def parse_contig_annotations(contents: str, filename: str) -> pd.DataFrame: - content_type, content_string = contents.split(",") - decoded = base64.b64decode(content_string) - df = pd.DataFrame() - if "csv" in filename: - # Assume that the user uploaded a CSV file - df = pd.read_csv(io.StringIO(decoded.decode("utf-8"))) - elif "xls" in filename: - # Assume that the user uploaded an excel file - df = pd.read_excel(io.BytesIO(decoded)) - elif ".tsv" in filename: - # Assume that the user uploaded an excel file - df = pd.read_csv( - io.StringIO(decoded.decode("utf-8")), - sep="\t", - ) - if "contig" not in df.columns: - raise ValueError(f"contig not in {df.columns}") - if df.empty: - raise ValueError(f"{filename} is empty!") - return df +def get_uploaded_datatables() -> List[str]: + engine = create_engine(url=POSTGRES_URL, echo=False) + tables = engine.table_names() + return tables -def store_binning_main(filepath: str, session_uid: str) -> html.Div: - try: - df = pd.read_csv(filepath, sep="\t") - engine = create_engine(url=POSTGRES_URL, echo=False) - table_name = f"{session_uid}-binning" - df.to_sql(table_name, engine, if_exists='replace', index=False) - logger.debug(f"{filepath} ({df.shape[0]:,} contigs) saved to datatable {table_name}") - except Exception as err: - logger.error(err) - return html.Div(["There was an error processing this file."]) - filename = os.path.basename(filepath) - timestamp = os.path.getmtime(filepath) - last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") - return html.Div( - [ - html.H5("binning-main annotations uploaded:"), - html.H6(f"filename: {filename}"), - html.H6(f"last modified: {last_modified}"), - html.H6(f"contigs: {df.shape[0]:,}, columns: {df.shape[1]}"), - html.Pre(f"table_name: {table_name}"), - ] - ) - -def read_binning_main(session_uid: str)->pd.DataFrame: - table_name = f"{session_uid}-binning" - return retrieve_datatable(table_name) - -def retrieve_datatable(table_name: str) -> pd.DataFrame: +def get_datatable(table_name: str) -> pd.DataFrame: engine = create_engine(url=POSTGRES_URL, echo=False) if not engine.has_table(table_name): tables = engine.table_names() raise ValueError(f"{table_name} not in postgres database! available: {tables}") - engine.table_names() df = pd.read_sql(table_name, engine).set_index("contig") logger.debug(f"retrieved {df.shape[0]} contigs from {table_name} datatable") return df -def store_markers(filepath: str, session_uid: str) -> html.Div: + +def convert_bytes(size: int, unit: str="MB", ndigits: int= 2) -> float: + """Convert bytes from os.path.getsize(...) to provided `unit` + choices include: 'KB', 'MB' or 'GB' + + Parameters + ---------- + size : int + bytes returned from `os.path.getsize(...)` + unit : str, optional + size to convert from bytes, by default MB + + Returns + ------- + float + Converted bytes value + """ + # Yoinked from + # https://amiradata.com/python-get-file-size-in-kb-mb-or-gb/#Get_file_size_in_KiloBytes_MegaBytes_or_GigaBytes + if unit == "KB": + return round(size / 1024, ndigits) + elif unit == "MB": + return round(size / (1024 * 1024), ndigits) + elif unit == "GB": + return round(size / (1024 * 1024 * 1024), ndigits) + else: + return size + + +def store_binning_main(filepath: Path, if_exists: str = "replace") -> str: + """Parse `filepath` into `pd.DataFrame` then save to postgres table + + `table_id` is composed of 2 pieces: + + 1. md5 checksum of 'filepath' + 2. binning + + e.g. `'{checksum}-binning'` + + Parameters + ---------- + filepath : Path + Path to binning.main.tsv Autometa results + if_exists : str + {'fail', 'replace', 'append'}, by default 'replace' + How to behave if the table already exists. + + Returns + ------- + str + table_id = postgres table id for retrieving stored data (AKA name of SQL table) + e.g. `'{checksum}-binning'` + """ try: + # Construct table name + checksum = calc_checksum(str(filepath)).split()[0] + table_name = f"{checksum}-binning" + # Read filepath contents + df = pd.read_csv(filepath, sep="\t") + # Create postgres connection then write table to DB + engine = create_engine(url=POSTGRES_URL, echo=False) + # NOTE: table_name must not exceed maximum length of 63 characters + df.to_sql(table_name, engine, if_exists=if_exists, index=False) + logger.debug(f"Saved {df.shape[0]:,} contigs to postgres table: {table_name}") + except Exception as err: + logger.error(err) + logger.error("There was an error processing this file.") + table_name = "" + + return table_name + + + +def store_markers(filepath: Path, if_exists: str = "replace") -> str: + """Parse `filepath` into `pd.DataFrame` then save to postgres table + + `table_id` is composed of 2 pieces: + + 1. md5 checksum of 'filepath' + 2. binning + + e.g. `'{checksum}-markers'` + + Parameters + ---------- + filepath : Path + Path to markers.tsv Autometa results + if_exists : str + {'fail', 'replace', 'append'}, by default 'replace' + How to behave if the table already exists. + + Returns + ------- + str + table_id = postgres table id for retrieving stored data (AKA name of SQL table) + e.g. `'{checksum}-markers'` + """ + try: + # Construct table name + checksum = calc_checksum(str(filepath)).split()[0] + table_name = f"{checksum}-markers" + # Read filepath contents df = load_markers(filepath) + # Create postgres connection then write table to DB engine = create_engine(url=POSTGRES_URL, echo=False) - table_name = f"{session_uid}-markers" - df.to_sql(table_name, engine, if_exists='replace', index=True) - logger.debug(f"{filepath} ({df.shape[0]:,} contigs) saved to datatable {table_name}") + # NOTE: table_name must not exceed maximum length of 63 characters + df.to_sql(table_name, engine, if_exists=if_exists, index=True) + logger.debug(f"Saved {df.shape[0]:,} contigs and {df.shape[1]:,} markers to postgres table: {table_name}") except Exception as err: logger.error(err) - return html.Div(["There was an error processing this file."]) - timestamp = os.path.getmtime(filepath) - last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") - filename = os.path.basename(filepath) - return html.Div( - [ - html.H5("markers annotations uploaded:"), - html.H6(f"filename: {filename}"), - html.H6(f"last modified: {last_modified}"), - html.H6(f"contigs: {df.shape[0]:,}, markers: {df.shape[1]}"), - html.Pre(f"table_name: {table_name}"), - ] - ) - - -def store_metagenome(filepath: str, session_uid: str) -> html.Div: + logger.error("There was an error processing this file.") + table_name = "" + + return table_name + +def store_metagenome(filepath: Path, if_exists: str = "replace") -> str: + """Parse `filepath` into `pd.DataFrame` then save to postgres table + + `table_id` is composed of 2 pieces: + + 1. md5 checksum of 'filepath' + 2. metagenome + + e.g. `'{checksum}-metagenome'` + + Parameters + ---------- + filepath : Path + Path to metagenome.main.tsv Autometa results + if_exists : str + {'fail', 'replace', 'append'}, by default 'replace' + How to behave if the table already exists. + + Returns + ------- + str + table_id = postgres table id for retrieving stored data (AKA name of SQL table) + e.g. `'{checksum}-metagenome'` + """ try: + # Read filepath contents + logger.debug(f"{filepath} uploaded... converting for datatable...") metagenome = Metagenome(assembly=filepath) - logger.debug(f"{filepath} uploaded... saving to datatable...") df = pd.DataFrame( - [{"contig":seqrecord.id, "sequence":str(seqrecord.seq)} for seqrecord in metagenome.seqrecords] + [ + {"contig": seqrecord.id, "sequence": str(seqrecord.seq)} + for seqrecord in metagenome.seqrecords + ] ) + logger.debug(f"converted... saving to datatable...") + # Create postgres connection then write table to DB engine = create_engine(url=POSTGRES_URL, echo=False) - table_name = f"{session_uid}-metagenome-seqrecords" - df.to_sql(table_name, engine, if_exists='replace', index=False) - logger.debug(f"{filepath} ({df.shape[0]:,} contigs) saved to datatable {table_name}") + # NOTE: table_name must not exceed maximum length of 63 characters + # Construct table name + checksum = calc_checksum(str(filepath)).split()[0] + table_name = f"{checksum}-metagenome" + df.to_sql(table_name, engine, if_exists=if_exists, index=False) + logger.debug(f"Saved {df.shape[0]:,} contigs to postgres table: {table_name}") except Exception as err: logger.error(err) - return html.Div(["There was an error processing this file."]) - filename = os.path.basename(filepath) - timestamp = os.path.getmtime(filepath) - last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") - # TODO: Store parsed data - tables = engine.table_names() - return html.Div( - [ - html.H5("metagenome assembly uploaded:"), - html.H6(f"filename: {filename}"), - html.H6(f"last modified: {last_modified}"), - html.H6(f"nseqs: {metagenome.nseqs:,}, size: {metagenome.size:,} (bp)"), - html.Pre(f"all tables in pgdb: {', '.join(tables)}"), - ] - ) + logger.error("There was an error processing this file.") + table_name = "" + return table_name if __name__ == "__main__": pass diff --git a/docker-compose.yml b/docker-compose.yml index 4baab0ad..ebab0c19 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -20,20 +20,20 @@ services: # image: rabbitmq:latest # env_file: # - .env - web: - build: - context: . - dockerfile: Dockerfile - hostname: web - command: automappa - volumes: - - .:/automappa - ports: - - "5000:5000" - links: - # - rabbitmq - # - redis - - postgres + # web: + # build: + # context: . + # dockerfile: Dockerfile + # hostname: web + # command: automappa + # volumes: + # - .:/automappa + # ports: + # - "5000:5000" + # links: + # # - rabbitmq + # # - redis + # - postgres # worker: # build: From 7b5f2d3b5fa4dbdc4d14c21f7ea2fbd1c94eca52 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Mon, 18 Apr 2022 18:40:11 -0400 Subject: [PATCH 10/37] :memo: Add resource to README in utils for working with postgresql serialization --- automappa/utils/README.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 automappa/utils/README.md diff --git a/automappa/utils/README.md b/automappa/utils/README.md new file mode 100644 index 00000000..2e02516b --- /dev/null +++ b/automappa/utils/README.md @@ -0,0 +1,5 @@ +# Automappa utils.py + +## Miscellaneous resources related to Automappa Utils + +- [Mapping between python and postgresql types](https://pynative.com/python-postgresql-tutorial/#h-the-mapping-between-python-and-postgresql-types "Mapping between python and postgresql types") From 991a45f5e00403082711f68e9f644c5022d48f8d Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Wed, 20 Apr 2022 19:15:28 -0400 Subject: [PATCH 11/37] :art::whale::fire: Changes for setup in docker-compose... (WIP) --- .dockerignore | 2 +- Makefile | 2 +- TODO.md | 56 ++++++++++++++++++++--- automappa/apps/home.py | 83 ++++++++++++++++++---------------- automappa/db.py | 11 +++++ automappa/settings.py | 55 ++++++++++++++++++++++ automappa/utils/serializers.py | 26 ++++------- docker-compose.yml | 33 +++++++++----- environment.yml | 1 + 9 files changed, 192 insertions(+), 77 deletions(-) create mode 100644 automappa/db.py create mode 100644 automappa/settings.py diff --git a/.dockerignore b/.dockerignore index b8c7321a..7b9126c0 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,4 +2,4 @@ data test Automappa.egg-info build -dist \ No newline at end of file +dist diff --git a/Makefile b/Makefile index f5ef8cde..95604ebe 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ build: docker-compose.yml ## docker compose up from docker-compose.yml up: docker-compose.yml - docker compose up --remove-orphans + docker-compose up --always-recreate-deps --remove-orphans --force-recreate ## docker compose down from docker-compose.yml down: docker-compose.yml diff --git a/TODO.md b/TODO.md index 2191f86a..ee0ef405 100644 --- a/TODO.md +++ b/TODO.md @@ -2,10 +2,52 @@ 1. [x] :art:::racehorse: Pass postgres datatable `table_name` back from `dash-uploader` callback 2. [x] :racehorse::art: `dcc.Store(...)` datatable id in `dcc.Store(...)` -3. [ ] :racehorse::art: Populate `dcc.Store(...)` with user's existing datatables -4. [ ] :racehorse::art: Populate `data_table.DataTable` with clickable `dcc.Link` that navigates user to `mag_refinement.py` (will load data relevant to link, e.g. metagenome annotations, markers, etc.) -5. [ ] :racehorse: Retrieve datatable within `mag_refinement.py` and `mag_summary.py` -6. [ ] :art::bug: Finish addition of other embeddings within 2D scatterplot in `mag_refinement.py` -7. [ ] :carrot::racehorse: Add task-queue for ingesting uploaded data to postgres table -8. [ ] :carrot::racehorse: Add k-mer embedding tasks to task-queue -9. [x] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) +3. [ ] :racehorse::art: Save uploaded file metadata to postgres datatable +4. [ ] :racehorse::art: Populate `dcc.Store(...)` with user's existing datatables +5. [ ] :racehorse::art: Populate `data_table.DataTable` with clickable `dcc.Link` that navigates user to `mag_refinement.py` (will load data relevant to link, e.g. metagenome annotations, markers, etc.) +6. [ ] :racehorse: Retrieve datatable within `mag_refinement.py` and `mag_summary.py` +7. [ ] :art::bug: Finish addition of other embeddings within 2D scatterplot in `mag_refinement.py` +8. [ ] :carrot::racehorse: Add task-queue for ingesting uploaded data to postgres table +9. [ ] :carrot::racehorse: Add k-mer embedding tasks to task-queue +10. [x] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) +11. [ ] :art: Refactor `on_{binning,markers,metagenome}_upload` callbacks to one function that takes in the filetype to determine storage method + +-------------------------------------------------------------------------------------------------- + +## Troubleshooting `docker-compose` + +For some reason, when adding the `web` service in `docker-compose.yml`, postgres no +longer accepts connections. It emits the error: + +```bash +sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) connection to server at "localhost" (127.0.0.1), port 5432 failed: Connection refused + Is the server running on that host and accepting TCP/IP connections? +connection to server at "localhost" (::1), port 5432 failed: Cannot assign requested address + Is the server running on that host and accepting TCP/IP connections? + +(Background on this error at: https://sqlalche.me/e/14/e3q8) +``` + +IDEA: Maybe it has to do with the default driver or network? Upon generation (w/ the `web` service) it emits: + +```bash +Creating network "automappa_default" with the default driver +``` + +> NOTE: When I comment out the `web` service and run only postgres _then_ run automappa in a separate +terminal, I am able to communicate with postgres and the tables are readily retrieved... + +One possible solution to this could be explicitly specifying the URIs in the `docker-compose.yml`. +From inspecting a `docker-compose.yml` file from this repo (), it looks like this could allow passing the +appropriate URI based on the service... + +When each service is constructed it is given its own `HOSTNAME` by docker.... However, it +automagically discovers the `DOCKER_IP` given to it by simply specifying the `service` _in place of_ +the `HOSTNAME`. + +## Misc. Resources + +- [docker-compose networking docs]() +- [live mongoDB dash example]() +- [plotly dash `dcc.Store` docs]() +- [dash bootstrap components docs](https://dash-bootstrap-components.opensource.faculty.ai/docs/components/layout/) diff --git a/automappa/apps/home.py b/automappa/apps/home.py index 3a3c7dc4..5bc8c1e7 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -17,6 +17,7 @@ from automappa.app import app from automappa.utils.serializers import ( convert_bytes, + get_uploaded_datatables, store_binning_main, store_markers, store_metagenome, @@ -197,12 +198,14 @@ samples_store = dcc.Store(id="samples-store", storage_type="local") # samples_datatable = html.Div(id="samples-datatable") -samples_datatable = dcc.Loading( +samples_datatable = ( + dcc.Loading( id="loading-samples-datatable", children=[html.Div(id="samples-datatable")], type="dot", color="#646569", ), +) layout = dbc.Container( children=[ @@ -220,17 +223,17 @@ @app.callback( - Output('samples-store', 'data'), + Output("samples-store", "data"), [ Input("binning-main-upload-store", "modified_timestamp"), Input("markers-upload-store", "modified_timestamp"), Input("metagenome-upload-store", "modified_timestamp"), ], [ - State("binning-main-upload-store", "data"), - State("markers-upload-store", "data"), - State("metagenome-upload-store", "data"), - State('samples-store', 'data'), + State("binning-main-upload-store", "data"), + State("markers-upload-store", "data"), + State("metagenome-upload-store", "data"), + State("samples-store", "data"), ], ) def on_binning_main_upload_store_data( @@ -246,49 +249,50 @@ def on_binning_main_upload_store_data( binning_uploads is None and markers_uploads is None and metagenome_uploads is None - ): - raise PreventUpdate - if ( + ) or ( binning_uploads_timestamp is None and markers_uploads_timestamp is None and metagenome_uploads_timestamp is None ): + # Check if db has any samples in table + tables = get_uploaded_datatables() + if tables: + samples_df = pd.DataFrame( + [ + { + "filetype": os.path.basename(table_id).split("-")[-1], + "table_id": table_id, + } + for table_id in tables + ] + ) + return samples_df.to_json(orient="split") raise PreventUpdate # We need to ensure we prevent an update if there has not been one, otherwise all of our datastore # gets removed... - binning_samples_df = pd.read_json(binning_uploads, orient="split") if binning_uploads else pd.DataFrame() - marker_samples_df = pd.read_json(markers_uploads, orient="split") if markers_uploads else pd.DataFrame() - metagenome_samples_df = pd.read_json(metagenome_uploads, orient="split") if metagenome_uploads else pd.DataFrame() - samples_store_df = pd.read_json(samples_store_data, orient='split') if samples_store_data else pd.DataFrame() - samples_df = pd.concat( - [ - samples_store_df, - binning_samples_df, - marker_samples_df, - metagenome_samples_df, - ] - ).drop_duplicates(subset=["table_id"]) + samples = [] + for data_upload in [binning_uploads, markers_uploads, metagenome_uploads, samples_store_data]: + df = pd.read_json(data_upload, orient="split") if data_upload else pd.DataFrame() + samples.append(df) + samples_df = pd.concat(samples).drop_duplicates(subset=["table_id"]) logger.debug(f"{samples_df.shape[0]:,} samples retrieved from data upload stores") - return samples_df.to_json(orient='split') + return samples_df.to_json(orient="split") + @app.callback( Output("samples-datatable", "children"), - [Input('samples-store', 'data')], - State('samples-store', 'data'), + [Input("samples-store", "data")], + State("samples-store", "data"), ) def on_samples_store_data(samples_store_data, new_samples_store_data): - # if samples_store_data is None: - # # Show upload button...? - # raise PreventUpdate - - # import pdb;pdb.set_trace() - - samples_df = pd.read_json(samples_store_data, orient='split') + samples_df = pd.read_json(samples_store_data, orient="split") if new_samples_store_data is not None: - new_samples_df = pd.read_json(new_samples_store_data, orient='split') - samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates(subset=['table_id']) + new_samples_df = pd.read_json(new_samples_store_data, orient="split") + samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( + subset=["table_id"] + ) logger.debug(f"retrieved {samples_df.shape[0]:,} samples from samples store") @@ -297,8 +301,9 @@ def on_samples_store_data(samples_store_data, new_samples_store_data): return DataTable( data=samples_df.to_dict("records"), - columns=[{"id": col, "name": col, "editable": False} for col in samples_df.columns], - + columns=[ + {"id": col, "name": col, "editable": False} for col in samples_df.columns + ], ) @@ -391,9 +396,9 @@ def on_markers_upload(iscompleted, filenames, upload_id): "filetype": "markers", "filename": filename, f"filesize ({unit})": filesize, - "timestamp": timestamp, - "uploaded": last_modified, "table_id": table_id, + "uploaded": last_modified, + "timestamp": timestamp, } ] ).to_json(orient="split") @@ -441,9 +446,9 @@ def on_metagenome_upload(iscompleted, filenames, upload_id): "filetype": "metagenome", "filename": filename, f"filesize ({unit})": filesize, - "timestamp": timestamp, - "uploaded": last_modified, "table_id": table_id, + "uploaded": last_modified, + "timestamp": timestamp, } ] ).to_json(orient="split") diff --git a/automappa/db.py b/automappa/db.py new file mode 100644 index 00000000..7eecde77 --- /dev/null +++ b/automappa/db.py @@ -0,0 +1,11 @@ +#!/usr/bin/env python + +from sqlalchemy import create_engine + +from automappa.settings import settings + +engine = create_engine( + url=settings.database_url, + # pool_size=settings.database_pool_size, + # pool_pre_ping=True, +) diff --git a/automappa/settings.py b/automappa/settings.py new file mode 100644 index 00000000..34ca023e --- /dev/null +++ b/automappa/settings.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python + +from pydantic import ( + BaseSettings, + RedisDsn, + PostgresDsn, + AmqpDsn, +) + +# Following: +# https://hackernoon.com/build-a-real-time-stock-price-dashboard-with-python-questdb-and-plotly +# https://github.com/gabor-boros/questdb-stock-market-dashboard/blob/main/app/settings.py + +# For the pydantic docs for defining settings, also See: +# https://pydantic-docs.helpmanual.io/usage/settings/ + + +class Settings(BaseSettings): + """ + Settings of the application, used by workers and dashboard. + """ + + # Celery settings + celery_broker: str = "redis://127.0.0.1:6379/0" + + # Database settings + redis_dsn: RedisDsn = "redis://user:pass@localhost:6379/1" + pg_dsn: PostgresDsn = "postgresql://admin:mypass@localhost:5432/automappa" + amqp_dsn: AmqpDsn = "amqp://user:pass@localhost:5672/" + database_pool_size: int = 3 + database_url = pg_dsn + + # Dash/Plotly + debug: bool = True + + class Config: + """ + Meta configuration of the settings parser. + """ + + case_sensitive = True + # Prefix the environment variable not to mix up with other variables + # used by the OS or other software. + env_prefix = "AUTOMAPPA_" + env_file = ".env" + env_file_encoding = "utf-8" + fields = { + "auth_key": { + "env": "my_auth_key", + }, + "redis_dsn": {"env": ["service_redis_dsn", "redis_url"]}, + } + + +settings = Settings() diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py index f860a04c..dfa29dac 100644 --- a/automappa/utils/serializers.py +++ b/automappa/utils/serializers.py @@ -6,8 +6,7 @@ from typing import List import pandas as pd - -from sqlalchemy import create_engine +from automappa.db import engine from autometa.common.markers import load as load_markers from autometa.common.utilities import calc_checksum @@ -21,16 +20,12 @@ logger = logging.getLogger(__name__) -POSTGRES_URL = os.environ.get("POSTGRES_URL") - - def get_uploaded_datatables() -> List[str]: - engine = create_engine(url=POSTGRES_URL, echo=False) tables = engine.table_names() return tables + def get_datatable(table_name: str) -> pd.DataFrame: - engine = create_engine(url=POSTGRES_URL, echo=False) if not engine.has_table(table_name): tables = engine.table_names() raise ValueError(f"{table_name} not in postgres database! available: {tables}") @@ -39,7 +34,7 @@ def get_datatable(table_name: str) -> pd.DataFrame: return df -def convert_bytes(size: int, unit: str="MB", ndigits: int= 2) -> float: +def convert_bytes(size: int, unit: str = "MB", ndigits: int = 2) -> float: """Convert bytes from os.path.getsize(...) to provided `unit` choices include: 'KB', 'MB' or 'GB' @@ -55,7 +50,7 @@ def convert_bytes(size: int, unit: str="MB", ndigits: int= 2) -> float: float Converted bytes value """ - # Yoinked from + # Yoinked from # https://amiradata.com/python-get-file-size-in-kb-mb-or-gb/#Get_file_size_in_KiloBytes_MegaBytes_or_GigaBytes if unit == "KB": return round(size / 1024, ndigits) @@ -97,8 +92,6 @@ def store_binning_main(filepath: Path, if_exists: str = "replace") -> str: table_name = f"{checksum}-binning" # Read filepath contents df = pd.read_csv(filepath, sep="\t") - # Create postgres connection then write table to DB - engine = create_engine(url=POSTGRES_URL, echo=False) # NOTE: table_name must not exceed maximum length of 63 characters df.to_sql(table_name, engine, if_exists=if_exists, index=False) logger.debug(f"Saved {df.shape[0]:,} contigs to postgres table: {table_name}") @@ -110,7 +103,6 @@ def store_binning_main(filepath: Path, if_exists: str = "replace") -> str: return table_name - def store_markers(filepath: Path, if_exists: str = "replace") -> str: """Parse `filepath` into `pd.DataFrame` then save to postgres table @@ -141,11 +133,11 @@ def store_markers(filepath: Path, if_exists: str = "replace") -> str: table_name = f"{checksum}-markers" # Read filepath contents df = load_markers(filepath) - # Create postgres connection then write table to DB - engine = create_engine(url=POSTGRES_URL, echo=False) # NOTE: table_name must not exceed maximum length of 63 characters df.to_sql(table_name, engine, if_exists=if_exists, index=True) - logger.debug(f"Saved {df.shape[0]:,} contigs and {df.shape[1]:,} markers to postgres table: {table_name}") + logger.debug( + f"Saved {df.shape[0]:,} contigs and {df.shape[1]:,} markers to postgres table: {table_name}" + ) except Exception as err: logger.error(err) logger.error("There was an error processing this file.") @@ -153,6 +145,7 @@ def store_markers(filepath: Path, if_exists: str = "replace") -> str: return table_name + def store_metagenome(filepath: Path, if_exists: str = "replace") -> str: """Parse `filepath` into `pd.DataFrame` then save to postgres table @@ -188,8 +181,6 @@ def store_metagenome(filepath: Path, if_exists: str = "replace") -> str: ] ) logger.debug(f"converted... saving to datatable...") - # Create postgres connection then write table to DB - engine = create_engine(url=POSTGRES_URL, echo=False) # NOTE: table_name must not exceed maximum length of 63 characters # Construct table name checksum = calc_checksum(str(filepath)).split()[0] @@ -203,5 +194,6 @@ def store_metagenome(filepath: Path, if_exists: str = "replace") -> str: return table_name + if __name__ == "__main__": pass diff --git a/docker-compose.yml b/docker-compose.yml index ebab0c19..ea7f2c29 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,39 +1,44 @@ -version: '3' +version: '3.8' services: # redis: - # image: redis:latest - # hostname: redis + # image: "redis:latest" + # ports: + # - 6379:6379 postgres: image: postgres + restart: always env_file: - .env ports: - 5432:5432 volumes: - - ./db-data/:/var/lib/postgresql/data/ + - pg_data:/var/lib/postgresql/data # rabbitmq: # hostname: rabbitmq # image: rabbitmq:latest # env_file: # - .env + # web: # build: # context: . # dockerfile: Dockerfile - # hostname: web - # command: automappa + # command: automappa --debug --port 8050 + # env_file: + # - .env # volumes: - # - .:/automappa + # - .:/automappa + # - ./automappa:/usr/src/app # ports: - # - "5000:5000" - # links: - # # - rabbitmq - # # - redis + # - 8050:8050 + # depends_on: # - postgres + # - rabbitmq + # - redis # worker: # build: @@ -41,10 +46,14 @@ services: # dockerfile: Dockerfile # command: celery --app=automappa.tasks.queue worker --loglevel INFO # volumes: - # - .:/automappa + # - ./automappa:/usr/src/app # links: # - rabbitmq # - redis # depends_on: # - rabbitmq # - redis + +volumes: + pg_data: + driver: local \ No newline at end of file diff --git a/environment.yml b/environment.yml index 91958ad8..db7b486b 100644 --- a/environment.yml +++ b/environment.yml @@ -18,6 +18,7 @@ dependencies: - plotly - psycopg2 - python-dotenv + - pydantic - sqlalchemy - pip - pip: From 3013b6244a197c93f324c987817d96900ac0584d Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Thu, 21 Apr 2022 18:45:00 -0400 Subject: [PATCH 12/37] :art: WIP postgresql and automappa-web now functional with doker-compose --- Dockerfile | 4 +- automappa/apps/home.py | 15 +++---- automappa/db.py | 6 +-- automappa/environment/db.env | 8 ++++ automappa/environment/flower.env | 7 +++ automappa/environment/rabbitmq.env | 5 +++ automappa/environment/server.env | 2 + automappa/settings.py | 71 ++++++++++++++++-------------- automappa/utils/README.md | 3 ++ automappa/utils/serializers.py | 1 - docker-compose.yml | 35 +++++++-------- 11 files changed, 92 insertions(+), 65 deletions(-) create mode 100644 automappa/environment/db.env create mode 100644 automappa/environment/flower.env create mode 100644 automappa/environment/rabbitmq.env create mode 100644 automappa/environment/server.env diff --git a/Dockerfile b/Dockerfile index 6cb5bf80..abb369b4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -7,8 +7,8 @@ RUN mamba env update --name base --file=./environment.yml \ && mamba clean --all --force-pkgs-dirs --yes # Test command is functional -COPY . /Automappa/ -WORKDIR /Automappa/ +COPY . /usr/src/app +WORKDIR /usr/src/app RUN python -m pip install . --ignore-installed --no-deps -vvv RUN automappa -h diff --git a/automappa/apps/home.py b/automappa/apps/home.py index 5bc8c1e7..7b54e3af 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -15,6 +15,7 @@ import dash_uploader as du from automappa.app import app +from automappa.settings import server from automappa.utils.serializers import ( convert_bytes, get_uploaded_datatables, @@ -32,8 +33,6 @@ pio.templates.default = "plotly_white" -UPLOAD_FOLDER_ROOT = os.environ.get("UPLOAD_FOLDER_ROOT") - ######################################################################## # LAYOUT # ###################################################################### @@ -321,9 +320,9 @@ def on_binning_main_upload(iscompleted, filenames, upload_id): if filenames is None: return if upload_id: - root_folder = Path(UPLOAD_FOLDER_ROOT) / upload_id + root_folder = Path(server.upload_folder_root) / upload_id else: - root_folder = Path(UPLOAD_FOLDER_ROOT) + root_folder = Path(server.upload_folder_root) uploaded_files = [] for filename in filenames: @@ -370,9 +369,9 @@ def on_markers_upload(iscompleted, filenames, upload_id): if filenames is None: return if upload_id: - root_folder = Path(UPLOAD_FOLDER_ROOT) / upload_id + root_folder = Path(server.upload_folder_root) / upload_id else: - root_folder = Path(UPLOAD_FOLDER_ROOT) + root_folder = Path(server.upload_folder_root) uploaded_files = [] for filename in filenames: @@ -420,9 +419,9 @@ def on_metagenome_upload(iscompleted, filenames, upload_id): if filenames is None: return if upload_id: - root_folder = Path(UPLOAD_FOLDER_ROOT) / upload_id + root_folder = Path(server.upload_folder_root) / upload_id else: - root_folder = Path(UPLOAD_FOLDER_ROOT) + root_folder = Path(server.upload_folder_root) uploaded_files = [] for filename in filenames: diff --git a/automappa/db.py b/automappa/db.py index 7eecde77..635bf9d9 100644 --- a/automappa/db.py +++ b/automappa/db.py @@ -2,10 +2,10 @@ from sqlalchemy import create_engine -from automappa.settings import settings +from automappa.settings import db engine = create_engine( - url=settings.database_url, - # pool_size=settings.database_pool_size, + url=db.url, + pool_size=db.pool_size, # pool_pre_ping=True, ) diff --git a/automappa/environment/db.env b/automappa/environment/db.env new file mode 100644 index 00000000..722bbcdc --- /dev/null +++ b/automappa/environment/db.env @@ -0,0 +1,8 @@ +UPLOAD_FOLDER_ROOT="${HOME}/.automappa/uploads" +USER="admin" +PASSWORD="mypass" +DB="automappa" +URL="postgresql+psycopg2://${USER}:${PASSWORD}@db:5432/${DB}" +DOCKER_URL="postgresql+psycopg2://${USER}:${PASSWORD}@db:5432/${DB}" +LOCAL_URL="postgresql+psycopg2://${USER}:${PASSWORD}@localhost:5432/${DB}" +POOL_SIZE=1 \ No newline at end of file diff --git a/automappa/environment/flower.env b/automappa/environment/flower.env new file mode 100644 index 00000000..a0a3f05c --- /dev/null +++ b/automappa/environment/flower.env @@ -0,0 +1,7 @@ +RABBITMQ_DEFAULT_USER="admin" +RABBITMQ_DEFAULT_PASS="mypass" +BACKEND_URL='redis://redis:6379/0' +BROKER_URL='amqp://admin:mypass@rabbit//' +CELERY_BROKER_URL="redis://127.0.0.1:6379/0" +REDIS_URL="redis://user:pass@localhost:6379/1" +RABBITMQ_URL="amqp://user:pass@localhost:5672/" \ No newline at end of file diff --git a/automappa/environment/rabbitmq.env b/automappa/environment/rabbitmq.env new file mode 100644 index 00000000..596e4590 --- /dev/null +++ b/automappa/environment/rabbitmq.env @@ -0,0 +1,5 @@ +URL="amqp://user:pass@rabbitmq:5672/" +DOCKER_URL="amqp://user:pass@rabbitmq:5672/" +LOCAL_URL="amqp://user:pass@localhost:5672/" +# TODO: Change to appropriate service name +# URL="amqp://user:pass@:5672/" \ No newline at end of file diff --git a/automappa/environment/server.env b/automappa/environment/server.env new file mode 100644 index 00000000..47f73a66 --- /dev/null +++ b/automappa/environment/server.env @@ -0,0 +1,2 @@ +UPLOAD_FOLDER_ROOT="${HOME}/.automappa/uploads" +DEBUG=True \ No newline at end of file diff --git a/automappa/settings.py b/automappa/settings.py index 34ca023e..65669dd2 100644 --- a/automappa/settings.py +++ b/automappa/settings.py @@ -1,5 +1,8 @@ #!/usr/bin/env python +import os +from pathlib import Path +from typing import Optional from pydantic import ( BaseSettings, RedisDsn, @@ -7,49 +10,51 @@ AmqpDsn, ) -# Following: -# https://hackernoon.com/build-a-real-time-stock-price-dashboard-with-python-questdb-and-plotly -# https://github.com/gabor-boros/questdb-stock-market-dashboard/blob/main/app/settings.py - # For the pydantic docs for defining settings, also See: # https://pydantic-docs.helpmanual.io/usage/settings/ +class DatabaseSettings(BaseSettings): + # TODO: Figure out how to make the url dynamic to docker_url or local_url based on + # is_docker_service... e.g. + # url: PostgresDsn = docker_url if is_docker_service else local_url + url: PostgresDsn + docker_url: Optional[PostgresDsn] + local_url: Optional[PostgresDsn] + pool_size: Optional[int] = 3 + is_docker_service : bool = os.environ.get('PWD') == '/usr/src/app' -class Settings(BaseSettings): - """ - Settings of the application, used by workers and dashboard. - """ + class Config: + env_file: str = "automappa/environment/db.env" + env_file_encoding: str = "utf-8" - # Celery settings - celery_broker: str = "redis://127.0.0.1:6379/0" +class RabbitmqSettings(BaseSettings): + url: AmqpDsn + class Config: + env_file: str = "automappa/environment/rabbitmq.env" + env_file_encoding: str = "utf-8" - # Database settings - redis_dsn: RedisDsn = "redis://user:pass@localhost:6379/1" - pg_dsn: PostgresDsn = "postgresql://admin:mypass@localhost:5432/automappa" - amqp_dsn: AmqpDsn = "amqp://user:pass@localhost:5672/" - database_pool_size: int = 3 - database_url = pg_dsn - # Dash/Plotly - debug: bool = True +class FlowerSettings(BaseSettings): + backend_url: RedisDsn + broker_url: AmqpDsn + class Config: + env_file: str = "automappa/environment/flower.env" + env_file_encoding: str = "utf-8" +class ServerSettings(BaseSettings): + """ + Settings of the application, used by workers and dashboard. + """ + upload_folder_root: Path + # Dash/Plotly + debug: Optional[bool] = True class Config: """ Meta configuration of the settings parser. """ - - case_sensitive = True - # Prefix the environment variable not to mix up with other variables - # used by the OS or other software. - env_prefix = "AUTOMAPPA_" - env_file = ".env" + env_file = "automappa/environment/server.env" env_file_encoding = "utf-8" - fields = { - "auth_key": { - "env": "my_auth_key", - }, - "redis_dsn": {"env": ["service_redis_dsn", "redis_url"]}, - } - - -settings = Settings() + +server = ServerSettings() +db = DatabaseSettings() +flower = FlowerSettings() \ No newline at end of file diff --git a/automappa/utils/README.md b/automappa/utils/README.md index 2e02516b..4360e0cf 100644 --- a/automappa/utils/README.md +++ b/automappa/utils/README.md @@ -3,3 +3,6 @@ ## Miscellaneous resources related to Automappa Utils - [Mapping between python and postgresql types](https://pynative.com/python-postgresql-tutorial/#h-the-mapping-between-python-and-postgresql-types "Mapping between python and postgresql types") +- [Hackernoon/questdb-and-plotly]() +- [github.com:gabor-boros/questdb-stock-market-dashboard]() +- [:tv: youtube: "Pydantic: Modern Python Data Validation and Settings by Michael Kennedy"](https://youtu.be/lon-dEXfY2I?t=1027 "Pydantic: Modern Python Data Validation and Settings by Michael Kennedy") diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py index dfa29dac..d72866a3 100644 --- a/automappa/utils/serializers.py +++ b/automappa/utils/serializers.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import os import logging from pathlib import Path from typing import List diff --git a/docker-compose.yml b/docker-compose.yml index ea7f2c29..0835f652 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: # ports: # - 6379:6379 - postgres: + db: image: postgres restart: always env_file: @@ -23,23 +23,6 @@ services: # env_file: # - .env - # web: - # build: - # context: . - # dockerfile: Dockerfile - # command: automappa --debug --port 8050 - # env_file: - # - .env - # volumes: - # - .:/automappa - # - ./automappa:/usr/src/app - # ports: - # - 8050:8050 - # depends_on: - # - postgres - # - rabbitmq - # - redis - # worker: # build: # context: . @@ -53,6 +36,22 @@ services: # depends_on: # - rabbitmq # - redis + web: + build: + context: . + dockerfile: Dockerfile + command: automappa --debug --port 8050 + # env_file: + # - .env + volumes: + # /usr/src/app is location of install in Dockerfile + - .:/usr/src/app + ports: + - 8050:8050 + depends_on: + - db + # - rabbitmq + # - redis volumes: pg_data: From ea83e850f32aebc9b17ec193dff749f2ae7c55c2 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Thu, 21 Apr 2022 20:36:17 -0400 Subject: [PATCH 13/37] :art: WIP now fetching all fileuploads and creating files datatable using MetaData.table.keys() Should now be able to :fire: remove all of the data stores to track the fileupload table_id... :fire: --- TODO.md | 6 +- automappa/apps/home.py | 164 +++++------------------ automappa/apps/mag_refinement.py | 4 + automappa/db.py | 4 +- automappa/utils/serializers.py | 215 ++++++++++++++++++++++--------- 5 files changed, 201 insertions(+), 192 deletions(-) diff --git a/TODO.md b/TODO.md index ee0ef405..d73d93b8 100644 --- a/TODO.md +++ b/TODO.md @@ -2,15 +2,15 @@ 1. [x] :art:::racehorse: Pass postgres datatable `table_name` back from `dash-uploader` callback 2. [x] :racehorse::art: `dcc.Store(...)` datatable id in `dcc.Store(...)` -3. [ ] :racehorse::art: Save uploaded file metadata to postgres datatable -4. [ ] :racehorse::art: Populate `dcc.Store(...)` with user's existing datatables +3. [x] :racehorse::art: Save uploaded file metadata to postgres datatable +4. [x] :racehorse::art: Populate `dcc.Store(...)` with user's existing datatables 5. [ ] :racehorse::art: Populate `data_table.DataTable` with clickable `dcc.Link` that navigates user to `mag_refinement.py` (will load data relevant to link, e.g. metagenome annotations, markers, etc.) 6. [ ] :racehorse: Retrieve datatable within `mag_refinement.py` and `mag_summary.py` 7. [ ] :art::bug: Finish addition of other embeddings within 2D scatterplot in `mag_refinement.py` 8. [ ] :carrot::racehorse: Add task-queue for ingesting uploaded data to postgres table 9. [ ] :carrot::racehorse: Add k-mer embedding tasks to task-queue 10. [x] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) -11. [ ] :art: Refactor `on_{binning,markers,metagenome}_upload` callbacks to one function that takes in the filetype to determine storage method +11. [x] :art: Refactor `on_{binning,markers,metagenome}_upload` callbacks to one function that takes in the filetype to determine storage method -------------------------------------------------------------------------------------------------- diff --git a/automappa/apps/home.py b/automappa/apps/home.py index 7b54e3af..b36cd3ef 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -1,9 +1,7 @@ # -*- coding: utf-8 -*- -from datetime import datetime import os import logging -from pathlib import Path from dash import html, dcc from dash.dash_table import DataTable from dash.exceptions import PreventUpdate @@ -15,13 +13,10 @@ import dash_uploader as du from automappa.app import app -from automappa.settings import server from automappa.utils.serializers import ( - convert_bytes, - get_uploaded_datatables, - store_binning_main, - store_markers, - store_metagenome, + get_uploaded_files_table, + save_to_db, + validate_uploader, ) logging.basicConfig( @@ -235,7 +230,7 @@ State("samples-store", "data"), ], ) -def on_binning_main_upload_store_data( +def on_upload_stores_data( binning_uploads_timestamp, markers_uploads_timestamp, metagenome_uploads_timestamp, @@ -254,18 +249,9 @@ def on_binning_main_upload_store_data( and metagenome_uploads_timestamp is None ): # Check if db has any samples in table - tables = get_uploaded_datatables() - if tables: - samples_df = pd.DataFrame( - [ - { - "filetype": os.path.basename(table_id).split("-")[-1], - "table_id": table_id, - } - for table_id in tables - ] - ) - return samples_df.to_json(orient="split") + uploaded_files_df = get_uploaded_files_table() + if uploaded_files_df: + return uploaded_files_df.to_json(orient="split") raise PreventUpdate # We need to ensure we prevent an update if there has not been one, otherwise all of our datastore # gets removed... @@ -315,44 +301,18 @@ def on_samples_store_data(samples_store_data, new_samples_store_data): ], ) def on_binning_main_upload(iscompleted, filenames, upload_id): - if not iscompleted: - return - if filenames is None: - return - if upload_id: - root_folder = Path(server.upload_folder_root) / upload_id - else: - root_folder = Path(server.upload_folder_root) - - uploaded_files = [] - for filename in filenames: - file = root_folder / filename - uploaded_files.append(file) - - if len(uploaded_files) > 1: - logger.error("You may only upload one file at a time!") + try: + filepath = validate_uploader(iscompleted, filenames, upload_id) + except ValueError as err: + logger.warn(err) raise PreventUpdate - - filepath = uploaded_files[0] - filename = os.path.basename(filepath) - unit = "MB" - filesize = convert_bytes(os.path.getsize(filepath), unit) - timestamp = os.path.getmtime(filepath) - last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") - table_id = store_binning_main(filepath) - - return pd.DataFrame( - [ - { - "filetype": "binning_main", - "filename": filename, - f"filesize ({unit})": filesize, - "table_id": table_id, - "uploaded": last_modified, - "timestamp": timestamp, - } - ] - ).to_json(orient="split") + if not filepath: + raise PreventUpdate + df = save_to_db( + filepath=filepath, + filetype="binning", + ) + return df.to_json(orient="split") @app.callback( @@ -364,43 +324,15 @@ def on_binning_main_upload(iscompleted, filenames, upload_id): ], ) def on_markers_upload(iscompleted, filenames, upload_id): - if not iscompleted: - return - if filenames is None: - return - if upload_id: - root_folder = Path(server.upload_folder_root) / upload_id - else: - root_folder = Path(server.upload_folder_root) - - uploaded_files = [] - for filename in filenames: - file = root_folder / filename - uploaded_files.append(file) - if len(uploaded_files) > 1: - logger.error("You may only upload one file at a time!") + try: + filepath = validate_uploader(iscompleted, filenames, upload_id) + except ValueError as err: + logger.warn(err) raise PreventUpdate - - filepath = uploaded_files[0] - filename = os.path.basename(filepath) - unit = "MB" - filesize = convert_bytes(os.path.getsize(filepath), unit) - timestamp = os.path.getmtime(filepath) - last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") - table_id = store_markers(filepath) - - return pd.DataFrame( - [ - { - "filetype": "markers", - "filename": filename, - f"filesize ({unit})": filesize, - "table_id": table_id, - "uploaded": last_modified, - "timestamp": timestamp, - } - ] - ).to_json(orient="split") + if not filepath: + raise PreventUpdate + df = save_to_db(filepath, "markers") + return df.to_json(orient="split") # For information on the dash_uploader component and callbacks... @@ -414,43 +346,15 @@ def on_markers_upload(iscompleted, filenames, upload_id): ], ) def on_metagenome_upload(iscompleted, filenames, upload_id): - if not iscompleted: - return - if filenames is None: - return - if upload_id: - root_folder = Path(server.upload_folder_root) / upload_id - else: - root_folder = Path(server.upload_folder_root) - - uploaded_files = [] - for filename in filenames: - file = root_folder / filename - uploaded_files.append(file) - if len(uploaded_files) > 1: - logger.error("You may only upload one file at a time!") + try: + filepath = validate_uploader(iscompleted, filenames, upload_id) + except ValueError as err: + logger.warn(err) raise PreventUpdate - - filepath = uploaded_files[0] - filename = os.path.basename(filepath) - unit = "MB" - filesize = convert_bytes(os.path.getsize(filepath), unit) - timestamp = os.path.getmtime(filepath) - last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") - table_id = store_metagenome(filepath) - - return pd.DataFrame( - [ - { - "filetype": "metagenome", - "filename": filename, - f"filesize ({unit})": filesize, - "table_id": table_id, - "uploaded": last_modified, - "timestamp": timestamp, - } - ] - ).to_json(orient="split") + if not filepath: + raise PreventUpdate + df = save_to_db(filepath, "metagenome") + return df.to_json(orient="split") @app.callback( diff --git a/automappa/apps/mag_refinement.py b/automappa/apps/mag_refinement.py index 1c982ed7..802d9ba8 100644 --- a/automappa/apps/mag_refinement.py +++ b/automappa/apps/mag_refinement.py @@ -18,6 +18,8 @@ from automappa.app import app +from automappa.utils.serializers import get_table + from automappa.utils.figures import ( format_axis_title, get_scatterplot_2d, @@ -465,6 +467,8 @@ def update_mag_metrics_datatable_callback( markers_json: "str | None", selected_contigs: Dict[str, List[Dict[str, str]]] ) -> DataTable: markers_df = pd.read_json(markers_json, orient="split").set_index("contig") + # TODO: Replace pd.read_json get_table(table_name) + # TODO: Need to pass table_name from home.py... if selected_contigs: contigs = {point["text"] for point in selected_contigs["points"]} selected_contigs_count = len(contigs) diff --git a/automappa/db.py b/automappa/db.py index 635bf9d9..e35bfebb 100644 --- a/automappa/db.py +++ b/automappa/db.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from sqlalchemy import create_engine +from sqlalchemy import create_engine,MetaData from automappa.settings import db @@ -9,3 +9,5 @@ pool_size=db.pool_size, # pool_pre_ping=True, ) + +metadata = MetaData(bind=engine) \ No newline at end of file diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py index d72866a3..74b8c6f2 100644 --- a/automappa/utils/serializers.py +++ b/automappa/utils/serializers.py @@ -1,11 +1,14 @@ #!/usr/bin/env python +from datetime import datetime import logging from pathlib import Path -from typing import List +from typing import List, Optional +import uuid import pandas as pd -from automappa.db import engine +from automappa.db import engine,metadata +from automappa.settings import server from autometa.common.markers import load as load_markers from autometa.common.utilities import calc_checksum @@ -20,16 +23,28 @@ def get_uploaded_datatables() -> List[str]: - tables = engine.table_names() - return tables + return list(metadata.tables.keys()) + +def get_uploaded_files_table() -> pd.DataFrame: + df = pd.DataFrame() + with engine.connect() as conn: + tables = [table for table in metadata.table.keys() if "fileupload" in table] + if tables: + df = pd.DataFrame([ + pd.read_sql(table, conn) + for table in tables + ]) + return df -def get_datatable(table_name: str) -> pd.DataFrame: +def get_table(table_name: str, index_col: Optional[str]) -> pd.DataFrame: if not engine.has_table(table_name): - tables = engine.table_names() - raise ValueError(f"{table_name} not in postgres database! available: {tables}") - df = pd.read_sql(table_name, engine).set_index("contig") - logger.debug(f"retrieved {df.shape[0]} contigs from {table_name} datatable") + tables = metadata.table.keys() + raise ValueError(f"{table_name} not in database! available: {tables}") + df = pd.read_sql(table_name, engine) + if index_col: + df = df.set_index(index_col) + logger.debug(f"retrieved {table_name} datatable, shape: {df.shape}") return df @@ -85,20 +100,14 @@ def store_binning_main(filepath: Path, if_exists: str = "replace") -> str: table_id = postgres table id for retrieving stored data (AKA name of SQL table) e.g. `'{checksum}-binning'` """ - try: - # Construct table name - checksum = calc_checksum(str(filepath)).split()[0] - table_name = f"{checksum}-binning" - # Read filepath contents - df = pd.read_csv(filepath, sep="\t") - # NOTE: table_name must not exceed maximum length of 63 characters - df.to_sql(table_name, engine, if_exists=if_exists, index=False) - logger.debug(f"Saved {df.shape[0]:,} contigs to postgres table: {table_name}") - except Exception as err: - logger.error(err) - logger.error("There was an error processing this file.") - table_name = "" - + # Construct table name + checksum = calc_checksum(str(filepath)).split()[0] + table_name = f"{checksum}-binning" + # Read filepath contents + df = pd.read_csv(filepath, sep="\t") + # NOTE: table_name must not exceed maximum length of 63 characters + df.to_sql(table_name, engine, if_exists=if_exists, index=False) + logger.debug(f"Saved {df.shape[0]:,} contigs to postgres table: {table_name}") return table_name @@ -126,22 +135,16 @@ def store_markers(filepath: Path, if_exists: str = "replace") -> str: table_id = postgres table id for retrieving stored data (AKA name of SQL table) e.g. `'{checksum}-markers'` """ - try: - # Construct table name - checksum = calc_checksum(str(filepath)).split()[0] - table_name = f"{checksum}-markers" - # Read filepath contents - df = load_markers(filepath) - # NOTE: table_name must not exceed maximum length of 63 characters - df.to_sql(table_name, engine, if_exists=if_exists, index=True) - logger.debug( - f"Saved {df.shape[0]:,} contigs and {df.shape[1]:,} markers to postgres table: {table_name}" - ) - except Exception as err: - logger.error(err) - logger.error("There was an error processing this file.") - table_name = "" - + # Construct table name + checksum = calc_checksum(str(filepath)).split()[0] + table_name = f"{checksum}-markers" + # Read filepath contents + df = load_markers(filepath) + # NOTE: table_name must not exceed maximum length of 63 characters + df.to_sql(table_name, engine, if_exists=if_exists, index=True) + logger.debug( + f"Saved {df.shape[0]:,} contigs and {df.shape[1]:,} markers to postgres table: {table_name}" + ) return table_name @@ -169,30 +172,126 @@ def store_metagenome(filepath: Path, if_exists: str = "replace") -> str: table_id = postgres table id for retrieving stored data (AKA name of SQL table) e.g. `'{checksum}-metagenome'` """ + # Read filepath contents + logger.debug(f"{filepath} uploaded... converting for datatable...") + metagenome = Metagenome(assembly=filepath) + df = pd.DataFrame( + [ + {"contig": seqrecord.id, "sequence": str(seqrecord.seq)} + for seqrecord in metagenome.seqrecords + ] + ) + logger.debug(f"converted... saving to datatable...") + # NOTE: table_name must not exceed maximum length of 63 characters + # Construct table name + checksum = calc_checksum(str(filepath)).split()[0] + table_name = f"{checksum}-metagenome" + df.to_sql(table_name, engine, if_exists=if_exists, index=False) + logger.debug(f"Saved {df.shape[0]:,} contigs to postgres table: {table_name}") + return table_name + + +def save_to_db(filepath: Path, filetype: str, if_exists: str = "replace", rm_after_upload: bool = True) -> pd.DataFrame: + """Store `filepath` to db table based on `filetype` + + Parameters + ---------- + filepath : Path + Path to uploaded file to be stored in db table + filetype : str + type of file to be stored + choices include 'markers', 'metagenome', 'binning' + + Returns + ------- + pd.DataFrame + cols=[filetype, filename, filesize (MB), table_id, uploaded, timestamp] + + Raises + ------ + ValueError + `filetype` not in filetype store methods + """ + bytes_size = filepath.stat().st_size + filesize = convert_bytes(bytes_size, "MB") + timestamp = filepath.stat().st_mtime + last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") + filetype_store_methods = { + "markers": store_markers, + "metagenome": store_metagenome, + "binning": store_binning_main, + } + if filetype not in filetype_store_methods: + raise ValueError(f"{filetype} not in filetype store methods {','.join(filetype_store_methods.keys())}") + store_method = filetype_store_methods[filetype] try: - # Read filepath contents - logger.debug(f"{filepath} uploaded... converting for datatable...") - metagenome = Metagenome(assembly=filepath) - df = pd.DataFrame( - [ - {"contig": seqrecord.id, "sequence": str(seqrecord.seq)} - for seqrecord in metagenome.seqrecords - ] - ) - logger.debug(f"converted... saving to datatable...") - # NOTE: table_name must not exceed maximum length of 63 characters - # Construct table name - checksum = calc_checksum(str(filepath)).split()[0] - table_name = f"{checksum}-metagenome" - df.to_sql(table_name, engine, if_exists=if_exists, index=False) - logger.debug(f"Saved {df.shape[0]:,} contigs to postgres table: {table_name}") + table_id = store_method(filepath, if_exists=if_exists) + if rm_after_upload: + # TODO: Should have some way to retrieve data if already uploaded to server... + # ...but data ingestion fails for some reason... + logger.debug(f"Removed upload: {filepath.name} from server") + filepath.unlink(missing_ok=True) except Exception as err: logger.error(err) - logger.error("There was an error processing this file.") - table_name = "" + return pd.DataFrame() + df = pd.DataFrame( + [ + { + "filetype": filetype, + "filename": filepath.name, + "filesize (MB)": filesize, + "table_id": table_id, + "uploaded": last_modified, + "timestamp": timestamp, + } + ] + ) + + # Create table_name specific to uploaded file with the upload file metadata... + # This should contain mapping to where the file contents are stored (i.e. table_id) + table_name = f"{uuid.uuid4()}-fileupload" + df.to_sql(table_name, engine, if_exists=if_exists, index=False) + logger.debug(f"Saved {table_name} to db") + return df - return table_name +def validate_uploader(iscompleted: bool, filenames, upload_id) -> Path: + """Ensure only one file was uploaded and create Path to uploaded file + Parameters + ---------- + iscompleted : bool + Whether or not the upload has finished + filenames : list + list of filenames + upload_id : uuid + unique user id associated with upload + + Returns + ------- + Path + Server-side path to uploaded file + + Raises + ------ + ValueError + You may only upload one file at a time! + """ + if not iscompleted: + return + if filenames is None: + return + if upload_id: + root_folder = server.upload_folder_root / upload_id + else: + root_folder = server.upload_folder_root + + uploaded_files = [] + for filename in filenames: + file = root_folder / filename + uploaded_files.append(file) + if len(uploaded_files) > 1: + raise ValueError("You may only upload one file at a time!") + return uploaded_files[0] if __name__ == "__main__": pass From d0f943f674d7dff5cfce45aef6999f9360452f05 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Thu, 21 Apr 2022 21:28:40 -0400 Subject: [PATCH 14/37] :art::whale: Change docker compose s.t. live-reloading works again --- .env | 1 + Dockerfile | 2 +- docker-compose.yml | 36 +++++++++++++++--------------------- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/.env b/.env index c4c6a1d8..5842502f 100644 --- a/.env +++ b/.env @@ -1,3 +1,4 @@ +# Required for docker-compose.yml postgres service UPLOAD_FOLDER_ROOT="${HOME}/.automappa/uploads" POSTGRES_USER="admin" POSTGRES_PASSWORD="mypass" diff --git a/Dockerfile b/Dockerfile index abb369b4..aa6991ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,10 +6,10 @@ RUN conda install --prune --name base mamba --yes RUN mamba env update --name base --file=./environment.yml \ && mamba clean --all --force-pkgs-dirs --yes -# Test command is functional COPY . /usr/src/app WORKDIR /usr/src/app RUN python -m pip install . --ignore-installed --no-deps -vvv +# Test command is functional RUN automappa -h # Create an unprivileged user for running our Python code. diff --git a/docker-compose.yml b/docker-compose.yml index 0835f652..986ebae1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,47 +2,40 @@ version: '3.8' services: - # redis: - # image: "redis:latest" - # ports: - # - 6379:6379 db: image: postgres restart: always - env_file: - - .env ports: - 5432:5432 volumes: - pg_data:/var/lib/postgresql/data # rabbitmq: - # hostname: rabbitmq # image: rabbitmq:latest - # env_file: - # - .env - - # worker: + # celery: # build: # context: . # dockerfile: Dockerfile # command: celery --app=automappa.tasks.queue worker --loglevel INFO # volumes: - # - ./automappa:/usr/src/app - # links: - # - rabbitmq - # - redis - # depends_on: - # - rabbitmq - # - redis + # - .:/usr/src/app + # flower: + # image: "mher/flower:latest" + # ports: + # - 1234:1234 + # redis: + # image: "redis:latest" + # ports: + # - 6379:6379 web: build: context: . dockerfile: Dockerfile - command: automappa --debug --port 8050 - # env_file: - # - .env + # command: automappa --debug --port 8050 + # This command allows live-reloading for dev + # Not sure which is more appropriate for production :shrug: + command: python -m automappa.index --debug --port 8050 volumes: # /usr/src/app is location of install in Dockerfile - .:/usr/src/app @@ -51,6 +44,7 @@ services: depends_on: - db # - rabbitmq + # - celery # - redis volumes: From cd6d4f813a7f4221a14a572339f9d0b8182838cf Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Fri, 22 Apr 2022 00:45:53 -0400 Subject: [PATCH 15/37] :fire::whale::art::sunflower::elephant: Add docker-services, rm environment/ :whale::sunflower::elephant::art: Add docker-services: celery, flower, prometheus, grafana, redis, rabbitmq :art::fire: Remove environment/*.env files. Consolidate to .env with corresponding prefixes :art: Change pydantic models built on BaseSettings in automappa.settings to use only .env with env_prefix :art::sunflower: Add working configuration of celery task-queue with redis backend and rabbitMQ broker :memo: Add resources/references for misc. links used during debugging/troubleshooting :arrow_up: Add flower. Move celery[redis] to pip install section of environment.yml :art: black formatting to serializers and change server.upload_folder root to server.root_upload_folder :art: blak formatting to home.py --- .env | 15 +- Makefile | 12 +- TODO.md | 51 +- automappa/app.py | 8 +- automappa/apps/home.py | 11 +- automappa/db.py | 4 +- automappa/environment/db.env | 8 - automappa/environment/flower.env | 7 - automappa/environment/rabbitmq.env | 5 - automappa/environment/server.env | 2 - automappa/settings.py | 39 +- automappa/tasks.py | 11 +- automappa/utils/README.md | 7 +- automappa/utils/serializers.py | 27 +- celery-monitoring-grafana-dashboard.json | 759 +++++++++++++++++++++++ docker-compose.yml | 96 ++- environment.yml | 5 +- 17 files changed, 935 insertions(+), 132 deletions(-) delete mode 100644 automappa/environment/db.env delete mode 100644 automappa/environment/flower.env delete mode 100644 automappa/environment/rabbitmq.env delete mode 100644 automappa/environment/server.env create mode 100644 celery-monitoring-grafana-dashboard.json diff --git a/.env b/.env index 5842502f..062d7e50 100644 --- a/.env +++ b/.env @@ -1,10 +1,13 @@ # Required for docker-compose.yml postgres service -UPLOAD_FOLDER_ROOT="${HOME}/.automappa/uploads" +SERVER_ROOT_UPLOAD_FOLDER="${HOME}/.automappa/uploads" +SERVER_DEBUG=True POSTGRES_USER="admin" POSTGRES_PASSWORD="mypass" POSTGRES_DB="automappa" -POSTGRES_URL="postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@localhost:5432/${POSTGRES_DB}" -RABBITMQ_DEFAULT_USER="admin" -RABBITMQ_DEFAULT_PASS="mypass" -CELERY_REDIS_URL='redis://redis:6379/0' -CELERY_BROKER_URL='amqp://admin:mypass@rabbit//' \ No newline at end of file +POSTGRES_URL="postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}" +POSTGRES_POOL_SIZE=1 +RABBITMQ_DEFAULT_USER="user" +RABBITMQ_DEFAULT_PASS="pass" +RABBITMQ_URL="amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/" +CELERY_BACKEND_URL='redis://redis:6379/0' +CELERY_BROKER_URL="amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672//" \ No newline at end of file diff --git a/Makefile b/Makefile index 95604ebe..83fd7641 100644 --- a/Makefile +++ b/Makefile @@ -51,15 +51,19 @@ install: ## docker compose build from docker-compose.yml build: docker-compose.yml - docker compose build + docker-compose build -## docker compose up from docker-compose.yml +## alias for docker-compose up --always-recreate-deps --remove-orphans --force-recreate up: docker-compose.yml docker-compose up --always-recreate-deps --remove-orphans --force-recreate -## docker compose down from docker-compose.yml +## alias for docker-compose down down: docker-compose.yml - docker compose down + docker-compose down --volumes + +## alias for docker-compose down --volumes +down-v: docker-compose.yml + docker-compose down --volumes # Run Automappa on test data # test: test_data diff --git a/TODO.md b/TODO.md index d73d93b8..ce1d57be 100644 --- a/TODO.md +++ b/TODO.md @@ -4,46 +4,43 @@ 2. [x] :racehorse::art: `dcc.Store(...)` datatable id in `dcc.Store(...)` 3. [x] :racehorse::art: Save uploaded file metadata to postgres datatable 4. [x] :racehorse::art: Populate `dcc.Store(...)` with user's existing datatables -5. [ ] :racehorse::art: Populate `data_table.DataTable` with clickable `dcc.Link` that navigates user to `mag_refinement.py` (will load data relevant to link, e.g. metagenome annotations, markers, etc.) +5. [ ] ~:racehorse::art: Populate `data_table.DataTable` with clickable `dcc.Link` that navigates user to `mag_refinement.py` (will load data relevant to link, e.g. metagenome annotations, markers, etc.)~ 6. [ ] :racehorse: Retrieve datatable within `mag_refinement.py` and `mag_summary.py` + - binning, markers, metagenome dropdowns for selecting data available in database + - dropdown values correspond to `table_id` and are sent to `mag_refinement.py` callbacks, replacing `pd.read_json(...)` + - dropdowns should have a "NA" or placeholder causing inability to navigate to other layouts when data for the particular filetype is unavailable 7. [ ] :art::bug: Finish addition of other embeddings within 2D scatterplot in `mag_refinement.py` -8. [ ] :carrot::racehorse: Add task-queue for ingesting uploaded data to postgres table -9. [ ] :carrot::racehorse: Add k-mer embedding tasks to task-queue -10. [x] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) -11. [x] :art: Refactor `on_{binning,markers,metagenome}_upload` callbacks to one function that takes in the filetype to determine storage method +8. [x] :carrot::racehorse: Add celery task-queue +9. [x] :carrot::racehorse: Add celery-monitoring services +10. [ ] :carrot::racehorse: Add uploaded data ingestion to task-queue +11. [ ] :carrot::racehorse: Add k-mer embedding tasks to task-queue +12. [x] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) +13. [x] :art: Refactor `on_{binning,markers,metagenome}_upload` callbacks to one function that takes in the filetype to determine storage method -------------------------------------------------------------------------------------------------- -## Troubleshooting `docker-compose` +## docker-compose services configuration -For some reason, when adding the `web` service in `docker-compose.yml`, postgres no -longer accepts connections. It emits the error: +> NOTE: All of this assumes you have all docker services running via `make up` or `docker-compose up` -```bash -sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) connection to server at "localhost" (127.0.0.1), port 5432 failed: Connection refused - Is the server running on that host and accepting TCP/IP connections? -connection to server at "localhost" (::1), port 5432 failed: Cannot assign requested address - Is the server running on that host and accepting TCP/IP connections? +TODO: Provision grafana from `docker-compose.yml`. See: [Grafana provisioning example data source config file](https://grafana.com/docs/grafana/latest/administration/provisioning/#example-data-source-config-file) -(Background on this error at: https://sqlalche.me/e/14/e3q8) -``` +### Navigate to grafana + +- prometheus link - [http://localhost:9090](http://localhost:9090) +- grafana link - [http://localhost:3000](http://localhost:3000) -IDEA: Maybe it has to do with the default driver or network? Upon generation (w/ the `web` service) it emits: +- flower+prometheus+grafana [add prometheus as a data source in grafana]( "flower+prometheus+grafana add prometheus as a data source in grafana") + +Add the prometheus url as: ```bash -Creating network "automappa_default" with the default driver +http://prometheus:9090 ``` -> NOTE: When I comment out the `web` service and run only postgres _then_ run automappa in a separate -terminal, I am able to communicate with postgres and the tables are readily retrieved... - -One possible solution to this could be explicitly specifying the URIs in the `docker-compose.yml`. -From inspecting a `docker-compose.yml` file from this repo (), it looks like this could allow passing the -appropriate URI based on the service... +Notice the tutorial mentions `http://localhost:9090`, but since this is running as a service using `docker-compose` the hostname changes to the +`prometheus` alias (this is the name of the service in the `docker-compose.yml` file) -When each service is constructed it is given its own `HOSTNAME` by docker.... However, it -automagically discovers the `DOCKER_IP` given to it by simply specifying the `service` _in place of_ -the `HOSTNAME`. ## Misc. Resources @@ -51,3 +48,5 @@ the `HOSTNAME`. - [live mongoDB dash example]() - [plotly dash `dcc.Store` docs]() - [dash bootstrap components docs](https://dash-bootstrap-components.opensource.faculty.ai/docs/components/layout/) +- [how to access rabbitmq publicly]( "how to access RabbitMQ publicly") +- [StackOverflow: how to access rabbitmq publicly](https://stackoverflow.com/a/57612615 "StackOverflow: how to access RabbitMQ publicly") diff --git a/automappa/app.py b/automappa/app.py index a5cc4895..cc0c87ef 100755 --- a/automappa/app.py +++ b/automappa/app.py @@ -1,11 +1,9 @@ -import os + import dash import dash_bootstrap_components as dbc import dash_uploader as du -from dotenv import load_dotenv - -load_dotenv() +from automappa.settings import server app = dash.Dash( name=__name__, @@ -15,4 +13,4 @@ suppress_callback_exceptions=True, ) -du.configure_upload(app, os.environ.get("UPLOAD_FOLDER_ROOT")) +du.configure_upload(app=app, folder=server.root_upload_folder) diff --git a/automappa/apps/home.py b/automappa/apps/home.py index b36cd3ef..4a2a799d 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -256,8 +256,15 @@ def on_upload_stores_data( # We need to ensure we prevent an update if there has not been one, otherwise all of our datastore # gets removed... samples = [] - for data_upload in [binning_uploads, markers_uploads, metagenome_uploads, samples_store_data]: - df = pd.read_json(data_upload, orient="split") if data_upload else pd.DataFrame() + for data_upload in [ + binning_uploads, + markers_uploads, + metagenome_uploads, + samples_store_data, + ]: + df = ( + pd.read_json(data_upload, orient="split") if data_upload else pd.DataFrame() + ) samples.append(df) samples_df = pd.concat(samples).drop_duplicates(subset=["table_id"]) diff --git a/automappa/db.py b/automappa/db.py index e35bfebb..cc0f7ea5 100644 --- a/automappa/db.py +++ b/automappa/db.py @@ -1,6 +1,6 @@ #!/usr/bin/env python -from sqlalchemy import create_engine,MetaData +from sqlalchemy import create_engine, MetaData from automappa.settings import db @@ -10,4 +10,4 @@ # pool_pre_ping=True, ) -metadata = MetaData(bind=engine) \ No newline at end of file +metadata = MetaData(bind=engine) diff --git a/automappa/environment/db.env b/automappa/environment/db.env deleted file mode 100644 index 722bbcdc..00000000 --- a/automappa/environment/db.env +++ /dev/null @@ -1,8 +0,0 @@ -UPLOAD_FOLDER_ROOT="${HOME}/.automappa/uploads" -USER="admin" -PASSWORD="mypass" -DB="automappa" -URL="postgresql+psycopg2://${USER}:${PASSWORD}@db:5432/${DB}" -DOCKER_URL="postgresql+psycopg2://${USER}:${PASSWORD}@db:5432/${DB}" -LOCAL_URL="postgresql+psycopg2://${USER}:${PASSWORD}@localhost:5432/${DB}" -POOL_SIZE=1 \ No newline at end of file diff --git a/automappa/environment/flower.env b/automappa/environment/flower.env deleted file mode 100644 index a0a3f05c..00000000 --- a/automappa/environment/flower.env +++ /dev/null @@ -1,7 +0,0 @@ -RABBITMQ_DEFAULT_USER="admin" -RABBITMQ_DEFAULT_PASS="mypass" -BACKEND_URL='redis://redis:6379/0' -BROKER_URL='amqp://admin:mypass@rabbit//' -CELERY_BROKER_URL="redis://127.0.0.1:6379/0" -REDIS_URL="redis://user:pass@localhost:6379/1" -RABBITMQ_URL="amqp://user:pass@localhost:5672/" \ No newline at end of file diff --git a/automappa/environment/rabbitmq.env b/automappa/environment/rabbitmq.env deleted file mode 100644 index 596e4590..00000000 --- a/automappa/environment/rabbitmq.env +++ /dev/null @@ -1,5 +0,0 @@ -URL="amqp://user:pass@rabbitmq:5672/" -DOCKER_URL="amqp://user:pass@rabbitmq:5672/" -LOCAL_URL="amqp://user:pass@localhost:5672/" -# TODO: Change to appropriate service name -# URL="amqp://user:pass@:5672/" \ No newline at end of file diff --git a/automappa/environment/server.env b/automappa/environment/server.env deleted file mode 100644 index 47f73a66..00000000 --- a/automappa/environment/server.env +++ /dev/null @@ -1,2 +0,0 @@ -UPLOAD_FOLDER_ROOT="${HOME}/.automappa/uploads" -DEBUG=True \ No newline at end of file diff --git a/automappa/settings.py b/automappa/settings.py index 65669dd2..3d3b9509 100644 --- a/automappa/settings.py +++ b/automappa/settings.py @@ -1,6 +1,5 @@ #!/usr/bin/env python -import os from pathlib import Path from typing import Optional from pydantic import ( @@ -13,48 +12,52 @@ # For the pydantic docs for defining settings, also See: # https://pydantic-docs.helpmanual.io/usage/settings/ + class DatabaseSettings(BaseSettings): # TODO: Figure out how to make the url dynamic to docker_url or local_url based on # is_docker_service... e.g. # url: PostgresDsn = docker_url if is_docker_service else local_url url: PostgresDsn - docker_url: Optional[PostgresDsn] - local_url: Optional[PostgresDsn] pool_size: Optional[int] = 3 - is_docker_service : bool = os.environ.get('PWD') == '/usr/src/app' class Config: - env_file: str = "automappa/environment/db.env" + env_prefix: str = "POSTGRES_" + env_file: str = ".env" env_file_encoding: str = "utf-8" + class RabbitmqSettings(BaseSettings): url: AmqpDsn + class Config: - env_file: str = "automappa/environment/rabbitmq.env" + env_prefix: str = "RABBITMQ_" + env_file: str = ".env" env_file_encoding: str = "utf-8" -class FlowerSettings(BaseSettings): +class CelerySettings(BaseSettings): backend_url: RedisDsn broker_url: AmqpDsn + class Config: - env_file: str = "automappa/environment/flower.env" + env_prefix: str = "CELERY_" + env_file: str = ".env" env_file_encoding: str = "utf-8" + class ServerSettings(BaseSettings): - """ - Settings of the application, used by workers and dashboard. - """ - upload_folder_root: Path + root_upload_folder: Path # Dash/Plotly debug: Optional[bool] = True + class Config: - """ - Meta configuration of the settings parser. - """ - env_file = "automappa/environment/server.env" + env_prefix = "SERVER_" + env_file: str = ".env" env_file_encoding = "utf-8" - + + server = ServerSettings() db = DatabaseSettings() -flower = FlowerSettings() \ No newline at end of file +database = DatabaseSettings() +rabbitmq = RabbitmqSettings() +celery = CelerySettings() diff --git a/automappa/tasks.py b/automappa/tasks.py index 3869d52f..0a4ef70a 100644 --- a/automappa/tasks.py +++ b/automappa/tasks.py @@ -10,17 +10,14 @@ from celery import Celery, chain from celery.result import AsyncResult -from dotenv import load_dotenv - from autometa.common.external import hmmscan from autometa.common.kmers import normalize, embed, count -load_dotenv() - -CELERY_REDIS_URL = os.environ.get("CELERY_REDIS_URL") -CELERY_BROKER_URL = os.environ.get("CELERY_BROKER_URL") +from automappa import settings -queue = Celery(__name__, backend=CELERY_REDIS_URL, broker=CELERY_BROKER_URL) +queue = Celery( + __name__, backend=settings.celery.backend_url, broker=settings.celery.broker_url +) logger = get_task_logger(__name__) diff --git a/automappa/utils/README.md b/automappa/utils/README.md index 4360e0cf..90d62166 100644 --- a/automappa/utils/README.md +++ b/automappa/utils/README.md @@ -2,7 +2,8 @@ ## Miscellaneous resources related to Automappa Utils -- [Mapping between python and postgresql types](https://pynative.com/python-postgresql-tutorial/#h-the-mapping-between-python-and-postgresql-types "Mapping between python and postgresql types") -- [Hackernoon/questdb-and-plotly]() -- [github.com:gabor-boros/questdb-stock-market-dashboard]() +- [:elephant: Mapping between python and postgresql types](https://pynative.com/python-postgresql-tutorial/#h-the-mapping-between-python-and-postgresql-types "Mapping between python and postgresql types") +- [:ninja: Hackernoon/questdb-and-plotly]() +- [:chart_with_upwards_trend: github.com:gabor-boros/questdb-stock-market-dashboard]() - [:tv: youtube: "Pydantic: Modern Python Data Validation and Settings by Michael Kennedy"](https://youtu.be/lon-dEXfY2I?t=1027 "Pydantic: Modern Python Data Validation and Settings by Michael Kennedy") +- [:sunflower: Celery+Flower+Prometheus+Grafana integration guide](https://flower.readthedocs.io/en/latest/prometheus-integration.html#celery-flower-prometheus-grafana-integration-guide "celery flower prometheus grafana integration guide") diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py index 74b8c6f2..769fc3a7 100644 --- a/automappa/utils/serializers.py +++ b/automappa/utils/serializers.py @@ -7,7 +7,7 @@ import uuid import pandas as pd -from automappa.db import engine,metadata +from automappa.db import engine, metadata from automappa.settings import server from autometa.common.markers import load as load_markers @@ -25,15 +25,13 @@ def get_uploaded_datatables() -> List[str]: return list(metadata.tables.keys()) + def get_uploaded_files_table() -> pd.DataFrame: df = pd.DataFrame() with engine.connect() as conn: tables = [table for table in metadata.table.keys() if "fileupload" in table] if tables: - df = pd.DataFrame([ - pd.read_sql(table, conn) - for table in tables - ]) + df = pd.DataFrame([pd.read_sql(table, conn) for table in tables]) return df @@ -191,7 +189,12 @@ def store_metagenome(filepath: Path, if_exists: str = "replace") -> str: return table_name -def save_to_db(filepath: Path, filetype: str, if_exists: str = "replace", rm_after_upload: bool = True) -> pd.DataFrame: +def save_to_db( + filepath: Path, + filetype: str, + if_exists: str = "replace", + rm_after_upload: bool = True, +) -> pd.DataFrame: """Store `filepath` to db table based on `filetype` Parameters @@ -222,7 +225,9 @@ def save_to_db(filepath: Path, filetype: str, if_exists: str = "replace", rm_aft "binning": store_binning_main, } if filetype not in filetype_store_methods: - raise ValueError(f"{filetype} not in filetype store methods {','.join(filetype_store_methods.keys())}") + raise ValueError( + f"{filetype} not in filetype store methods {','.join(filetype_store_methods.keys())}" + ) store_method = filetype_store_methods[filetype] try: table_id = store_method(filepath, if_exists=if_exists) @@ -246,7 +251,7 @@ def save_to_db(filepath: Path, filetype: str, if_exists: str = "replace", rm_aft } ] ) - + # Create table_name specific to uploaded file with the upload file metadata... # This should contain mapping to where the file contents are stored (i.e. table_id) table_name = f"{uuid.uuid4()}-fileupload" @@ -254,6 +259,7 @@ def save_to_db(filepath: Path, filetype: str, if_exists: str = "replace", rm_aft logger.debug(f"Saved {table_name} to db") return df + def validate_uploader(iscompleted: bool, filenames, upload_id) -> Path: """Ensure only one file was uploaded and create Path to uploaded file @@ -281,9 +287,9 @@ def validate_uploader(iscompleted: bool, filenames, upload_id) -> Path: if filenames is None: return if upload_id: - root_folder = server.upload_folder_root / upload_id + root_folder = server.root_upload_folder / upload_id else: - root_folder = server.upload_folder_root + root_folder = server.root_upload_folder uploaded_files = [] for filename in filenames: @@ -293,5 +299,6 @@ def validate_uploader(iscompleted: bool, filenames, upload_id) -> Path: raise ValueError("You may only upload one file at a time!") return uploaded_files[0] + if __name__ == "__main__": pass diff --git a/celery-monitoring-grafana-dashboard.json b/celery-monitoring-grafana-dashboard.json new file mode 100644 index 00000000..806910e3 --- /dev/null +++ b/celery-monitoring-grafana-dashboard.json @@ -0,0 +1,759 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.5.2" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "description": "Basic celery monitoring example", + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": null, + "links": [], + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "description": "This panel shows status of celery workers. 1 = online, 0 = offline.", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 0 + }, + "hiddenSeries": false, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "flower_worker_online", + "interval": "", + "legendFormat": "{{ worker }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Celery Worker Status", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:150", + "format": "short", + "label": "", + "logBase": 1, + "max": "1", + "min": "0", + "show": true + }, + { + "$$hashKey": "object:151", + "decimals": null, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "description": "This panel shows number of tasks currently executing at worker.", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 0 + }, + "hiddenSeries": false, + "id": 9, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "flower_worker_number_of_currently_executing_tasks", + "interval": "", + "legendFormat": "{{worker}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Number of Tasks Currently Executing at Worker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:79", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:80", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "description": "This panel shows average task runtime at worker by worker and task name.", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 8 + }, + "hiddenSeries": false, + "id": 11, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "rate(flower_task_runtime_seconds_sum[5m]) / rate(flower_task_runtime_seconds_count[5m])", + "interval": "", + "legendFormat": "{{task}}, {{worker}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Average Task Runtime at Worker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:337", + "format": "s", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:338", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "description": "This panel shows task prefetch time at worker by worker and task name.", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 17 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "flower_task_prefetch_time_seconds", + "interval": "", + "legendFormat": "{{task}}, {{worker}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Task Prefetch Time at Worker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:337", + "format": "s", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:338", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "description": "This panel shows number of tasks prefetched at worker by task and worker name.", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 24, + "x": 0, + "y": 26 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "alignAsTable": true, + "avg": true, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "flower_worker_prefetched_tasks", + "interval": "", + "legendFormat": "{{task}}, {{worker}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Number of Tasks Prefetched At Worker", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:337", + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:338", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "description": "This panel presents average task success ratio over time by task name.", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 35 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "(sum(avg_over_time(flower_events_total{type=\"task-succeeded\"}[15m])) by (task) / sum(avg_over_time(flower_events_total{type=~\"task-failed|task-succeeded\"}[15m])) by (task)) * 100", + "interval": "", + "legendFormat": "{{ task }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Task Success Ratio", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:63", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:64", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "description": "This panel presents average task failure ratio over time by task name.", + "fieldConfig": { + "defaults": {}, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 35 + }, + "hiddenSeries": false, + "id": 7, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": true, + "min": true, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.5.2", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "exemplar": true, + "expr": "(sum(avg_over_time(flower_events_total{type=\"task-failed\"}[15m])) by (task) / sum(avg_over_time(flower_events_total{type=~\"task-failed|task-succeeded\"}[15m])) by (task)) * 100", + "interval": "", + "legendFormat": "{{ task }}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Task Failure Ratio", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:63", + "format": "percent", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:64", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": false + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "10s", + "schemaVersion": 27, + "style": "dark", + "tags": [ + "celery", + "monitoring", + "flower" + ], + "templating": { + "list": [] + }, + "time": { + "from": "now-15m", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Celery Monitoring", + "uid": "3OBI1flGz", + "version": 9 +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 986ebae1..54ed7dc6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,31 +3,73 @@ version: '3.8' services: - db: + postgres: image: postgres restart: always ports: - 5432:5432 + env_file: + - .env volumes: - - pg_data:/var/lib/postgresql/data + - postgres-data:/var/lib/postgresql/data - # rabbitmq: - # image: rabbitmq:latest - # celery: - # build: - # context: . - # dockerfile: Dockerfile - # command: celery --app=automappa.tasks.queue worker --loglevel INFO - # volumes: - # - .:/usr/src/app - # flower: - # image: "mher/flower:latest" - # ports: - # - 1234:1234 - # redis: - # image: "redis:latest" - # ports: - # - 6379:6379 + rabbitmq: + image: rabbitmq:latest + restart: always + ports: + - 5672:5672 + env_file: + - .env + + celery: + build: + context: . + dockerfile: Dockerfile + restart: always + command: celery --app=automappa.tasks.queue worker --loglevel INFO --uid automappa -E + volumes: + - .:/usr/src/app + depends_on: + - redis + - rabbitmq + flower: + build: + context: . + dockerfile: Dockerfile + restart: always + command: celery --app=automappa.tasks.queue flower --port=5555 + volumes: + - .:/usr/src/app + ports: + - 49555:5555 + env_file: + - .env + depends_on: + - celery + + prometheus: + image: prom/prometheus:latest + ports: + - 9090:9090 + volumes: + - prometheus-data:/prometheus + # - ./prometheus.yml:/etc/prometheus/prometheus.yml + depends_on: + - flower + + grafana: + image: grafana/grafana:latest + ports: + - 3000:3000 + volumes: + - grafana-storage:/var/lib/grafana + depends_on: + - prometheus + + redis: + image: "redis:latest" + ports: + - 6379:6379 web: build: context: . @@ -42,11 +84,15 @@ services: ports: - 8050:8050 depends_on: - - db - # - rabbitmq - # - celery - # - redis + - postgres + - rabbitmq + - celery + - redis volumes: - pg_data: - driver: local \ No newline at end of file + postgres-data: + grafana-storage: + driver: local + prometheus-data: + driver: local + diff --git a/environment.yml b/environment.yml index db7b486b..a2270274 100644 --- a/environment.yml +++ b/environment.yml @@ -6,12 +6,12 @@ channels: - defaults dependencies: - autometa - - flask - - celery[redis] - dash - dash-bootstrap-components - dash-daq - dash-extensions + - flask + - flower - msgpack-python - numpy - pandas @@ -22,5 +22,6 @@ dependencies: - sqlalchemy - pip - pip: + - celery[redis] - geom-median - dash-uploader From 6e92c24e18645951bd7bcc8a41e19cc60e35733b Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Fri, 22 Apr 2022 01:34:40 -0400 Subject: [PATCH 16/37] Add dbc.Select to home for choosing metagenome annotations (WIP) :art: dbc.Select(...) are now populated with values from db tables :bug::art: Investigate whether serializer.get_uploaded_files_table() is functional (TODO) :whale: Add commented out arg to create_engine(..., pre_pool_ping: Optional[bool]) and db settings :whale: Add PRE_POOL_PING to .env --- .env | 1 + automappa/apps/home.py | 109 +++++++++++++++++++++++++++++++-- automappa/db.py | 2 +- automappa/settings.py | 1 + automappa/utils/serializers.py | 2 +- docker-compose.yml | 3 +- 6 files changed, 111 insertions(+), 7 deletions(-) diff --git a/.env b/.env index 062d7e50..850d4e6c 100644 --- a/.env +++ b/.env @@ -6,6 +6,7 @@ POSTGRES_PASSWORD="mypass" POSTGRES_DB="automappa" POSTGRES_URL="postgresql+psycopg2://${POSTGRES_USER}:${POSTGRES_PASSWORD}@postgres:5432/${POSTGRES_DB}" POSTGRES_POOL_SIZE=1 +POSTGRES_POOL_PRE_PING=False RABBITMQ_DEFAULT_USER="user" RABBITMQ_DEFAULT_PASS="pass" RABBITMQ_URL="amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/" diff --git a/automappa/apps/home.py b/automappa/apps/home.py index 4a2a799d..e655e8b1 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -128,10 +128,6 @@ ], ) -# html.Div(id="output-binning-main-data-upload"), -html.Div(id="output-markers-data-upload"), -html.Div(id="output-metagenome-data-upload"), - example_card = dbc.Card( [ dbc.CardHeader(id="binning-main-samples-cardheader"), @@ -201,6 +197,21 @@ ), ) +markers_select = dbc.Select( + id="markers-select", + placeholder="Select marker set annotations", +) + +metagenome_select = dbc.Select( + id="metagenome-select", + placeholder="Select metagenome annotations", +) + +binning_select = dbc.Select( + id="binning-select", + placeholder="Select binning annotations", +) + layout = dbc.Container( children=[ binning_main_upload_store, @@ -211,6 +222,14 @@ html.Br(), # row_example_cards, dbc.Row(samples_datatable), + html.Br(), + dbc.Row([ + dbc.Col(binning_select), + dbc.Col(markers_select), + dbc.Col(metagenome_select), + ]), + html.Br(), + dbc.Row(dbc.Button("Refine MAGs")), ], fluid=True, ) @@ -272,6 +291,88 @@ def on_upload_stores_data( return samples_df.to_json(orient="split") +@app.callback( + Output("binning-select", "options"), + [Input("samples-store", "data")], + State("samples-store", "data"), +) +def binning_select_options(samples_store_data, new_samples_store_data): + samples_df = pd.read_json(samples_store_data, orient="split") + if new_samples_store_data is not None: + new_samples_df = pd.read_json(new_samples_store_data, orient="split") + samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( + subset=["table_id"] + ) + + if samples_df.empty: + raise PreventUpdate + + # {"label": filename+truncated-hash, "value": table_id} + df = samples_df.loc[samples_df.filetype.eq('binning')] + logger.debug(f"{df.shape[0]:,} binning available for mag_refinement") + return [ + { + "label":filename, + "value": table_id, + } + for filename,table_id in + zip(df.filename.tolist(), df.table_id.tolist()) + ] + +@app.callback( + Output("markers-select", "options"), + [Input("samples-store", "data")], + State("samples-store", "data"), +) +def markers_select_options(samples_store_data, new_samples_store_data): + samples_df = pd.read_json(samples_store_data, orient="split") + if new_samples_store_data is not None: + new_samples_df = pd.read_json(new_samples_store_data, orient="split") + samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( + subset=["table_id"] + ) + + if samples_df.empty: + raise PreventUpdate + + # {"label": filename+truncated-hash, "value": table_id} + markers_samples = samples_df.loc[samples_df.filetype.eq('markers')] + logger.debug(f"{markers_samples.shape[0]:,} markers available for mag_refinement") + return [ + { + "label":filename, + "value": table_id, + } + for filename,table_id in + zip(markers_samples.filename.tolist(), markers_samples.table_id.tolist()) + ] + +@app.callback( + Output("metagenome-select", "options"), + [Input("samples-store", "data")], + State("samples-store", "data"), +) +def metagenome_select_options(samples_store_data, new_samples_store_data): + samples_df = pd.read_json(samples_store_data, orient="split") + if new_samples_store_data is not None: + new_samples_df = pd.read_json(new_samples_store_data, orient="split") + samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( + subset=["table_id"] + ) + + if samples_df.empty: + raise PreventUpdate + + df = samples_df.loc[samples_df.filetype.eq('metagenome')] + logger.debug(f"{df.shape[0]:,} metagenomes available for mag_refinement") + return [ + { + "label":filename, + "value": table_id, + } + for filename,table_id in + zip(df.filename.tolist(), df.table_id.tolist()) + ] @app.callback( Output("samples-datatable", "children"), diff --git a/automappa/db.py b/automappa/db.py index cc0f7ea5..7786eb3d 100644 --- a/automappa/db.py +++ b/automappa/db.py @@ -7,7 +7,7 @@ engine = create_engine( url=db.url, pool_size=db.pool_size, - # pool_pre_ping=True, + pool_pre_ping=db.pool_pre_ping, ) metadata = MetaData(bind=engine) diff --git a/automappa/settings.py b/automappa/settings.py index 3d3b9509..847093fc 100644 --- a/automappa/settings.py +++ b/automappa/settings.py @@ -19,6 +19,7 @@ class DatabaseSettings(BaseSettings): # url: PostgresDsn = docker_url if is_docker_service else local_url url: PostgresDsn pool_size: Optional[int] = 3 + pool_pre_ping: Optional[bool] = False class Config: env_prefix: str = "POSTGRES_" diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py index 769fc3a7..a51ed882 100644 --- a/automappa/utils/serializers.py +++ b/automappa/utils/serializers.py @@ -28,8 +28,8 @@ def get_uploaded_datatables() -> List[str]: def get_uploaded_files_table() -> pd.DataFrame: df = pd.DataFrame() + tables = [table for table in metadata.tables.keys() if "fileupload" in table] with engine.connect() as conn: - tables = [table for table in metadata.table.keys() if "fileupload" in table] if tables: df = pd.DataFrame([pd.read_sql(table, conn) for table in tables]) return df diff --git a/docker-compose.yml b/docker-compose.yml index 54ed7dc6..e2047d29 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -41,7 +41,7 @@ services: volumes: - .:/usr/src/app ports: - - 49555:5555 + - 5555:5555 env_file: - .env depends_on: @@ -74,6 +74,7 @@ services: build: context: . dockerfile: Dockerfile + restart: always # command: automappa --debug --port 8050 # This command allows live-reloading for dev # Not sure which is more appropriate for production :shrug: From c6fd9da17d601fa900807d35731f3ed6750a1a6e Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Fri, 22 Apr 2022 12:31:26 -0400 Subject: [PATCH 17/37] :art: WIP refinement page reading user uploaded/selected data :fire: Remove (now deprecated) stores :art: Add serialization of a hash-refinement table when hash-binning file is uploaded (respective to hash-binning) :art: Remove --debug arg from parser. Move statement to .env (now handle read with automappa.settings.server.debug :fire: remove unused imports in index.py :art: Move samples stores from home.py to index.py Add dbc.InputGroup(...) with selects for binning, markers and metagenome :art: Add dbc.Button(...) to save table-ids in 'selected-tables-store' to be read from mag_refinement.py :art::bug: Continue refactor of get_scatterplot_2d(...) to use other embedding methods :art: Add get_marker_symbols(...) to tasks.py and automappa.utils.markers (WIP currently being used to *expensively* generate symbols during 2d scatterplot creation --- automappa/app.py | 1 - automappa/apps/home.py | 123 +++++++++++++++++++---------- automappa/apps/mag_refinement.py | 83 ++++++++++++-------- automappa/index.py | 130 ++++++++----------------------- automappa/settings.py | 4 +- automappa/tasks.py | 13 ++++ automappa/utils/figures.py | 5 +- automappa/utils/markers.py | 6 ++ automappa/utils/serializers.py | 21 ++++- docker-compose.yml | 2 +- 10 files changed, 213 insertions(+), 175 deletions(-) diff --git a/automappa/app.py b/automappa/app.py index cc0c87ef..6434d6c3 100755 --- a/automappa/app.py +++ b/automappa/app.py @@ -1,4 +1,3 @@ - import dash import dash_bootstrap_components as dbc import dash_uploader as du diff --git a/automappa/apps/home.py b/automappa/apps/home.py index e655e8b1..8477dfc8 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -180,12 +180,12 @@ ] ) -binning_main_upload_store = dcc.Store( - id="binning-main-upload-store", storage_type="local" -) -markers_upload_store = dcc.Store(id="markers-upload-store", storage_type="local") -metagenome_upload_store = dcc.Store(id="metagenome-upload-store", storage_type="local") -samples_store = dcc.Store(id="samples-store", storage_type="local") +# binning_main_upload_store = dcc.Store( +# id="binning-main-upload-store", storage_type="local" +# ) +# markers_upload_store = dcc.Store(id="markers-upload-store", storage_type="local") +# metagenome_upload_store = dcc.Store(id="metagenome-upload-store", storage_type="local") +# samples_store = dcc.Store(id="samples-store", storage_type="local") # samples_datatable = html.Div(id="samples-datatable") samples_datatable = ( @@ -197,39 +197,57 @@ ), ) -markers_select = dbc.Select( - id="markers-select", - placeholder="Select marker set annotations", -) - -metagenome_select = dbc.Select( - id="metagenome-select", - placeholder="Select metagenome annotations", +refine_mags_input_groups = html.Div( + [ + dbc.InputGroup( + [ + dbc.InputGroupText("Binning"), + dbc.Select( + id="binning-select", + placeholder="Select binning annotations", + ), + ] + ), + dbc.InputGroup( + [ + dbc.InputGroupText("Markers"), + dbc.Select( + id="markers-select", + placeholder="Select marker annotations", + ), + ] + ), + dbc.InputGroup( + [ + dbc.InputGroupText("Metagenome"), + dbc.Select( + id="metagenome-select", + placeholder="Select metagenome annotations", + ), + ] + ), + ] ) -binning_select = dbc.Select( - id="binning-select", - placeholder="Select binning annotations", +refine_mags_button = dbc.Button( + id="refine-mags-button", + children="Refine MAGs", ) layout = dbc.Container( children=[ - binning_main_upload_store, - markers_upload_store, - metagenome_upload_store, - samples_store, + # binning_main_upload_store, + # markers_upload_store, + # metagenome_upload_store, + # samples_store, dbc.Row(upload_modal), html.Br(), # row_example_cards, dbc.Row(samples_datatable), html.Br(), - dbc.Row([ - dbc.Col(binning_select), - dbc.Col(markers_select), - dbc.Col(metagenome_select), - ]), + dbc.Row(refine_mags_input_groups), html.Br(), - dbc.Row(dbc.Button("Refine MAGs")), + dbc.Row(refine_mags_button), ], fluid=True, ) @@ -291,6 +309,7 @@ def on_upload_stores_data( return samples_df.to_json(orient="split") + @app.callback( Output("binning-select", "options"), [Input("samples-store", "data")], @@ -308,17 +327,17 @@ def binning_select_options(samples_store_data, new_samples_store_data): raise PreventUpdate # {"label": filename+truncated-hash, "value": table_id} - df = samples_df.loc[samples_df.filetype.eq('binning')] + df = samples_df.loc[samples_df.filetype.eq("binning")] logger.debug(f"{df.shape[0]:,} binning available for mag_refinement") return [ { - "label":filename, + "label": filename, "value": table_id, } - for filename,table_id in - zip(df.filename.tolist(), df.table_id.tolist()) + for filename, table_id in zip(df.filename.tolist(), df.table_id.tolist()) ] + @app.callback( Output("markers-select", "options"), [Input("samples-store", "data")], @@ -336,17 +355,19 @@ def markers_select_options(samples_store_data, new_samples_store_data): raise PreventUpdate # {"label": filename+truncated-hash, "value": table_id} - markers_samples = samples_df.loc[samples_df.filetype.eq('markers')] + markers_samples = samples_df.loc[samples_df.filetype.eq("markers")] logger.debug(f"{markers_samples.shape[0]:,} markers available for mag_refinement") return [ { - "label":filename, + "label": filename, "value": table_id, } - for filename,table_id in - zip(markers_samples.filename.tolist(), markers_samples.table_id.tolist()) + for filename, table_id in zip( + markers_samples.filename.tolist(), markers_samples.table_id.tolist() + ) ] + @app.callback( Output("metagenome-select", "options"), [Input("samples-store", "data")], @@ -363,17 +384,17 @@ def metagenome_select_options(samples_store_data, new_samples_store_data): if samples_df.empty: raise PreventUpdate - df = samples_df.loc[samples_df.filetype.eq('metagenome')] + df = samples_df.loc[samples_df.filetype.eq("metagenome")] logger.debug(f"{df.shape[0]:,} metagenomes available for mag_refinement") return [ { - "label":filename, + "label": filename, "value": table_id, } - for filename,table_id in - zip(df.filename.tolist(), df.table_id.tolist()) + for filename, table_id in zip(df.filename.tolist(), df.table_id.tolist()) ] + @app.callback( Output("samples-datatable", "children"), [Input("samples-store", "data")], @@ -474,3 +495,27 @@ def toggle_modal(n_open, n_close, is_open): if n_open or n_close: return not is_open return is_open + + +# TODO: Disable refine-mags button when not all select values are provided + + +@app.callback( + Output("selected-tables-store", "data"), + [ + Input("refine-mags-button", "n_clicks"), + Input("binning-select", "value"), + Input("markers-select", "value"), + Input("metagenome-select", "value"), + ], +) +def on_refine_mags_button_click( + n, binning_select_value, markers_select_value, metagenome_select_value +): + if n is None: + raise PreventUpdate + return { + "binning": binning_select_value, + "markers": markers_select_value, + "metagenome": metagenome_select_value, + } diff --git a/automappa/apps/mag_refinement.py b/automappa/apps/mag_refinement.py index 802d9ba8..ca8938ae 100644 --- a/automappa/apps/mag_refinement.py +++ b/automappa/apps/mag_refinement.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import logging from typing import Dict, List import pandas as pd @@ -18,8 +19,9 @@ from automappa.app import app +# from automappa.tasks import get_marker_symbols from automappa.utils.serializers import get_table - +from automappa.utils.markers import get_marker_symbols from automappa.utils.figures import ( format_axis_title, get_scatterplot_2d, @@ -28,6 +30,13 @@ metric_boxplot, ) +logging.basicConfig( + format="[%(levelname)s] %(name)s: %(message)s", + level=logging.DEBUG, +) + +logger = logging.getLogger(__name__) + pio.templates.default = "plotly_white" @@ -459,16 +468,16 @@ def color_by_column_options_callback(annotations_json: "str | None"): @app.callback( Output("mag-metrics-datatable", "children"), [ - Input("markers-store", "data"), + Input("selected-tables-store", "data"), Input("scatterplot-2d", "selectedData"), ], ) def update_mag_metrics_datatable_callback( - markers_json: "str | None", selected_contigs: Dict[str, List[Dict[str, str]]] + selected_tables_data: Dict[str, str], + selected_contigs: Dict[str, List[Dict[str, str]]], ) -> DataTable: - markers_df = pd.read_json(markers_json, orient="split").set_index("contig") - # TODO: Replace pd.read_json get_table(table_name) - # TODO: Need to pass table_name from home.py... + table_name = selected_tables_data["markers"] + markers_df = get_table(table_name, index_col="contig") if selected_contigs: contigs = {point["text"] for point in selected_contigs["points"]} selected_contigs_count = len(contigs) @@ -541,9 +550,9 @@ def update_mag_metrics_datatable_callback( @app.callback( Output("scatterplot-2d", "figure"), [ - Input("metagenome-annotations", "data"), - Input("refinement-data", "data"), - Input("contig-marker-symbols-store", "data"), + Input("selected-tables-store", "data"), + # Input("refinement-data", "data"), + # Input("contig-marker-symbols-store", "data"), Input("x-axis-2d", "value"), Input("y-axis-2d", "value"), Input("scatterplot-2d-legend-toggle", "value"), @@ -552,9 +561,9 @@ def update_mag_metrics_datatable_callback( ], ) def scatterplot_2d_figure_callback( - annotations: "str | None", - refinement: "str | None", - contig_marker_symbols_json: "str | None", + selected_tables_data: Dict[str, str], + # refinement: "str | None", + # contig_marker_symbols_json: "str | None", xaxis_column: str, yaxis_column: str, show_legend: bool, @@ -562,10 +571,14 @@ def scatterplot_2d_figure_callback( hide_selection_toggle: bool, ) -> go.Figure: # TODO: #23 refactor scatterplot callbacks - bin_df = pd.read_json(annotations, orient="split").set_index("contig") - markers = pd.read_json(contig_marker_symbols_json, orient="split").set_index( - "contig" - ) + bin_table_name = selected_tables_data["binning"] + bin_df = get_table(bin_table_name, index_col="contig") + markers_table_name = selected_tables_data["markers"] + markers_df = get_table(markers_table_name, index_col="contig") + markers = get_marker_symbols(bin_df, markers_df) + # markers = pd.read_json(contig_marker_symbols_json, orient="split").set_index( + # "contig" + # ) if color_by_col not in bin_df.columns: for col in ["phylum", "class", "order", "family"]: if col in bin_df.columns: @@ -578,7 +591,8 @@ def scatterplot_2d_figure_callback( # Subset metagenome-annotations by selections iff selections have been made if hide_selection_toggle: - refine_df = pd.read_json(refinement, orient="split").set_index("contig") + refine_table_name = selected_tables_data["binning"].replace("-binning","-refinement") + refine_df = get_table(refine_table_name, index_col="contig") refine_cols = [col for col in refine_df.columns if "refinement" in col] if refine_cols: latest_refine_col = refine_cols.pop() @@ -620,17 +634,18 @@ def scatterplot_2d_figure_callback( @app.callback( Output("taxonomy-distribution", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("scatterplot-2d", "selectedData"), Input("taxonomy-distribution-dropdown", "value"), ], ) def taxonomy_distribution_figure_callback( - annotations: "str | None", + selected_tables_data: Dict[str, str], selected_contigs: Dict[str, List[Dict[str, str]]], selected_rank: str, ) -> go.Figure: - df = pd.read_json(annotations, orient="split") + table_name = selected_tables_data["binning"] + df = get_table(table_name) if selected_contigs and selected_contigs["points"]: contigs = {point["text"] for point in selected_contigs["points"]} df = df[df.contig.isin[contigs]] @@ -641,7 +656,7 @@ def taxonomy_distribution_figure_callback( @app.callback( Output("scatterplot-3d", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("scatterplot-3d-zaxis-dropdown", "value"), Input("scatterplot-3d-legend-toggle", "value"), Input("color-by-column", "value"), @@ -649,13 +664,14 @@ def taxonomy_distribution_figure_callback( ], ) def scatterplot_3d_figure_callback( - annotations: "str | None", + selected_tables_data: Dict[str, str], z_axis: str, show_legend: bool, color_by_col: str, selected_contigs: Dict[str, List[Dict[str, str]]], ) -> go.Figure: - df = pd.read_json(annotations, orient="split") + table_name = selected_tables_data["binning"] + df = get_table(table_name) color_by_col = "phylum" if color_by_col not in df.columns else color_by_col if not selected_contigs: contigs = set(df.contig.tolist()) @@ -684,16 +700,17 @@ def scatterplot_3d_figure_callback( @app.callback( Output("mag-refinement-coverage-boxplot", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("scatterplot-2d", "selectedData"), ], ) def mag_summary_coverage_boxplot_callback( - df_json: "str | None", selected_data: Dict[str, List[Dict[str, str]]] + selected_tables_data: Dict[str, str], selected_data: Dict[str, List[Dict[str, str]]] ) -> go.Figure: - df = pd.read_json(df_json, orient="split") if not selected_data: raise PreventUpdate + table_name = selected_tables_data["binning"] + df = get_table(table_name) contigs = {point["text"] for point in selected_data["points"]} df = df.loc[df.contig.isin(contigs)] fig = metric_boxplot(df, metrics=["coverage"], boxmean="sd") @@ -703,16 +720,17 @@ def mag_summary_coverage_boxplot_callback( @app.callback( Output("mag-refinement-gc-content-boxplot", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("scatterplot-2d", "selectedData"), ], ) def mag_summary_gc_content_boxplot_callback( - df_json: "str | None", selected_data: Dict[str, List[Dict[str, str]]] + selected_tables_data: Dict[str, str], selected_data: Dict[str, List[Dict[str, str]]] ) -> go.Figure: - df = pd.read_json(df_json, orient="split") if not selected_data: raise PreventUpdate + table_name = selected_tables_data["binning"] + df = get_table(table_name) contigs = {point["text"] for point in selected_data["points"]} df = df.loc[df.contig.isin(contigs)] fig = metric_boxplot(df, metrics=["gc_content"], boxmean="sd") @@ -723,16 +741,17 @@ def mag_summary_gc_content_boxplot_callback( @app.callback( Output("mag-refinement-length-boxplot", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("scatterplot-2d", "selectedData"), ], ) def mag_summary_length_boxplot_callback( - df_json: "str | None", selected_data: Dict[str, List[Dict[str, str]]] + selected_tables_data: Dict[str, str], selected_data: Dict[str, List[Dict[str, str]]] ) -> go.Figure: - df = pd.read_json(df_json, orient="split") if not selected_data: raise PreventUpdate + table_name = selected_tables_data["binning"] + df = get_table(table_name) contigs = {point["text"] for point in selected_data["points"]} df = df.loc[df.contig.isin(contigs)] fig = metric_boxplot(df, metrics=["length"]) diff --git a/automappa/index.py b/automappa/index.py index 874db22c..2ca2c62c 100755 --- a/automappa/index.py +++ b/automappa/index.py @@ -2,21 +2,13 @@ # -*- coding: utf-8 -*- import argparse -import os import logging from dash.dependencies import Input, Output from dash import dcc, html import dash_bootstrap_components as dbc -import pandas as pd - -from autometa.common.markers import load as load_markers - -from automappa.utils.markers import ( - convert_marker_counts_to_marker_symbols, - get_contig_marker_counts, -) +from automappa.settings import server from automappa.apps import home, mag_refinement, mag_summary from automappa.app import app @@ -84,9 +76,9 @@ def main(): "The type of the web storage. (default: %(default)s)\n" "- memory: only kept in memory, reset on page refresh.\n" "- session: data is cleared once the browser quit.\n" - "- local: data is kept after the browser quit. (Currently not supported)\n" + "- local: data is kept after the browser quit.\n" ), - choices=["memory", "session"], + choices=["memory", "session", "local"], default="session", ) parser.add_argument( @@ -98,92 +90,39 @@ def main(): action="store_true", default=False, ) - parser.add_argument( - "--debug", - help="Turn on debug mode", - action="store_true", - default=False, - ) args = parser.parse_args() - # logger.info("Please wait a moment while all of the data is loaded.") - # Needed separately for binning refinement selections. - # binning = pd.read_csv(args.binning_main, sep="\t", low_memory=False) - # Needed for completeness/purity calculations - # markers = load_markers(args.markers).reset_index().copy() - - # Check dataset size for dcc.Store(...) with browser limits... - # For details see: https://stackoverflow.com/a/61018107 and https://arty.name/localstorage.html - # chrome_browser_quota = 5_200_000 - # dataset_chars = len(binning.to_json(orient="split")) - # if dataset_chars >= chrome_browser_quota: - # logger.warning( - # f"{args.binning_main} exceeds browser storage limits ({dataset_chars:,} > {chrome_browser_quota:,})." - # ) - # logger.warning("Persisting refinements is DISABLED!") - - # browser_storage_toast = dbc.Toast( - # f"{args.binning_main} exceeds browser storage limits ({dataset_chars:,} > {chrome_browser_quota:,}).", - # id="positioned-toast", - # header="Persisting refinements DISABLED", - # is_open=True, - # dismissable=True, - # icon="danger", - # # top: 66 positions the toast below the navbar - # style={"position": "fixed", "top": 66, "right": 10, "width": 350}, - # ) - # else: - # browser_storage_toast = dbc.Toast(is_open=False) - - # Metagenome Annotations Store - # metagenome_annotations_store = dcc.Store( - # id="metagenome-annotations", - # storage_type=args.storage_type, - # data=binning.to_json(orient="split"), - # clear_data=args.clear_store_data, - # ) - - # # Kingdom Markers Store - # markers_store = dcc.Store( - # id="markers-store", - # storage_type=args.storage_type, - # data=markers.to_json(orient="split"), - # clear_data=args.clear_store_data, - # ) - # # MAG Refinement Data Store - # # NOTE: MAG refinement columns are enumerated (1-indexed) and prepended with 'refinement_' - # if "cluster" not in binning.columns: - # binning["cluster"] = "unclustered" - # else: - # binning["cluster"].fillna("unclustered", inplace=True) - - # binning_cols = [ - # col - # for col in binning.columns - # if "refinement_" in col or "cluster" in col or "contig" in col - # ] - - # refinement_data_store = dcc.Store( - # id="refinement-data", - # storage_type=args.storage_type, - # data=binning[binning_cols].to_json(orient="split"), - # clear_data=args.clear_store_data, - # ) - - # # Contig Marker Symbols Store - # contig_marker_counts = get_contig_marker_counts( - # binning.set_index("contig"), markers.set_index("contig") - # ) - # contig_marker_symbols = convert_marker_counts_to_marker_symbols( - # contig_marker_counts - # ).reset_index() - # contig_marker_symbols_store = dcc.Store( # id="contig-marker-symbols-store", # storage_type=args.storage_type, # data=contig_marker_symbols.to_json(orient="split"), # clear_data=args.clear_store_data, # ) + binning_main_upload_store = dcc.Store( + id="binning-main-upload-store", + storage_type=args.storage_type, + clear_data=args.clear_store_data, + ) + markers_upload_store = dcc.Store( + id="markers-upload-store", + storage_type=args.storage_type, + clear_data=args.clear_store_data, + ) + metagenome_upload_store = dcc.Store( + id="metagenome-upload-store", + storage_type=args.storage_type, + clear_data=args.clear_store_data, + ) + samples_store = dcc.Store( + id="samples-store", + storage_type=args.storage_type, + clear_data=args.clear_store_data, + ) + selected_samples_store = dcc.Store( + id="selected-tables-store", + storage_type=args.storage_type, + clear_data=args.clear_store_data, + ) if args.clear_store_data: logger.info( @@ -191,12 +130,6 @@ def main(): ) exit() - # logger.info(f"binning shape:\t\t{binning.shape}") - # logger.info(f"markers shape:\t\t{markers.shape}") - # logger.info( - # "Data loaded. It may take a minute or two to construct all interactive graphs..." - # ) - home_tab = dbc.Tab(label="Home", tab_id="home") refinement_tab = dbc.Tab(label="MAG Refinement", tab_id="mag_refinement") summary_tab = dbc.Tab(label="MAG Summary", tab_id="mag_summary") @@ -207,6 +140,11 @@ def main(): # dbc.Col(metagenome_annotations_store), # dbc.Col(refinement_data_store), # dbc.Col(contig_marker_symbols_store), + dbc.Col(binning_main_upload_store), + dbc.Col(markers_upload_store), + dbc.Col(metagenome_upload_store), + dbc.Col(samples_store), + dbc.Col(selected_samples_store), # Navbar dbc.Tabs( id="tabs", @@ -225,7 +163,7 @@ def main(): # sample_name = os.path.basename(args.binning_main).replace(" ", "_").split(".")[0] # app.title = f"Automappa: {sample_name}" app.title = "Automappa" - app.run_server(host=args.host, port=args.port, debug=args.debug) + app.run_server(host=args.host, port=args.port, debug=server.debug) if __name__ == "__main__": diff --git a/automappa/settings.py b/automappa/settings.py index 847093fc..96bc09b4 100644 --- a/automappa/settings.py +++ b/automappa/settings.py @@ -59,6 +59,6 @@ class Config: server = ServerSettings() db = DatabaseSettings() -database = DatabaseSettings() -rabbitmq = RabbitmqSettings() +database = DatabaseSettings() +rabbitmq = RabbitmqSettings() celery = CelerySettings() diff --git a/automappa/tasks.py b/automappa/tasks.py index 0a4ef70a..f7c7e5b2 100644 --- a/automappa/tasks.py +++ b/automappa/tasks.py @@ -15,6 +15,12 @@ from automappa import settings +from automappa.utils.markers import ( + convert_marker_counts_to_marker_symbols, + get_contig_marker_counts, +) + + queue = Celery( __name__, backend=settings.celery.backend_url, broker=settings.celery.broker_url ) @@ -30,6 +36,13 @@ def get_job(job_id): return AsyncResult(job_id, app=queue) +# @queue.task +def get_marker_symbols(bin_df: pd.DataFrame, markers_df: pd.DataFrame) -> pd.DataFrame: + marker_counts = get_contig_marker_counts(bin_df, markers_df) + marker_symbols = convert_marker_counts_to_marker_symbols(marker_counts) + return marker_symbols + + # TODO: Data loader # TODO: Create 2d-scatterplot figure # TODO: Marker symbols diff --git a/automappa/utils/figures.py b/automappa/utils/figures.py index 0b676cbc..774295f2 100644 --- a/automappa/utils/figures.py +++ b/automappa/utils/figures.py @@ -206,9 +206,10 @@ def get_scattergl_traces( x_axis=x_axis, y_axis=y_axis ) traces = [] + metadata_cols = [col for col in metadata_cols if col in df.columns] for color_col_name in df[color_by_col].fillna(fillna).unique(): dff = df.loc[df[color_by_col].eq(color_col_name)] - customdata = dff[metadata_cols] if metadata_cols in dff.columns else [] + customdata = dff[metadata_cols] if metadata_cols else [] trace = go.Scattergl( x=dff[x_axis], y=dff[y_axis], @@ -263,7 +264,7 @@ def get_scatterplot_2d( df: pd.DataFrame, x_axis: str, y_axis: str, - embed_method: str, + # embed_method: str, color_by_col: str = "cluster", fillna: str = "unclustered", ) -> go.Figure: diff --git a/automappa/utils/markers.py b/automappa/utils/markers.py index 6ed8c3d4..ac4d394e 100644 --- a/automappa/utils/markers.py +++ b/automappa/utils/markers.py @@ -62,3 +62,9 @@ def convert_marker_counts_to_marker_symbols(df: pd.DataFrame) -> pd.DataFrame: df["symbol"] = df.marker_count.map(lambda count: symbols.get(count, "circle")) df["marker_size"] = df.marker_count.fillna(0).map(lambda count: count + 7) return df + + +def get_marker_symbols(bin_df: pd.DataFrame, markers_df: pd.DataFrame) -> pd.DataFrame: + marker_counts = get_contig_marker_counts(bin_df, markers_df) + marker_symbols = convert_marker_counts_to_marker_symbols(marker_counts) + return marker_symbols diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py index a51ed882..770a4d87 100644 --- a/automappa/utils/serializers.py +++ b/automappa/utils/serializers.py @@ -35,9 +35,9 @@ def get_uploaded_files_table() -> pd.DataFrame: return df -def get_table(table_name: str, index_col: Optional[str]) -> pd.DataFrame: +def get_table(table_name: str, index_col: Optional[str] = None) -> pd.DataFrame: if not engine.has_table(table_name): - tables = metadata.table.keys() + tables = metadata.tables.keys() raise ValueError(f"{table_name} not in database! available: {tables}") df = pd.read_sql(table_name, engine) if index_col: @@ -106,6 +106,23 @@ def store_binning_main(filepath: Path, if_exists: str = "replace") -> str: # NOTE: table_name must not exceed maximum length of 63 characters df.to_sql(table_name, engine, if_exists=if_exists, index=False) logger.debug(f"Saved {df.shape[0]:,} contigs to postgres table: {table_name}") + # MAG Refinement Data Store + # TODO: Refactor this to move it out of store_binning(...) + # Should be another celery task... + # NOTE: MAG refinement columns are enumerated (1-indexed) and prepended with 'refinement_' + if "cluster" not in df.columns: + df["cluster"] = "unclustered" + else: + df["cluster"].fillna("unclustered", inplace=True) + + df_cols = [ + col + for col in df.columns + if "refinement_" in col or "cluster" in col or "contig" in col + ] + refinement_table_name = table_name.replace("-binning", "-refinement") + df[df_cols].to_sql(refinement_table_name, engine, if_exists=if_exists, index=False) + logger.debug(f"Saved {df[df_cols]} refinements to postgres table: {refinement_table_name}") return table_name diff --git a/docker-compose.yml b/docker-compose.yml index e2047d29..96242be7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -78,7 +78,7 @@ services: # command: automappa --debug --port 8050 # This command allows live-reloading for dev # Not sure which is more appropriate for production :shrug: - command: python -m automappa.index --debug --port 8050 + command: python -m automappa.index --port 8050 volumes: # /usr/src/app is location of install in Dockerfile - .:/usr/src/app From dc5665d2052436748abc9b8546b3cea1f4a3c648 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Fri, 22 Apr 2022 12:45:35 -0400 Subject: [PATCH 18/37] :art: Move port and host argparse args to .env :fire: Remove unused (commented out) argparse args :fire::art::whale: Update automappa-web service command to not use removed argparse args --- .env | 2 ++ automappa/index.py | 37 +------------------------------------ automappa/settings.py | 2 ++ docker-compose.yml | 6 +++--- 4 files changed, 8 insertions(+), 39 deletions(-) diff --git a/.env b/.env index 850d4e6c..0f908f61 100644 --- a/.env +++ b/.env @@ -1,5 +1,7 @@ # Required for docker-compose.yml postgres service SERVER_ROOT_UPLOAD_FOLDER="${HOME}/.automappa/uploads" +SERVER_HOST="0.0.0.0" +SERVER_PORT=8050 SERVER_DEBUG=True POSTGRES_USER="admin" POSTGRES_PASSWORD="mypass" diff --git a/automappa/index.py b/automappa/index.py index 2ca2c62c..c87c3a2f 100755 --- a/automappa/index.py +++ b/automappa/index.py @@ -35,41 +35,6 @@ def main(): description="Automappa: An interactive interface for exploration of metagenomes", formatter_class=argparse.RawTextHelpFormatter, ) - # parser.add_argument( - # "--binning-main", - # help="Path to --binning-main output of Autometa binning/recruitment results", - # type=str, - # metavar="filepath", - # required=True, - # ) - # parser.add_argument( - # "--markers", - # help="Path to Autometa-formatted markers table (may be taxon-specific)", - # type=str, - # metavar="filepath", - # required=True, - # ) - # parser.add_argument( - # "--fasta", - # help="Path to metagenome.fasta", - # type=str, - # metavar="filepath", - # required=False, - # ) - parser.add_argument( - "--port", - help="port to expose. (default: %(default)s)", - default=8050, - type=int, - metavar="number", - ) - parser.add_argument( - "--host", - help="host ip address to expose. (default: %(default)s)", - type=str, - default="0.0.0.0", - metavar="ip address", - ) parser.add_argument( "--storage-type", help=( @@ -163,7 +128,7 @@ def main(): # sample_name = os.path.basename(args.binning_main).replace(" ", "_").split(".")[0] # app.title = f"Automappa: {sample_name}" app.title = "Automappa" - app.run_server(host=args.host, port=args.port, debug=server.debug) + app.run_server(host=server.host, port=server.port, debug=server.debug) if __name__ == "__main__": diff --git a/automappa/settings.py b/automappa/settings.py index 96bc09b4..31850f79 100644 --- a/automappa/settings.py +++ b/automappa/settings.py @@ -49,6 +49,8 @@ class Config: class ServerSettings(BaseSettings): root_upload_folder: Path # Dash/Plotly + host: Optional[str] = "localhost" + port: Optional[int] = 8050 debug: Optional[bool] = True class Config: diff --git a/docker-compose.yml b/docker-compose.yml index 96242be7..978676ea 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -75,10 +75,10 @@ services: context: . dockerfile: Dockerfile restart: always - # command: automappa --debug --port 8050 - # This command allows live-reloading for dev # Not sure which is more appropriate for production :shrug: - command: python -m automappa.index --port 8050 + # This command allows live-reloading for dev + # command: automappa + command: python -m automappa.index volumes: # /usr/src/app is location of install in Dockerfile - .:/usr/src/app From e54108c498b58d7ce24edb8e4f273a12678f1eac Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Fri, 22 Apr 2022 14:29:36 -0400 Subject: [PATCH 19/37] :memo: Update TODO list --- TODO.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/TODO.md b/TODO.md index ce1d57be..33f58377 100644 --- a/TODO.md +++ b/TODO.md @@ -5,10 +5,10 @@ 3. [x] :racehorse::art: Save uploaded file metadata to postgres datatable 4. [x] :racehorse::art: Populate `dcc.Store(...)` with user's existing datatables 5. [ ] ~:racehorse::art: Populate `data_table.DataTable` with clickable `dcc.Link` that navigates user to `mag_refinement.py` (will load data relevant to link, e.g. metagenome annotations, markers, etc.)~ -6. [ ] :racehorse: Retrieve datatable within `mag_refinement.py` and `mag_summary.py` - - binning, markers, metagenome dropdowns for selecting data available in database - - dropdown values correspond to `table_id` and are sent to `mag_refinement.py` callbacks, replacing `pd.read_json(...)` - - dropdowns should have a "NA" or placeholder causing inability to navigate to other layouts when data for the particular filetype is unavailable +6. [x] :racehorse: Retrieve datatable within `mag_refinement.py` + - [x] binning, markers, metagenome dropdowns for selecting data available in database + - [x] dropdown values correspond to `table_id` and are sent to `mag_refinement.py` callbacks, replacing `pd.read_json(...)` + - [x] dropdowns should have a "NA" or placeholder causing inability to navigate to other layouts when data for the particular filetype is unavailable 7. [ ] :art::bug: Finish addition of other embeddings within 2D scatterplot in `mag_refinement.py` 8. [x] :carrot::racehorse: Add celery task-queue 9. [x] :carrot::racehorse: Add celery-monitoring services @@ -16,6 +16,7 @@ 11. [ ] :carrot::racehorse: Add k-mer embedding tasks to task-queue 12. [x] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) 13. [x] :art: Refactor `on_{binning,markers,metagenome}_upload` callbacks to one function that takes in the filetype to determine storage method +14. [ ] :racehorse: Retrieve datatable within `mag_summary.py` -------------------------------------------------------------------------------------------------- @@ -25,12 +26,16 @@ TODO: Provision grafana from `docker-compose.yml`. See: [Grafana provisioning example data source config file](https://grafana.com/docs/grafana/latest/administration/provisioning/#example-data-source-config-file) -### Navigate to grafana +## Monitoring Services +- flower link - [http://localhost:5555](http://localhost:5555) - prometheus link - [http://localhost:9090](http://localhost:9090) - grafana link - [http://localhost:3000](http://localhost:3000) +### Grafana configuration + - flower+prometheus+grafana [add prometheus as a data source in grafana]( "flower+prometheus+grafana add prometheus as a data source in grafana") +- grafana link - [http://localhost:3000](http://localhost:3000) Add the prometheus url as: @@ -41,7 +46,6 @@ http://prometheus:9090 Notice the tutorial mentions `http://localhost:9090`, but since this is running as a service using `docker-compose` the hostname changes to the `prometheus` alias (this is the name of the service in the `docker-compose.yml` file) - ## Misc. Resources - [docker-compose networking docs]() From d35a6cc43900f5616a0041ef3506e38116365881 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Sat, 23 Apr 2022 18:26:58 -0400 Subject: [PATCH 20/37] :whale::tv: provisioned Grafana within docker-compose. Will still need to ensure this is working once celery tasks are implemented --- .env | 12 +++++- Makefile | 8 ++-- TODO.md | 13 +++++-- automappa/apps/home.py | 38 ++++++++++--------- automappa/apps/mag_refinement.py | 2 +- automappa/utils/serializers.py | 2 +- docker-compose.yml | 5 +++ .../celery-monitoring-grafana-dashboard.json | 4 +- docker/grafana/grafana.ini | 23 +++++++++++ .../provisioning/dashboards/dashboard.yml | 25 ++++++++++++ .../provisioning/datasources/prometheus.yml | 9 +++++ 11 files changed, 110 insertions(+), 31 deletions(-) rename celery-monitoring-grafana-dashboard.json => docker/grafana/dashboards/celery-monitoring-grafana-dashboard.json (99%) create mode 100644 docker/grafana/grafana.ini create mode 100644 docker/grafana/provisioning/dashboards/dashboard.yml create mode 100644 docker/grafana/provisioning/datasources/prometheus.yml diff --git a/.env b/.env index 0f908f61..e307a7b9 100644 --- a/.env +++ b/.env @@ -1,4 +1,4 @@ -# Required for docker-compose.yml postgres service +# Required for docker-compose.yml SERVER_ROOT_UPLOAD_FOLDER="${HOME}/.automappa/uploads" SERVER_HOST="0.0.0.0" SERVER_PORT=8050 @@ -13,4 +13,12 @@ RABBITMQ_DEFAULT_USER="user" RABBITMQ_DEFAULT_PASS="pass" RABBITMQ_URL="amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/" CELERY_BACKEND_URL='redis://redis:6379/0' -CELERY_BROKER_URL="amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672//" \ No newline at end of file +CELERY_BROKER_URL="amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672//" + +# Grafana configuration +# Allow anonymous authentication or not +GF_AUTH_DISABLE_LOGIN_FORM="false" +# Role of anonymous user +GF_AUTH_ANONYMOUS_ENABLED="false" +# Install plugins here our in your own config file +GF_AUTH_ANONYMOUS_ORG_ROLE="Admin" diff --git a/Makefile b/Makefile index 83fd7641..bc792276 100644 --- a/Makefile +++ b/Makefile @@ -57,13 +57,13 @@ build: docker-compose.yml up: docker-compose.yml docker-compose up --always-recreate-deps --remove-orphans --force-recreate -## alias for docker-compose down +## alias for docker-compose down --remove-orphans down: docker-compose.yml - docker-compose down --volumes + docker-compose down --remove-orphans -## alias for docker-compose down --volumes +## alias for docker-compose down --remove-orphans --volumes down-v: docker-compose.yml - docker-compose down --volumes + docker-compose down --remove-orphans -v # Run Automappa on test data # test: test_data diff --git a/TODO.md b/TODO.md index 33f58377..382f083b 100644 --- a/TODO.md +++ b/TODO.md @@ -8,7 +8,7 @@ 6. [x] :racehorse: Retrieve datatable within `mag_refinement.py` - [x] binning, markers, metagenome dropdowns for selecting data available in database - [x] dropdown values correspond to `table_id` and are sent to `mag_refinement.py` callbacks, replacing `pd.read_json(...)` - - [x] dropdowns should have a "NA" or placeholder causing inability to navigate to other layouts when data for the particular filetype is unavailable + - [ ] dropdowns should have a "NA" or placeholder causing inability to navigate to other layouts when data for the particular filetype is unavailable 7. [ ] :art::bug: Finish addition of other embeddings within 2D scatterplot in `mag_refinement.py` 8. [x] :carrot::racehorse: Add celery task-queue 9. [x] :carrot::racehorse: Add celery-monitoring services @@ -17,14 +17,21 @@ 12. [x] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) 13. [x] :art: Refactor `on_{binning,markers,metagenome}_upload` callbacks to one function that takes in the filetype to determine storage method 14. [ ] :racehorse: Retrieve datatable within `mag_summary.py` +15. [ ] :whale::elephant::sunflower: Grafana provision from within `docker-compose.yml` +16. [ ] :art::fire: Refactor `store_binning_main(...)` s.t. refinement-data is stored in a separate async process. -------------------------------------------------------------------------------------------------- ## docker-compose services configuration -> NOTE: All of this assumes you have all docker services running via `make up` or `docker-compose up` +***NOTE: All of this assumes you have all docker services running via `make up` or `docker-compose up`*** -TODO: Provision grafana from `docker-compose.yml`. See: [Grafana provisioning example data source config file](https://grafana.com/docs/grafana/latest/administration/provisioning/#example-data-source-config-file) +> ~Provision grafana from `docker-compose.yml`. See: [Grafana provisioning example data source config file](https://grafana.com/docs/grafana/latest/administration/provisioning/#example-data-source-config-file)~ +> Found a nice blog post and accompanying GitHub repo to follow: +> +> - [Medium blog post](https://medium.com/swlh/easy-grafana-and-docker-compose-setup-d0f6f9fcec13) +> - [github.com/annea-ai/grafana-infrastructure]() +> - [Grafana docs on Provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/) ## Monitoring Services diff --git a/automappa/apps/home.py b/automappa/apps/home.py index 8477dfc8..dfcc8fc9 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -180,14 +180,6 @@ ] ) -# binning_main_upload_store = dcc.Store( -# id="binning-main-upload-store", storage_type="local" -# ) -# markers_upload_store = dcc.Store(id="markers-upload-store", storage_type="local") -# metagenome_upload_store = dcc.Store(id="metagenome-upload-store", storage_type="local") -# samples_store = dcc.Store(id="samples-store", storage_type="local") - -# samples_datatable = html.Div(id="samples-datatable") samples_datatable = ( dcc.Loading( id="loading-samples-datatable", @@ -236,10 +228,6 @@ layout = dbc.Container( children=[ - # binning_main_upload_store, - # markers_upload_store, - # metagenome_upload_store, - # samples_store, dbc.Row(upload_modal), html.Br(), # row_example_cards, @@ -287,11 +275,9 @@ def on_upload_stores_data( ): # Check if db has any samples in table uploaded_files_df = get_uploaded_files_table() - if uploaded_files_df: + if not uploaded_files_df.empty: return uploaded_files_df.to_json(orient="split") raise PreventUpdate - # We need to ensure we prevent an update if there has not been one, otherwise all of our datastore - # gets removed... samples = [] for data_upload in [ binning_uploads, @@ -316,6 +302,8 @@ def on_upload_stores_data( State("samples-store", "data"), ) def binning_select_options(samples_store_data, new_samples_store_data): + if samples_store_data is None: + raise PreventUpdate samples_df = pd.read_json(samples_store_data, orient="split") if new_samples_store_data is not None: new_samples_df = pd.read_json(new_samples_store_data, orient="split") @@ -326,7 +314,6 @@ def binning_select_options(samples_store_data, new_samples_store_data): if samples_df.empty: raise PreventUpdate - # {"label": filename+truncated-hash, "value": table_id} df = samples_df.loc[samples_df.filetype.eq("binning")] logger.debug(f"{df.shape[0]:,} binning available for mag_refinement") return [ @@ -344,6 +331,8 @@ def binning_select_options(samples_store_data, new_samples_store_data): State("samples-store", "data"), ) def markers_select_options(samples_store_data, new_samples_store_data): + if samples_store_data is None: + raise PreventUpdate samples_df = pd.read_json(samples_store_data, orient="split") if new_samples_store_data is not None: new_samples_df = pd.read_json(new_samples_store_data, orient="split") @@ -354,7 +343,6 @@ def markers_select_options(samples_store_data, new_samples_store_data): if samples_df.empty: raise PreventUpdate - # {"label": filename+truncated-hash, "value": table_id} markers_samples = samples_df.loc[samples_df.filetype.eq("markers")] logger.debug(f"{markers_samples.shape[0]:,} markers available for mag_refinement") return [ @@ -374,6 +362,8 @@ def markers_select_options(samples_store_data, new_samples_store_data): State("samples-store", "data"), ) def metagenome_select_options(samples_store_data, new_samples_store_data): + if samples_store_data is None: + raise PreventUpdate samples_df = pd.read_json(samples_store_data, orient="split") if new_samples_store_data is not None: new_samples_df = pd.read_json(new_samples_store_data, orient="split") @@ -401,6 +391,8 @@ def metagenome_select_options(samples_store_data, new_samples_store_data): State("samples-store", "data"), ) def on_samples_store_data(samples_store_data, new_samples_store_data): + if samples_store_data is None: + raise PreventUpdate samples_df = pd.read_json(samples_store_data, orient="split") if new_samples_store_data is not None: new_samples_df = pd.read_json(new_samples_store_data, orient="split") @@ -498,7 +490,17 @@ def toggle_modal(n_open, n_close, is_open): # TODO: Disable refine-mags button when not all select values are provided - +@app.callback( + Output("refine-mags-button", "active"), + [Input("binning-select", "value"), + Input("markers-select", "value"), + Input("metagenome-select", "value"),], +) +def refine_mags_button_active_callback(binning_value, markers_value, metagenome_value): + if not binning_value or not markers_value or not metagenome_value: + return False + else: + return True @app.callback( Output("selected-tables-store", "data"), diff --git a/automappa/apps/mag_refinement.py b/automappa/apps/mag_refinement.py index ca8938ae..4f241065 100644 --- a/automappa/apps/mag_refinement.py +++ b/automappa/apps/mag_refinement.py @@ -648,7 +648,7 @@ def taxonomy_distribution_figure_callback( df = get_table(table_name) if selected_contigs and selected_contigs["points"]: contigs = {point["text"] for point in selected_contigs["points"]} - df = df[df.contig.isin[contigs]] + df = df.loc[df.contig.isin[contigs]] fig = taxonomy_sankey(df, selected_rank=selected_rank) return fig diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py index 770a4d87..5ba7df40 100644 --- a/automappa/utils/serializers.py +++ b/automappa/utils/serializers.py @@ -122,7 +122,7 @@ def store_binning_main(filepath: Path, if_exists: str = "replace") -> str: ] refinement_table_name = table_name.replace("-binning", "-refinement") df[df_cols].to_sql(refinement_table_name, engine, if_exists=if_exists, index=False) - logger.debug(f"Saved {df[df_cols]} refinements to postgres table: {refinement_table_name}") + logger.debug(f"Saved {df[df_cols].info()} refinements to postgres table: {refinement_table_name}") return table_name diff --git a/docker-compose.yml b/docker-compose.yml index 978676ea..c2ec1919 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -61,8 +61,13 @@ services: image: grafana/grafana:latest ports: - 3000:3000 + env_file: + - .env volumes: - grafana-storage:/var/lib/grafana + - ./docker/grafana/provisioning:/etc/grafana/provisioning + - ./docker/grafana/grafana.ini:/etc/grafana/grafana.ini + - ./docker/grafana/dashboards:/etc/grafana/dashboards depends_on: - prometheus diff --git a/celery-monitoring-grafana-dashboard.json b/docker/grafana/dashboards/celery-monitoring-grafana-dashboard.json similarity index 99% rename from celery-monitoring-grafana-dashboard.json rename to docker/grafana/dashboards/celery-monitoring-grafana-dashboard.json index 806910e3..04b9dfd0 100644 --- a/celery-monitoring-grafana-dashboard.json +++ b/docker/grafana/dashboards/celery-monitoring-grafana-dashboard.json @@ -42,7 +42,7 @@ } ] }, - "description": "Basic celery monitoring example", + "description": "Basic celery monitoring", "editable": true, "gnetId": null, "graphTooltip": 0, @@ -753,7 +753,7 @@ }, "timepicker": {}, "timezone": "", - "title": "Celery Monitoring", + "title": "Automappa Celery Monitoring", "uid": "3OBI1flGz", "version": 9 } \ No newline at end of file diff --git a/docker/grafana/grafana.ini b/docker/grafana/grafana.ini new file mode 100644 index 00000000..9f442223 --- /dev/null +++ b/docker/grafana/grafana.ini @@ -0,0 +1,23 @@ +[paths] +provisioning = /etc/grafana/provisioning + +[server] +enable_gzip = true +# To add HTTPS support: +#protocol = https +#;http_addr = +#http_port = 3000 +#domain = localhost +#enforce_domain = false +#root_url = https://localhost:3000 +#router_logging = false +#static_root_path = public +#cert_file = /etc/certs/cert.pem +#cert_key = /etc/certs/cert-key.pem + +[security] +# If you want to embed grafana into an iframe for example +allow_embedding = true + +[users] +default_theme = dark \ No newline at end of file diff --git a/docker/grafana/provisioning/dashboards/dashboard.yml b/docker/grafana/provisioning/dashboards/dashboard.yml new file mode 100644 index 00000000..945453a4 --- /dev/null +++ b/docker/grafana/provisioning/dashboards/dashboard.yml @@ -0,0 +1,25 @@ +# config file version +apiVersion: 1 + +providers: + # an unique provider name + - name: Automappa Monitoring + # org id. will default to orgId 1 if not specified + org_id: 1 + # name of the dashboard folder. Required + folder: '' + # provider type. Required + type: 'file' + # disable dashboard deletion + disableDeletion: false + # enable dashboard editing + editable: true + # how often Grafana will scan for changed dashboards + updateIntervalSeconds: 5 + # allow updating provisioned dashboards from the UI + allowUiUpdates: true + options: + # path to dashboard files on disk. Required + path: /etc/grafana/dashboards + # use folder names from filesystem to create folders in Grafana + foldersFromFilesStructure: true \ No newline at end of file diff --git a/docker/grafana/provisioning/datasources/prometheus.yml b/docker/grafana/provisioning/datasources/prometheus.yml new file mode 100644 index 00000000..ef132d28 --- /dev/null +++ b/docker/grafana/provisioning/datasources/prometheus.yml @@ -0,0 +1,9 @@ +apiVersion: 1 + +deleteDatasources: + - name: Prometheus + +datasources: + - name: Prometheus + type: prometheus + url: http://prometheus:9090 From ec9989bdd5b6c2b23df450a09187dd64c08a2625 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Sun, 24 Apr 2022 22:35:43 -0400 Subject: [PATCH 21/37] :art::bug: Connect mag_summary.layout callbacks to postgres db :bug::fire: Fix sankey diagram subsetting bug where selections would not render :art: Change MAG-specific completeness/purity boxplot to barplot :art: Rename save_to_db(...) to file_to_db(...) in serializer :art: Add metric_barplot(...) func to automappa.utils.figures --- automappa/apps/home.py | 8 +- automappa/apps/mag_refinement.py | 67 +++++++------- automappa/apps/mag_summary.py | 148 ++++++++++++++++++++----------- automappa/utils/figures.py | 14 +++ automappa/utils/serializers.py | 25 +++++- 5 files changed, 169 insertions(+), 93 deletions(-) diff --git a/automappa/apps/home.py b/automappa/apps/home.py index dfcc8fc9..51865481 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -15,7 +15,7 @@ from automappa.app import app from automappa.utils.serializers import ( get_uploaded_files_table, - save_to_db, + file_to_db, validate_uploader, ) @@ -429,7 +429,7 @@ def on_binning_main_upload(iscompleted, filenames, upload_id): raise PreventUpdate if not filepath: raise PreventUpdate - df = save_to_db( + df = file_to_db( filepath=filepath, filetype="binning", ) @@ -452,7 +452,7 @@ def on_markers_upload(iscompleted, filenames, upload_id): raise PreventUpdate if not filepath: raise PreventUpdate - df = save_to_db(filepath, "markers") + df = file_to_db(filepath, "markers") return df.to_json(orient="split") @@ -474,7 +474,7 @@ def on_metagenome_upload(iscompleted, filenames, upload_id): raise PreventUpdate if not filepath: raise PreventUpdate - df = save_to_db(filepath, "metagenome") + df = file_to_db(filepath, "metagenome") return df.to_json(orient="split") diff --git a/automappa/apps/mag_refinement.py b/automappa/apps/mag_refinement.py index 4f241065..7c5cae93 100644 --- a/automappa/apps/mag_refinement.py +++ b/automappa/apps/mag_refinement.py @@ -20,7 +20,7 @@ from automappa.app import app # from automappa.tasks import get_marker_symbols -from automappa.utils.serializers import get_table +from automappa.utils.serializers import get_table, table_to_db from automappa.utils.markers import get_marker_symbols from automappa.utils.figures import ( format_axis_title, @@ -551,13 +551,13 @@ def update_mag_metrics_datatable_callback( Output("scatterplot-2d", "figure"), [ Input("selected-tables-store", "data"), - # Input("refinement-data", "data"), # Input("contig-marker-symbols-store", "data"), Input("x-axis-2d", "value"), Input("y-axis-2d", "value"), Input("scatterplot-2d-legend-toggle", "value"), Input("color-by-column", "value"), Input("hide-selections-toggle", "value"), + Input("mag-refinement-save-button", "n_clicks"), ], ) def scatterplot_2d_figure_callback( @@ -569,6 +569,7 @@ def scatterplot_2d_figure_callback( show_legend: bool, color_by_col: str, hide_selection_toggle: bool, + btn_clicks: int, ) -> go.Figure: # TODO: #23 refactor scatterplot callbacks bin_table_name = selected_tables_data["binning"] @@ -576,9 +577,6 @@ def scatterplot_2d_figure_callback( markers_table_name = selected_tables_data["markers"] markers_df = get_table(markers_table_name, index_col="contig") markers = get_marker_symbols(bin_df, markers_df) - # markers = pd.read_json(contig_marker_symbols_json, orient="split").set_index( - # "contig" - # ) if color_by_col not in bin_df.columns: for col in ["phylum", "class", "order", "family"]: if col in bin_df.columns: @@ -588,7 +586,6 @@ def scatterplot_2d_figure_callback( raise ValueError( f"No columns were found in binning-main that could be used to group traces. {color_by_col} not found in table..." ) - # Subset metagenome-annotations by selections iff selections have been made if hide_selection_toggle: refine_table_name = selected_tables_data["binning"].replace("-binning","-refinement") @@ -648,7 +645,7 @@ def taxonomy_distribution_figure_callback( df = get_table(table_name) if selected_contigs and selected_contigs["points"]: contigs = {point["text"] for point in selected_contigs["points"]} - df = df.loc[df.contig.isin[contigs]] + df = df.loc[df.contig.isin(contigs)] fig = taxonomy_sankey(df, selected_rank=selected_rank) return fig @@ -760,10 +757,17 @@ def mag_summary_length_boxplot_callback( @app.callback( Output("refinements-table", "children"), - [Input("refinement-data", "data")], + [ + Input("selected-tables-store", "data"), + Input("mag-refinement-save-button", "n_clicks"), + ], ) -def refinements_table_callback(refinement_store_data: "str | None") -> DataTable: - df = pd.read_json(refinement_store_data, orient="split") +def refinements_table_callback( + selected_tables_data: Dict[str, str], + btn_clicks: int, +) -> DataTable: + refine_table_name = selected_tables_data["binning"].replace("-binning","-refinement") + df = get_table(refine_table_name) return DataTable( data=df.to_dict("records"), columns=[{"name": col, "id": col} for col in df.columns], @@ -777,15 +781,16 @@ def refinements_table_callback(refinement_store_data: "str | None") -> DataTable Output("refinements-download", "data"), [ Input("refinements-download-button", "n_clicks"), - Input("refinement-data", "data"), + Input("selected-tables-store", "data"), ], ) def download_refinements( - n_clicks: int, curated_mags: "str | None" + n_clicks: int, selected_tables_data: Dict[str, str], ) -> Dict[str, "str | bool"]: if not n_clicks: raise PreventUpdate - df = pd.read_json(curated_mags, orient="split") + refine_table_name = selected_tables_data["binning"].replace("-binning","-refinement") + df = get_table(refine_table_name) return send_data_frame(df.to_csv, "refinements.csv", index=False) @@ -800,37 +805,29 @@ def mag_refinement_save_button_disabled_callback( @app.callback( - [ - Output("refinement-data", "data"), - Output("mag-refinement-save-button", "n_clicks"), - ], + Output("mag-refinement-save-button", "n_clicks"), [ Input("scatterplot-2d", "selectedData"), - Input("refinement-data", "data"), + Input("selected-tables-store", "data"), Input("mag-refinement-save-button", "n_clicks"), ], - [ - State("refinement-data", "data"), - ], ) def store_binning_refinement_selections( selected_data: Dict[str, List[Dict[str, str]]], - refinement_data: "str | None", + selected_tables_data: Dict[str, str], n_clicks: int, - intermediate_selections: "str | None", -) -> "str | None": +) -> int: # Initial load... - if not selected_data: - bin_df = pd.read_json(refinement_data, orient="split") - return bin_df.to_json(orient="split"), 0 - if not n_clicks or (n_clicks and not selected_data): + if not n_clicks or (n_clicks and not selected_data) or not selected_data: raise PreventUpdate - pdf = pd.read_json(intermediate_selections, orient="split").set_index("contig") - refinement_cols = [col for col in pdf.columns if "refinement" in col] + refine_table_name = selected_tables_data["binning"].replace("-binning","-refinement") + bin_df = get_table(refine_table_name, index_col="contig") + refinement_cols = [col for col in bin_df.columns if "refinement" in col] refinement_num = len(refinement_cols) + 1 - group_name = f"refinement_{refinement_num}" + refinement_name = f"refinement_{refinement_num}" contigs = list({point["text"] for point in selected_data["points"]}) - pdf.loc[contigs, group_name] = group_name - pdf = pdf.fillna(axis="columns", method="ffill") - pdf.reset_index(inplace=True) - return pdf.to_json(orient="split"), 0 + bin_df.loc[contigs, refinement_name] = refinement_name + bin_df = bin_df.fillna(axis="columns", method="ffill") + bin_df.reset_index(inplace=True) + table_to_db(df=bin_df, name=refine_table_name) + return 0 diff --git a/automappa/apps/mag_summary.py b/automappa/apps/mag_summary.py index 65b325c2..ed280da3 100644 --- a/automappa/apps/mag_summary.py +++ b/automappa/apps/mag_summary.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from typing import Any, Dict, List +from typing import Dict, List from dash.exceptions import PreventUpdate import numpy as np @@ -15,7 +15,8 @@ import plotly.io as pio from automappa.app import app -from automappa.utils.figures import taxonomy_sankey, metric_boxplot +from automappa.utils.figures import metric_barplot, taxonomy_sankey, metric_boxplot +from automappa.utils.serializers import get_table pio.templates.default = "plotly_white" @@ -93,10 +94,10 @@ ) mag_metrics_boxplot = dcc.Loading( - id="loading-mag-metrics-boxplot", + id="loading-mag-metrics-barplot", children=[ dcc.Graph( - id="mag-metrics-boxplot", + id="mag-metrics-barplot", config={"displayModeBar": False, "displaylogo": False}, ) ], @@ -150,13 +151,18 @@ dcc.Dropdown( id="mag-summary-cluster-col-dropdown", value="cluster", + placeholder="Select a cluster column to compute MAG summary metrics", clearable=False, ), ] mag_selection_dropdown = [ html.Label("MAG Selection Dropdown"), - dcc.Dropdown(id="mag-selection-dropdown", clearable=True), + dcc.Dropdown( + id="mag-selection-dropdown", + clearable=True, + placeholder="Select a MAG from this dropdown for a MAG-specific summary", + ), ] @@ -208,12 +214,12 @@ @app.callback( Output("mag-overview-metrics-boxplot", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("mag-summary-cluster-col-dropdown", "value"), ], ) def mag_overview_metrics_boxplot_callback( - df_json: "str | None", cluster_col: str + selected_tables_data: Dict[str, str], cluster_col: str ) -> go.Figure: """ Writes @@ -224,7 +230,8 @@ def mag_overview_metrics_boxplot_callback( Returns: n_unique_bins - number of unique bins """ - mag_summary_df = pd.read_json(df_json, orient="split") + table_name = selected_tables_data["binning"] + mag_summary_df = get_table(table_name) if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -236,14 +243,15 @@ def mag_overview_metrics_boxplot_callback( @app.callback( Output("mag-overview-gc-content-boxplot", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("mag-summary-cluster-col-dropdown", "value"), ], ) def mag_overview_gc_content_boxplot_callback( - df_json: "str | None", cluster_col: str + selected_tables_data: Dict[str, str], cluster_col: str ) -> go.Figure: - mag_summary_df = pd.read_json(df_json, orient="split") + table_name = selected_tables_data["binning"] + mag_summary_df = get_table(table_name) if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -256,14 +264,15 @@ def mag_overview_gc_content_boxplot_callback( @app.callback( Output("mag-overview-length-boxplot", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("mag-summary-cluster-col-dropdown", "value"), ], ) def mag_overview_length_boxplot_callback( - df_json: "str | None", cluster_col: str + selected_tables_data: Dict[str, str], cluster_col: str ) -> go.Figure: - mag_summary_df = pd.read_json(df_json, orient="split") + table_name = selected_tables_data["binning"] + mag_summary_df = get_table(table_name) if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -275,14 +284,15 @@ def mag_overview_length_boxplot_callback( @app.callback( Output("mag-overview-coverage-boxplot", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("mag-summary-cluster-col-dropdown", "value"), ], ) def mag_overview_coverage_boxplot_callback( - df_json: "str | None", cluster_col: str + selected_tables_data: Dict[str, str], cluster_col: str ) -> go.Figure: - mag_summary_df = pd.read_json(df_json, orient="split") + table_name = selected_tables_data["binning"] + mag_summary_df = get_table(table_name) if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -293,13 +303,14 @@ def mag_overview_coverage_boxplot_callback( @app.callback( Output("mag-summary-cluster-col-dropdown", "options"), - [Input("metagenome-annotations", "data")], + [Input("selected-tables-store", "data")], ) -def mag_summary_cluster_col_dropdown_options_callback(df_json): - bin_df = pd.read_json(df_json, orient="split") +def mag_summary_cluster_col_dropdown_options_callback(selected_tables_data: Dict[str, str]): + refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") + refinement_df = get_table(refinement_table_name, index_col='contig') return [ {"label": col.title(), "value": col} - for col in bin_df.columns + for col in refinement_df.columns if "cluster" in col or "refinement" in col ] @@ -307,16 +318,20 @@ def mag_summary_cluster_col_dropdown_options_callback(df_json): @app.callback( Output("mag-summary-stats-datatable", "children"), [ - Input("metagenome-annotations", "data"), - Input("markers-store", "data"), + Input("selected-tables-store", "data"), Input("mag-summary-cluster-col-dropdown", "value"), ], ) def mag_summary_stats_datatable_callback( - mag_annotations_json, markers_json, cluster_col + selected_tables_data: Dict[str, str], cluster_col:str ): - bin_df = pd.read_json(mag_annotations_json, orient="split") - markers = pd.read_json(markers_json, orient="split").set_index("contig") + binning_table_name = selected_tables_data["binning"] + bin_df = get_table(binning_table_name, index_col='contig') + refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") + refinement_df = get_table(refinement_table_name, index_col='contig').drop(columns="cluster") + bin_df = bin_df.join(refinement_df, how='right') + markers_table_name = selected_tables_data["markers"] + markers = get_table(markers_table_name, index_col="contig") if cluster_col not in bin_df.columns: num_expected_markers = markers.shape[1] length_weighted_coverage = np.average( @@ -356,7 +371,7 @@ def mag_summary_stats_datatable_callback( else: stats_df = ( get_metabin_stats( - bin_df=bin_df.set_index("contig"), + bin_df=bin_df, markers=markers, cluster_col=cluster_col, ) @@ -387,18 +402,22 @@ def mag_summary_stats_datatable_callback( @app.callback( Output("mag-taxonomy-sankey", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("mag-summary-cluster-col-dropdown", "value"), Input("mag-selection-dropdown", "value"), ], ) def mag_taxonomy_sankey_callback( - mag_summary_json: "str | None", cluster_col: str, selected_mag: str + selected_tables_data: Dict[str,str], cluster_col: str, selected_mag: str ) -> go.Figure: - mag_summary_df = pd.read_json(mag_summary_json, orient="split") - if cluster_col not in mag_summary_df.columns: + binning_table_name = selected_tables_data["binning"] + bin_df = get_table(binning_table_name, index_col='contig') + refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") + refinement_df = get_table(refinement_table_name, index_col='contig').drop(columns="cluster") + bin_df = bin_df.join(refinement_df, how='right') + if cluster_col not in bin_df.columns: raise PreventUpdate - mag_df = mag_summary_df.loc[mag_summary_df[cluster_col].eq(selected_mag)] + mag_df = bin_df.loc[bin_df[cluster_col].eq(selected_mag)] fig = taxonomy_sankey(mag_df) return fig @@ -406,19 +425,23 @@ def mag_taxonomy_sankey_callback( @app.callback( Output("mag-gc-content-boxplot", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("mag-summary-cluster-col-dropdown", "value"), Input("mag-selection-dropdown", "value"), ], ) def mag_summary_gc_content_boxplot_callback( - df_json: "str | None", cluster_col: str, selected_mag: str + selected_tables_data: Dict[str,str], cluster_col: str, selected_mag: str ) -> go.Figure: - mag_summary_df = pd.read_json(df_json, orient="split") - if cluster_col not in mag_summary_df.columns: + binning_table_name = selected_tables_data["binning"] + bin_df = get_table(binning_table_name, index_col='contig') + refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") + refinement_df = get_table(refinement_table_name, index_col='contig').drop(columns="cluster") + bin_df = bin_df.join(refinement_df, how='right') + if cluster_col not in bin_df.columns: raise PreventUpdate - mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) - mag_df = mag_summary_df.loc[mag_summary_df[cluster_col].eq(selected_mag)] + bin_df = bin_df.dropna(subset=[cluster_col]) + mag_df = bin_df.loc[bin_df[cluster_col].eq(selected_mag)] mag_df = mag_df.round(2) fig = metric_boxplot(df=mag_df, metrics=["gc_content"]) fig.update_traces(name="GC Content") @@ -426,38 +449,50 @@ def mag_summary_gc_content_boxplot_callback( @app.callback( - Output("mag-metrics-boxplot", "figure"), + Output("mag-metrics-barplot", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("mag-summary-cluster-col-dropdown", "value"), Input("mag-selection-dropdown", "value"), ], ) -def mag_summary_gc_content_boxplot_callback( - df_json: "str | None", cluster_col: str, selected_mag: str +def mag_metrics_callback( + selected_tables_data: Dict[str,str], cluster_col: str, selected_mag: str ) -> go.Figure: - mag_summary_df = pd.read_json(df_json, orient="split") + if not selected_mag: + raise PreventUpdate + binning_table_name = selected_tables_data["binning"] + bin_df = get_table(binning_table_name, index_col='contig') + refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") + refinement_df = get_table(refinement_table_name, index_col='contig').drop(columns="cluster") + mag_summary_df = bin_df.join(refinement_df, how='right') if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) mag_df = mag_summary_df.loc[mag_summary_df[cluster_col].eq(selected_mag)] mag_df = mag_df.round(2) - fig = metric_boxplot(df=mag_df, metrics=["completeness", "purity"]) + fig = metric_barplot(df=mag_df, metrics=["completeness", "purity"], name=selected_mag) return fig @app.callback( Output("mag-coverage-boxplot", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("mag-summary-cluster-col-dropdown", "value"), Input("mag-selection-dropdown", "value"), ], ) def mag_summary_gc_content_boxplot_callback( - df_json: "str | None", cluster_col: str, selected_mag: str + selected_tables_data: Dict[str,str], cluster_col: str, selected_mag: str ) -> go.Figure: - mag_summary_df = pd.read_json(df_json, orient="split") + if not selected_mag: + raise PreventUpdate + binning_table_name = selected_tables_data["binning"] + bin_df = get_table(binning_table_name, index_col='contig') + refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") + refinement_df = get_table(refinement_table_name, index_col='contig').drop(columns="cluster") + mag_summary_df = bin_df.join(refinement_df, how='right') if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -470,15 +505,21 @@ def mag_summary_gc_content_boxplot_callback( @app.callback( Output("mag-length-boxplot", "figure"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("mag-summary-cluster-col-dropdown", "value"), Input("mag-selection-dropdown", "value"), ], ) def mag_summary_gc_content_boxplot_callback( - df_json: "str | None", cluster_col: str, selected_mag: str + selected_tables_data: Dict[str,str], cluster_col: str, selected_mag: str ) -> go.Figure: - mag_summary_df = pd.read_json(df_json, orient="split") + if not selected_mag: + raise PreventUpdate + binning_table_name = selected_tables_data["binning"] + bin_df = get_table(binning_table_name, index_col='contig') + refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") + refinement_df = get_table(refinement_table_name, index_col='contig').drop(columns="cluster") + mag_summary_df = bin_df.join(refinement_df, how='right') if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -491,14 +532,15 @@ def mag_summary_gc_content_boxplot_callback( @app.callback( Output("mag-selection-dropdown", "options"), [ - Input("metagenome-annotations", "data"), + Input("selected-tables-store", "data"), Input("mag-summary-cluster-col-dropdown", "value"), ], ) def mag_selection_dropdown_options_callback( - mag_annotations_json: "str | None", cluster_col: str + selected_tables_data: Dict[str,str], cluster_col: str ) -> List[Dict[str, str]]: - df = pd.read_json(mag_annotations_json, orient="split") + table_name = selected_tables_data["binning"].replace("-binning", "-refinement") + df = get_table(table_name, index_col="contig") if cluster_col not in df.columns: options = [] else: diff --git a/automappa/utils/figures.py b/automappa/utils/figures.py index 774295f2..de89e279 100644 --- a/automappa/utils/figures.py +++ b/automappa/utils/figures.py @@ -103,6 +103,20 @@ def metric_boxplot( return fig +def metric_barplot( + df: pd.DataFrame, + metrics: List[str] = [], + horizontal: bool = False, + name: str = None, +) -> go.Figure: + if not metrics: + raise PreventUpdate + x = [metric.replace("_", " ").title() for metric in metrics] + y = [df[metric].iat[0] for metric in metrics] + orientation = 'h' if horizontal else "v" + return go.Figure([go.Bar(x=x, y=y, orientation=orientation, name=name)]) + + def marker_size_scaler(x: pd.DataFrame, scale_by: str = "length") -> int: x_min_scaler = x[scale_by] - x[scale_by].min() x_max_scaler = x[scale_by].max() - x[scale_by].min() diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py index 5ba7df40..73e98f66 100644 --- a/automappa/utils/serializers.py +++ b/automappa/utils/serializers.py @@ -206,7 +206,30 @@ def store_metagenome(filepath: Path, if_exists: str = "replace") -> str: return table_name -def save_to_db( +def table_to_db(df:pd.DataFrame, name:str, if_exists: str = "replace", index:bool = False)->None: + """Write `df` to `table_name` in database. + + Parameters + ---------- + df : pd.DataFrame + Dataframe of data to write + table_name : str + Name of data table to store in database + if_exists : str, optional + What to do if `table_name` exists. + Choices include: 'fail', 'replace', 'append', by default "replace" + index : bool, optional + Whether to write the index to the database, by default False + + Returns + ------- + NoneType + Nothing is returned... + """ + return df.to_sql(name=name, con=engine, if_exists=if_exists, index=index) + + +def file_to_db( filepath: Path, filetype: str, if_exists: str = "replace", From bbc93e93b3004a2fe0d0bf901e27c41c96ff16a4 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Mon, 25 Apr 2022 13:31:56 -0400 Subject: [PATCH 22/37] :art: Beginning of celery task-queue implementation - :whale::art: Add mem_limit: 4GB to automappa-web service - :art::bug: Remove ability for user to navigate to other tabs when no data has been uploaded/selected - :art: Disable 'Refine mags' button if data has not been selected - :art: Add DataTable(...) to visualize the currently loaded datasets - :art: Change server settings import so it is clear that the server setting are imported and not the server - :art::carrot: Silence numba and h5py loggers in mag_refinement.py (beginnings of celery task-queue tasks implementation) - :art: Add logger to automappa.tasks - :bug: Fix logger emitted message during refinement data serialization to postgres db --- TODO.md | 10 +++--- automappa/apps/home.py | 54 ++++++++++++++++++++++++++++---- automappa/apps/mag_refinement.py | 12 +++++-- automappa/index.py | 27 +++++++++------- automappa/settings.py | 3 -- automappa/tasks.py | 46 +++++---------------------- docker-compose.yml | 1 + 7 files changed, 87 insertions(+), 66 deletions(-) diff --git a/TODO.md b/TODO.md index 382f083b..67d3838b 100644 --- a/TODO.md +++ b/TODO.md @@ -6,9 +6,9 @@ 4. [x] :racehorse::art: Populate `dcc.Store(...)` with user's existing datatables 5. [ ] ~:racehorse::art: Populate `data_table.DataTable` with clickable `dcc.Link` that navigates user to `mag_refinement.py` (will load data relevant to link, e.g. metagenome annotations, markers, etc.)~ 6. [x] :racehorse: Retrieve datatable within `mag_refinement.py` - - [x] binning, markers, metagenome dropdowns for selecting data available in database - - [x] dropdown values correspond to `table_id` and are sent to `mag_refinement.py` callbacks, replacing `pd.read_json(...)` - - [ ] dropdowns should have a "NA" or placeholder causing inability to navigate to other layouts when data for the particular filetype is unavailable + - [x] (`home.py`) binning, markers, metagenome dropdowns for selecting data available in database + - [x] (`home.py`) dropdown values correspond to `table_id` and are sent to `mag_refinement.py` callbacks, replacing `pd.read_json(...)` + - [x] (`home.py`/`index.py`) dropdowns should have a "NA" or placeholder disabling navigation to other layouts when data for the particular filetype is unavailable 7. [ ] :art::bug: Finish addition of other embeddings within 2D scatterplot in `mag_refinement.py` 8. [x] :carrot::racehorse: Add celery task-queue 9. [x] :carrot::racehorse: Add celery-monitoring services @@ -16,8 +16,8 @@ 11. [ ] :carrot::racehorse: Add k-mer embedding tasks to task-queue 12. [x] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) 13. [x] :art: Refactor `on_{binning,markers,metagenome}_upload` callbacks to one function that takes in the filetype to determine storage method -14. [ ] :racehorse: Retrieve datatable within `mag_summary.py` -15. [ ] :whale::elephant::sunflower: Grafana provision from within `docker-compose.yml` +14. [x] :racehorse: Retrieve datatable within `mag_summary.py` +15. [x] :whale::elephant::sunflower: Grafana provision from within `docker-compose.yml` 16. [ ] :art::fire: Refactor `store_binning_main(...)` s.t. refinement-data is stored in a separate async process. -------------------------------------------------------------------------------------------------- diff --git a/automappa/apps/home.py b/automappa/apps/home.py index 51865481..8db15a0e 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -183,7 +183,22 @@ samples_datatable = ( dcc.Loading( id="loading-samples-datatable", - children=[html.Div(id="samples-datatable")], + children=[ + html.Label("Uploaded Datasets"), + html.Div(id="samples-datatable") + ], + type="dot", + color="#646569", + ), +) + +selected_tables_datatable = ( + dcc.Loading( + id="loading-selected-tables-datatable", + children=[ + html.Label("Selected Datasets for Refinement & Summary:"), + html.Div(id="selected-tables-datatable") + ], type="dot", color="#646569", ), @@ -236,6 +251,8 @@ dbc.Row(refine_mags_input_groups), html.Br(), dbc.Row(refine_mags_button), + html.Br(), + dbc.Row(selected_tables_datatable), ], fluid=True, ) @@ -489,18 +506,17 @@ def toggle_modal(n_open, n_close, is_open): return is_open -# TODO: Disable refine-mags button when not all select values are provided @app.callback( - Output("refine-mags-button", "active"), + Output("refine-mags-button", "disabled"), [Input("binning-select", "value"), Input("markers-select", "value"), Input("metagenome-select", "value"),], ) def refine_mags_button_active_callback(binning_value, markers_value, metagenome_value): - if not binning_value or not markers_value or not metagenome_value: - return False - else: + if binning_value is None or markers_value is None: return True + else: + return False @app.callback( Output("selected-tables-store", "data"), @@ -521,3 +537,29 @@ def on_refine_mags_button_click( "markers": markers_select_value, "metagenome": metagenome_select_value, } + +@app.callback( + Output("selected-tables-datatable", "children"), + [ + Input("selected-tables-store", "data"), + Input("tabs", "active_tab"), + ], + State("selected-tables-store", "data"), +) +def selected_tables_datatable_children(selected_tables_store_data, active_tab, new_selected_tables_store_data): + # Why Input("tabs", "active_tab"): Navigating to back to home tab triggers rendering of table from store + if selected_tables_store_data is None: + raise PreventUpdate + if new_selected_tables_store_data is not None: + selected_tables_store_data.update(new_selected_tables_store_data) + + if not selected_tables_store_data: + raise PreventUpdate + + return DataTable( + data=[{"filetype":filetype, "table_id": table_id} for filetype,table_id in selected_tables_store_data.items() if table_id is not None], + columns=[ + {"id": "filetype", "name": "filetype", "editable": False}, + {"id": "table_id", "name": "table_id", "editable": False}, + ], + ) diff --git a/automappa/apps/mag_refinement.py b/automappa/apps/mag_refinement.py index 7c5cae93..16044a1f 100644 --- a/automappa/apps/mag_refinement.py +++ b/automappa/apps/mag_refinement.py @@ -1,6 +1,12 @@ # -*- coding: utf-8 -*- import logging + +numba_logger = logging.getLogger('numba') +numba_logger.setLevel(logging.WARNING) +h5py_logger = logging.getLogger('h5py') +h5py_logger.setLevel(logging.WARNING) + from typing import Dict, List import pandas as pd @@ -19,9 +25,9 @@ from automappa.app import app -# from automappa.tasks import get_marker_symbols +from automappa.tasks import get_marker_symbols from automappa.utils.serializers import get_table, table_to_db -from automappa.utils.markers import get_marker_symbols +# from automappa.utils.markers import get_marker_symbols from automappa.utils.figures import ( format_axis_title, get_scatterplot_2d, @@ -34,7 +40,6 @@ format="[%(levelname)s] %(name)s: %(message)s", level=logging.DEBUG, ) - logger = logging.getLogger(__name__) pio.templates.default = "plotly_white" @@ -571,6 +576,7 @@ def scatterplot_2d_figure_callback( hide_selection_toggle: bool, btn_clicks: int, ) -> go.Figure: + # NOTE: btn_clicks is an input so this figure is updated when new refinements are saved # TODO: #23 refactor scatterplot callbacks bin_table_name = selected_tables_data["binning"] bin_df = get_table(bin_table_name, index_col="contig") diff --git a/automappa/index.py b/automappa/index.py index c87c3a2f..1315685f 100755 --- a/automappa/index.py +++ b/automappa/index.py @@ -3,12 +3,13 @@ import argparse import logging +from typing import Dict from dash.dependencies import Input, Output from dash import dcc, html import dash_bootstrap_components as dbc -from automappa.settings import server +from automappa import settings from automappa.apps import home, mag_refinement, mag_summary from automappa.app import app @@ -20,8 +21,19 @@ logger = logging.getLogger(__name__) -@app.callback(Output("tab-content", "children"), [Input("tabs", "active_tab")]) -def render_content(active_tab): +@app.callback( + Output("tab-content", "children"), + [Input("tabs", "active_tab"), + Input("selected-tables-store", "data")] +) +def render_content(active_tab: str, selected_tables_data: Dict[str,str]) -> dbc.Container: + # Only alow user to navigate to mag refinement or summary if data is already uploaded + if selected_tables_data is None: + return home.layout + for data_table in ['markers', 'binning']: + if selected_tables_data[data_table] is None: + return home.layout + layouts = { "home": home.layout, "mag_refinement": mag_refinement.layout, @@ -101,9 +113,6 @@ def main(): app.layout = dbc.Container( [ - # dbc.Col(markers_store), - # dbc.Col(metagenome_annotations_store), - # dbc.Col(refinement_data_store), # dbc.Col(contig_marker_symbols_store), dbc.Col(binning_main_upload_store), dbc.Col(markers_upload_store), @@ -116,19 +125,15 @@ def main(): children=[home_tab, refinement_tab, summary_tab], className="nav-fill", ), - # html.Div(browser_storage_toast), html.Div(id="tab-content"), ], fluid=True, ) # TODO: Replace cli inputs (as well as updating title once file is uploaded...) - # dcc.Upload(id='metagenome-annotations-upload', children=dbc.Button("Upload annotations")) - # dcc.Upload(id='markers-upload', children=dbc.Button("Upload annotations")) - # sample_name = os.path.basename(args.binning_main).replace(" ", "_").split(".")[0] # app.title = f"Automappa: {sample_name}" app.title = "Automappa" - app.run_server(host=server.host, port=server.port, debug=server.debug) + app.run_server(host=settings.server.host, port=settings.server.port, debug=settings.server.debug) if __name__ == "__main__": diff --git a/automappa/settings.py b/automappa/settings.py index 31850f79..eabb3ffe 100644 --- a/automappa/settings.py +++ b/automappa/settings.py @@ -14,9 +14,6 @@ class DatabaseSettings(BaseSettings): - # TODO: Figure out how to make the url dynamic to docker_url or local_url based on - # is_docker_service... e.g. - # url: PostgresDsn = docker_url if is_docker_service else local_url url: PostgresDsn pool_size: Optional[int] = 3 pool_pre_ping: Optional[bool] = False diff --git a/automappa/tasks.py b/automappa/tasks.py index f7c7e5b2..d279e084 100644 --- a/automappa/tasks.py +++ b/automappa/tasks.py @@ -1,16 +1,15 @@ #!/usr/bin/env python +import logging import os import glob import pandas as pd from geom_median.numpy import compute_geometric_median -from celery.utils.log import get_task_logger -from celery import Celery, chain +from celery import Celery from celery.result import AsyncResult -from autometa.common.external import hmmscan from autometa.common.kmers import normalize, embed, count from automappa import settings @@ -25,7 +24,11 @@ __name__, backend=settings.celery.backend_url, broker=settings.celery.broker_url ) -logger = get_task_logger(__name__) +logging.basicConfig( + format="[%(levelname)s] %(name)s: %(message)s", + level=logging.DEBUG, +) +logger = logging.getLogger(__name__) def get_job(job_id): @@ -36,7 +39,7 @@ def get_job(job_id): return AsyncResult(job_id, app=queue) -# @queue.task +@queue.task def get_marker_symbols(bin_df: pd.DataFrame, markers_df: pd.DataFrame) -> pd.DataFrame: marker_counts = get_contig_marker_counts(bin_df, markers_df) marker_symbols = convert_marker_counts_to_marker_symbols(marker_counts) @@ -50,7 +53,6 @@ def get_marker_symbols(bin_df: pd.DataFrame, markers_df: pd.DataFrame) -> pd.Dat # TODO: kmer freq. analysis pipeline # TODO: scatterplot 2-d embedding views - @queue.task def get_embedding( assembly: str, norm_method: str = "am_clr", embed_method: str = "densmap" @@ -67,38 +69,6 @@ def get_embedding( return embed(norm_df, method=embed_method, embed_dimensions=2) -# @queue.task -# def hmmdb_formatter(hmmdb) -> None: -# hmmscan.hmmpress(hmmdb) - - -# @queue.task -# def scanner(seqfile, hmmdb, out) -> str: -# # NOTE: returns outfpath -# # cmd = [ -# # "hmmscan", -# # "--cpu", -# # "1", -# # "--seed", -# # "42", -# # "--tblout", -# # out, -# # hmmdb, -# # seqfile -# # ] -# # run_cmd = " ".join(cmd) -# # os.system(run_cmd) -# hmmscan.run( -# orfs=seqfile, -# hmmdb=hmmdb, -# outfpath=out, -# cpus=2, -# parallel=True, -# seed=42, -# force=True, -# ) - - @queue.task def get_clusters_geom_medians( df: pd.DataFrame, cluster_col: str = "cluster", weight_col: str = "length" diff --git a/docker-compose.yml b/docker-compose.yml index c2ec1919..7ef1f65d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -80,6 +80,7 @@ services: context: . dockerfile: Dockerfile restart: always + mem_limit: 4GB # Not sure which is more appropriate for production :shrug: # This command allows live-reloading for dev # command: automappa From 9917443616490638aaed06a4e9047c4d8c8bbbc6 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Tue, 26 Apr 2022 12:17:03 -0500 Subject: [PATCH 23/37] :green_heart::bug: Move plotly channel below bioconda and conda-forge --- environment.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/environment.yml b/environment.yml index a2270274..f345d791 100644 --- a/environment.yml +++ b/environment.yml @@ -1,8 +1,8 @@ name: automappa channels: - - plotly - conda-forge - bioconda + - plotly - defaults dependencies: - autometa From 731ffc1d23e5ef2fd1d895f53644f54b2ce9f7cf Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Thu, 5 May 2022 01:20:54 -0400 Subject: [PATCH 24/37] :art: WIP Working task-queue with scatterplot-2d views including embedding methods :art::bug: Need to replace kmers.embed(...) method in tasks.py to include n_jobs=1 when autometa 2.0.4 release is out :art::broccoli: Add celeryconfig :art::broccoli: Add working kmer freq. analysis parallelize pipeline for use in task-queue :art: Add use of pydantic.BaseModel in models.py of SampleTables, KmerTable, AnnotationTable and misc. others (WIP) :art: Add construction of SeqIO.SeqRecords when retrieving metagenome from table using new func, get_metagenome_seqrecords(...) :art: Refactor get_contig_marker_counts(...) :art: Refactor get_marker_symbols(...) in markers.py :art::fire: Remove hard-coded values in automappa.utils.figures.get_embedding_traces_df(...) :art: Parse selected_data_tables as SampleTables.parse_raw(selected_data_tables) for shared data :art: Add tasks button to home.py :art: Replace typehints for selected_data_tables to Json[SampleTables] --- README.md | 263 ++----------------------------- TODO.md | 10 +- automappa/apps/home.py | 142 ++++++++++++++--- automappa/apps/mag_refinement.py | 207 +++++++++++++++--------- automappa/apps/mag_summary.py | 128 ++++++++------- automappa/conf/__init__.py | 0 automappa/conf/celeryconfig.py | 5 + automappa/db.py | 10 ++ automappa/index.py | 24 ++- automappa/tasks.py | 194 +++++++++++++---------- automappa/utils/figures.py | 55 +++---- automappa/utils/markers.py | 31 +++- automappa/utils/models.py | 224 ++++++++++++++++++++++++++ automappa/utils/serializers.py | 31 +++- docker-compose.yml | 4 +- 15 files changed, 790 insertions(+), 538 deletions(-) create mode 100644 automappa/conf/__init__.py create mode 100644 automappa/conf/celeryconfig.py create mode 100644 automappa/utils/models.py diff --git a/README.md b/README.md index c0d4cdf1..f15ed46c 100644 --- a/README.md +++ b/README.md @@ -14,267 +14,34 @@ ## Getting Started -- [Install with conda](#install-with-conda) -- [Run `automappa` using docker](#quickstart-using-docker-no-installation-required) -- [Install from source](#install-from-source) -- [Advanced Usage](#advanced-usage) - - [A breakdown of the docker run wrapper script](#full-docker-run-command-example) - - [Using a remote Automappa server](#using-a-remote-automappa-server) - - [Using a remote docker container Automappa server](#using-a-remote-docker-container-automappa-server) +- [Clone the Automappa Repo](#clone-the-repository) +- [Run `make build` using Makefile](#build-images-for-automappa-services) +- [Run `make up` using Makefile](#build-and-run-automappa-services) +- [Open the Automappa url](#navigate-to-automappa-page) +## Clone the repository -## Install with conda +## Automappa testing setup/run commands -If you are using `conda` (or `mamba`) as a package manager, you can simply install `automappa` using one of the following one-liners. - -### with `conda` - -```bash -conda install -c bioconda automappa -``` - -### with `mamba` - -```bash -mamba install -c bioconda automappa -``` - -After you have installed `automappa`, you can simply run `automappa -h` to see a list of available arguments. - -To start the `automappa` app, you must specify your main binning results and respective kingdom's single-copy marker annotations -generated from an [Autometa analysis](https://www.github.com/KwanLab/Autometa). If you do not yet have these annotations and are -not sure where to start, I would recommend checking out [Autometa's documentation](https://autometa.readthedocs.io/en/latest/) - -### Example `automappa` command - -```bash -automappa --binning-main --markers -``` - -## Quickstart using Docker (No installation required) - - To quickly start exploring your data, run the app using a wrapper script that will run the docker image, `evanrees/automappa:latest`, ([available from Dockerhub](https://cloud.docker.com/repository/docker/evanrees/automappa/tags "Automappa Dockerhub Tags")). Now you can skip installation and start binning, examining and describing! Let the microbial exegesis begin! - -### Running with a docker container using `run_automappa.sh` - -A docker wrapper is available to run a docker container of `Automappa`. -The only required input for this script is the autometa main binning output table and the respective markers table. - -```bash -# First retrieve the script: -curl -o run_automappa.sh https://raw.githubusercontent.com/WiscEvan/Automappa/main/docker/run_automappa.sh -# (make it executable) -chmod a+x run_automappa.sh -``` - -Now run automappa on autometa binning results using the downloaded script: `run_automappa.sh`. - -### Start automappa docker container - -***NOTE: This will pull the automappa docker image if it is not already available*** - -```bash -./run_automappa.sh --binning binning.main.tsv --markers binning.markers.tsv -``` - ----------------------------------------------------------------------------------------------------- - -## Install from source - -### Installation from source (using `make`) - -You can install all of Automappa's dependencies using the Makefile found within the repository. - -#### Clone the Automappa repository - -```bash -cd $HOME -git clone https://github.com/WiscEvan/Automappa.git -cd $HOME/Automappa -``` - -#### First create environment - -```bash -make create_environment -``` - -#### Activate environment - -```bash -source activate automappa -``` - -#### The following will install the automappa entrypoint - -```bash -make install -``` - -Now that all of the dependencies are installed, you may run the app on your local machine or on a server. - - -### Listing available `make` commands - -You may also list other available make commands by simply typing `make` with no other arguments. - -```bash -make -``` - -A few examples: - -#### pull docker image - -```bash -make docker -``` - -#### build docker image - -```bash -make image -``` - -## Usage - -Simply provide the `automappa` entrypoint with the main binning file output by Autometa as well as the respective markers file. - -```bash -automappa \ - --binning-main \ - --markers -``` - ----------------------------------------------------------------------------------------------------- - -## Advanced Usage - -### Full `docker run` command example - -```bash -# Set automappa parameters (required) -binning="$HOME/test/binning.main.tsv" -markers="$HOME/test/binning.markers.tsv" - -# Set docker image/container parameters (optional) -localport=8050 -containerport=8886 -imagetag="latest" - -#NOTE: Some necessary path handling here for binding docker volumes -binning_dirname="$( cd -- "$(dirname "$binning")" >/dev/null 2>&1 ; pwd -P )" -binning_filename=$(basename $binning) -markers_dirname="$( cd -- "$(dirname "$markers")" >/dev/null 2>&1 ; pwd -P )" -markers_filename=$(basename $markers) - -# Run with provided parameters -docker run \ - --publish $localport:$containerport \ - --detach=false \ - -v $binning_dirname:/binning:rw \ - -v $markers_dirname:/markers:ro \ - --rm \ - evanrees/automappa:$imagetag \ - --binning-main /binning/$binning_filename \ - --markers /markers/$markers_filename \ - --port $containerport \ - --host 0.0.0.0 -``` - -## Using a remote Automappa server - -If you'd like to run Automappa on a *remote* server but view the output on your *local* machine, - -### Example remote server login with ssh tunnel - -you first need to login to the remote server with a tunnel, e.g. `ssh -L localport:localhost:serverport user@hostaddress`. - -```bash -#ssh -L localport:127.0.0.1:serverport user@kwan-bioinformatics.pharmacy.wisc.edu -#example -ssh -L 8888:127.0.0.1:8050 sam@kwan-bioinformatics.pharmacy.wisc.edu -``` - -Once you are on the server, simply start the Automappa server (with the appropriate port from the ssh tunnel). +### clone the Automappa Repository ```bash -automappa \ - --binning-main \ - --markers \ - --port 8050 +git clone -b home-tab https://github.com/WiscEvan/Automappa ``` -Navigate to the app view in your browser. - -This will correspond to the localport that was passed in upon login to the remote server. -In the previous example above we would navigate to `localhost:8888`. - -I've numbered the ports here to help illustrate the network communication. - -| Bridge | Port Bridge | Communication Context | -| :------------- | :------------- | :------------- | -| `localport:remoteport` | `8888:8050` | `local:remote` | - -### Using a remote docker container Automappa server - -To access Automappa through a docker container that is on a remote machine, one additional bridge -must be constructed. - -First we need to forward a port from the server back to our local machine. +### build images for automappa services ```bash -#ssh -L localport:localhost:serverport user@kwan-bioinformatics.pharmacy.wisc.edu -ssh -L 8888:localhost:8887 sam@kwan-bioinformatics.pharmacy.wisc.edu +make build ``` -Now run automappa using the docker wrapper script: `run_automappa.sh` +### build and run automappa services -> NOTE: A wrapper is available for download to run docker with port-forwarding. +NOTE: you can skip `make build` if you’d like, as this command will build and pull any images not available. ```bash -curl -o $HOME/run_automappa.sh https://raw.githubusercontent.com/WiscEvan/Automappa/main/docker/run_automappa.sh -chmod a+x $HOME/run_automappa.sh +make up ``` -Now start automappa while setting `--localport` to match the `serverport` (`8887` from above). - -```bash -# NOTE: This will pull the automappa docker image if it is not already available. -$HOME/run_automappa.sh \ - --imagetag main \ - # NOTE: The 'localport' here is referring to the port on the remote - --localport 8887 \ - --containerport 8050 \ - --binning binning.main.tsv \ - --markers binning.markers.tsv -``` - -Now navigate to `http://localhost:8888` and you will see the loaded data. - -I've numbered the ports here to help illustrate the network communication. - -#### Example port forwarding breakdown - -| Server | Port | -| :------------- | :------------- | -| Docker container | 8050 | -| Remote Server | 8887 | -| Local Computer | 8888 | - -#### Note - -- You may change **any** of these values as long as you change the respective value. -- This will be most useful if **multiple users** will need to use the app. - -| Bridge | Port Bridge | Communication Context | -| :------------- | :------------- | :------------- | -| `remoteport:containerport` | `8887:8050` | `remote:docker` | -| `localport:remoteport` | `8888:8887` | `local:remote` | - -e.g. - -- `localhost:8888` <-> `8888:8887` <-> `8887:8050` - -or +### Navigate to Automappa page -- `localhost:localport` <-> `localport:serverport` <-> `serverport:containerport` +Once you see `automappa_web_1` running from the terminal logs, you should be able to navigate to 🥳 diff --git a/TODO.md b/TODO.md index 67d3838b..518b3255 100644 --- a/TODO.md +++ b/TODO.md @@ -13,15 +13,23 @@ 8. [x] :carrot::racehorse: Add celery task-queue 9. [x] :carrot::racehorse: Add celery-monitoring services 10. [ ] :carrot::racehorse: Add uploaded data ingestion to task-queue -11. [ ] :carrot::racehorse: Add k-mer embedding tasks to task-queue +11. [x] :carrot::racehorse: Add k-mer embedding tasks to task-queue 12. [x] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) 13. [x] :art: Refactor `on_{binning,markers,metagenome}_upload` callbacks to one function that takes in the filetype to determine storage method 14. [x] :racehorse: Retrieve datatable within `mag_summary.py` 15. [x] :whale::elephant::sunflower: Grafana provision from within `docker-compose.yml` 16. [ ] :art::fire: Refactor `store_binning_main(...)` s.t. refinement-data is stored in a separate async process. +17. [ ] :art::carrot: Add selections/dropdowns/progress updates for metagenome embeddings +18. [ ] :art: Add embeddings selection dropdown in settings for 2d scatterplot axes (place task queueing from these selections) -------------------------------------------------------------------------------------------------- +## Troubleshooting Celery, RabbitMQ task-queue + +### Resources + +- [celery rabbitmq tutorial](https://suzannewang.com/celery-rabbitmq-tutorial/) + ## docker-compose services configuration ***NOTE: All of this assumes you have all docker services running via `make up` or `docker-compose up`*** diff --git a/automappa/apps/home.py b/automappa/apps/home.py index 8db15a0e..0cb2e4c0 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- -import os import logging from dash import html, dcc from dash.dash_table import DataTable @@ -13,6 +12,8 @@ import dash_uploader as du from automappa.app import app +from automappa.tasks import preprocess_clusters_geom_medians, preprocess_embeddings, preprocess_marker_symbols +from automappa.utils.models import SampleTables from automappa.utils.serializers import ( get_uploaded_files_table, file_to_db, @@ -183,10 +184,7 @@ samples_datatable = ( dcc.Loading( id="loading-samples-datatable", - children=[ - html.Label("Uploaded Datasets"), - html.Div(id="samples-datatable") - ], + children=[html.Label("Uploaded Datasets"), html.Div(id="samples-datatable")], type="dot", color="#646569", ), @@ -197,7 +195,7 @@ id="loading-selected-tables-datatable", children=[ html.Label("Selected Datasets for Refinement & Summary:"), - html.Div(id="selected-tables-datatable") + html.Div(id="selected-tables-datatable"), ], type="dot", color="#646569", @@ -241,6 +239,13 @@ children="Refine MAGs", ) +kmer_embed_tasks_button = dbc.Button( + id="kmer-embed-tasks-button", + children="Submit k-mer embedding tasks", +) + +tasks_table = html.Div(id="embedding-tasks") + layout = dbc.Container( children=[ dbc.Row(upload_modal), @@ -253,6 +258,9 @@ dbc.Row(refine_mags_button), html.Br(), dbc.Row(selected_tables_datatable), + html.Br(), + kmer_embed_tasks_button, + dbc.Row(tasks_table), ], fluid=True, ) @@ -508,12 +516,14 @@ def toggle_modal(n_open, n_close, is_open): @app.callback( Output("refine-mags-button", "disabled"), - [Input("binning-select", "value"), - Input("markers-select", "value"), - Input("metagenome-select", "value"),], + [ + Input("binning-select", "value"), + Input("markers-select", "value"), + Input("metagenome-select", "value") + ], ) def refine_mags_button_active_callback(binning_value, markers_value, metagenome_value): - if binning_value is None or markers_value is None: + if binning_value is None or markers_value is None or metagenome_value is None: return True else: return False @@ -532,11 +542,34 @@ def on_refine_mags_button_click( ): if n is None: raise PreventUpdate - return { - "binning": binning_select_value, - "markers": markers_select_value, - "metagenome": metagenome_select_value, - } + tables_dict = {} + if binning_select_value is not None and markers_select_value is not None: + marker_symbols_task = preprocess_marker_symbols.delay(binning_select_value, markers_select_value) + # logger.debug(f"{type(marker_symbols_task)} {marker_symbols_task}") + # tables_dict["marker_symbols"] = markers_select_value.replace("-markers","-marker-symbols") + if metagenome_select_value is not None: + tables_dict["metagenome"] = {"id":metagenome_select_value} + embeddings_task = preprocess_embeddings( + metagenome_table=metagenome_select_value, + norm_method="am_clr", + embed_methods=["densmap", "umap", "bhsne"], + ) + # logger.debug(f"{type(embeddings_task)} {embeddings_task}") + # tables_dict["embeddings"] = metagenome_select_value.replace("-metagenome","-embeddings") + if binning_select_value is not None: + tables_dict.update({ + "binning": {"id":binning_select_value}, + "refinements": {"id": binning_select_value.replace("-binning","-refinement")} + }) + cluster_col = "cluster" + clusters_geom_medians_task = preprocess_clusters_geom_medians.delay(binning_select_value, cluster_col) + # logger.debug(f"{type(clusters_geom_medians_task)} {clusters_geom_medians_task}") + # tables_dict["geom_medians"] = binning_select_value.replace("-binning",f"{cluster_col}-gmedians") + if markers_select_value is not None: + tables_dict["markers"] = {"id":markers_select_value} + logger.debug(tables_dict) + return SampleTables(**tables_dict).json() + @app.callback( Output("selected-tables-datatable", "children"), @@ -546,20 +579,87 @@ def on_refine_mags_button_click( ], State("selected-tables-store", "data"), ) -def selected_tables_datatable_children(selected_tables_store_data, active_tab, new_selected_tables_store_data): - # Why Input("tabs", "active_tab"): Navigating to back to home tab triggers rendering of table from store +def selected_tables_datatable_children( + selected_tables_store_data: SampleTables, active_tab: str, new_selected_tables_store_data: SampleTables +): + # Why Input("tabs", "active_tab"): Navigating back to home tab triggers rendering of table from store if selected_tables_store_data is None: raise PreventUpdate + samples = SampleTables.parse_raw(selected_tables_store_data) if new_selected_tables_store_data is not None: - selected_tables_store_data.update(new_selected_tables_store_data) - - if not selected_tables_store_data: + new_tables = SampleTables.parse_raw(new_selected_tables_store_data) + if new_tables != samples: + tables_dict = samples.dict() + tables_dict.update(new_tables.dict()) + samples = SampleTables.parse_obj(tables_dict) + + has_table = False + for __,table_id in samples: + if table_id: + has_table = True + break + + if not has_table: raise PreventUpdate return DataTable( - data=[{"filetype":filetype, "table_id": table_id} for filetype,table_id in selected_tables_store_data.items() if table_id is not None], + data=[ + {"filetype": sample, "table_id": table.id} + for sample,table in samples + if sample not in {"kmers"} + ], columns=[ {"id": "filetype", "name": "filetype", "editable": False}, {"id": "table_id", "name": "table_id", "editable": False}, ], ) + + +@app.callback( + Output("kmer-embed-tasks-button", "disabled"), + Input("metagenome-select", "value"), +) +def refine_mags_button_active_callback(metagenome_value): + if metagenome_value is None: + return True + else: + return False + + +# TODO: Store final embeddings-table and retrieve for 2d scatterplot axes dropdowns/views +# from automappa.utils.serializers import get_table +# embed_df = get_table(embed_table_name, index_col='contig') + +@app.callback( + Output("embedding-tasks", "children"), + Input("kmer-embed-tasks-button", "n_clicks"), + Input("metagenome-select", "value"), +) +def on_compute_metagenome_kmer_embedding(btn_clicks: int, metagenome_select_value: str): + if btn_clicks is None or metagenome_select_value is None: + raise PreventUpdate + embed_methods = ["densmap", "umap", "bhsne"] + norm_method = "am_clr" + task = preprocess_embeddings( + metagenome_table=metagenome_select_value, + norm_method=norm_method, + embed_methods=embed_methods, + ) + embed_table_name = metagenome_select_value.replace("-metagenome", "-embeddings") + # TODO: Should create a polling or dcc.Interval(...) to construct this table for monitoring tasks status + df = pd.DataFrame([{ + "task_name": task.name, + # "started": task.track_started, + "state": task.state, + "task_id": task.id, + "norm_method": norm_method, + "embed_methods": ','.join(embed_methods), + "embed-table-name": embed_table_name, + }]) + # logger.debug(df) + return DataTable( + data=df.to_dict("records"), + columns=[{"id": col, "name": col, "editable": False} for col in df.columns], + persistence=True, + persistence_type='session', + ) diff --git a/automappa/apps/mag_refinement.py b/automappa/apps/mag_refinement.py index 16044a1f..dc10c65b 100644 --- a/automappa/apps/mag_refinement.py +++ b/automappa/apps/mag_refinement.py @@ -2,9 +2,13 @@ import logging -numba_logger = logging.getLogger('numba') +from pydantic import Json + +from automappa.utils.models import SampleTables + +numba_logger = logging.getLogger("numba") numba_logger.setLevel(logging.WARNING) -h5py_logger = logging.getLogger('h5py') +h5py_logger = logging.getLogger("h5py") h5py_logger.setLevel(logging.WARNING) from typing import Dict, List @@ -25,9 +29,8 @@ from automappa.app import app -from automappa.tasks import get_marker_symbols from automappa.utils.serializers import get_table, table_to_db -# from automappa.utils.markers import get_marker_symbols + from automappa.utils.figures import ( format_axis_title, get_scatterplot_2d, @@ -75,13 +78,8 @@ html.Label("X-axis:"), dcc.Dropdown( id="x-axis-2d", - options=[ - {"label": "X_1", "value": "x_1"}, - {"label": "Coverage", "value": "coverage"}, - {"label": "GC%", "value": "gc_content"}, - {"label": "Length", "value": "length"}, - ], - value="x_1", + options=[], + value="coverage", clearable=False, ), ] @@ -90,13 +88,8 @@ html.Label("Y-axis:"), dcc.Dropdown( id="y-axis-2d", - options=[ - {"label": "X_2", "value": "x_2"}, - {"label": "Coverage", "value": "coverage"}, - {"label": "GC%", "value": "gc_content"}, - {"label": "Length", "value": "length"}, - ], - value="x_2", + options=[], + value="gc_content", clearable=False, ), ] @@ -459,17 +452,64 @@ def toggle_offcanvas(n1: int, is_open: bool) -> bool: @app.callback( - Output("color-by-column", "options"), [Input("metagenome-annotations", "data")] + Output("color-by-column", "options"), + Input("selected-tables-store", "data") ) -def color_by_column_options_callback(annotations_json: "str | None"): - df = pd.read_json(annotations_json, orient="split") +def color_by_column_options_callback(selected_tables_data: SampleTables): + sample = SampleTables.parse_raw(selected_tables_data) + df = sample.binning.table return [ {"label": col.title().replace("_", " "), "value": col} - for col in df.columns - if df[col].dtype.name not in {"float64", "int64"} and col != "contig" + for col in df.select_dtypes('object').columns ] +@app.callback( + Output("x-axis-2d", "options"), + Input("selected-tables-store", "data") +) +def x_axis_2d_options_callback(selected_tables_data: SampleTables): + sample = SampleTables.parse_raw(selected_tables_data) + binning_df = sample.binning.table + binning_cols = [ + {"label": col.title().replace("_", " "), "value": col, 'disabled': False} + for col in binning_df.select_dtypes({"float64", "int64"}).columns + if col not in {"completeness", "purity", "taxid"} + ] + kmer_cols = [ + { + "label": f"{kmer.embedding.name.replace('-',' ')}_x_1", + "value": f"{kmer.embedding.name}_x_1", + 'disabled': not kmer.embedding.exists, + } + for kmer in sample.kmers + ] + return binning_cols + kmer_cols + +@app.callback( + Output("y-axis-2d", "options"), + Input("selected-tables-store", "data") +) +def y_axis_2d_options_callback(selected_tables_data: Json[SampleTables]): + sample = SampleTables.parse_raw(selected_tables_data) + binning_df = sample.binning.table + binning_cols = [ + {"label": col.title().replace("_", " "), "value": col, 'disabled': False} + for col in binning_df.select_dtypes({"float64", "int64"}).columns + if col not in {"completeness", "purity", "taxid"} + ] + kmer_cols = [ + { + "label": f"{kmer.embedding.name.replace('-',' ')}_x_2", + "value": f"{kmer.embedding.name}_x_2", + 'disabled': not kmer.embedding.exists, + } + for kmer in sample.kmers + ] + return binning_cols + kmer_cols + + + @app.callback( Output("mag-metrics-datatable", "children"), [ @@ -478,11 +518,11 @@ def color_by_column_options_callback(annotations_json: "str | None"): ], ) def update_mag_metrics_datatable_callback( - selected_tables_data: Dict[str, str], + selected_tables_data: Json[SampleTables], selected_contigs: Dict[str, List[Dict[str, str]]], ) -> DataTable: - table_name = selected_tables_data["markers"] - markers_df = get_table(table_name, index_col="contig") + sample = SampleTables.parse_raw(selected_tables_data) + markers_df = sample.markers.table if selected_contigs: contigs = {point["text"] for point in selected_contigs["points"]} selected_contigs_count = len(contigs) @@ -556,7 +596,6 @@ def update_mag_metrics_datatable_callback( Output("scatterplot-2d", "figure"), [ Input("selected-tables-store", "data"), - # Input("contig-marker-symbols-store", "data"), Input("x-axis-2d", "value"), Input("y-axis-2d", "value"), Input("scatterplot-2d-legend-toggle", "value"), @@ -566,9 +605,8 @@ def update_mag_metrics_datatable_callback( ], ) def scatterplot_2d_figure_callback( - selected_tables_data: Dict[str, str], - # refinement: "str | None", - # contig_marker_symbols_json: "str | None", + # selected_tables_data: MetagenomeAnnotationsTables, + selected_tables_data: Json[SampleTables], xaxis_column: str, yaxis_column: str, show_legend: bool, @@ -578,11 +616,13 @@ def scatterplot_2d_figure_callback( ) -> go.Figure: # NOTE: btn_clicks is an input so this figure is updated when new refinements are saved # TODO: #23 refactor scatterplot callbacks - bin_table_name = selected_tables_data["binning"] - bin_df = get_table(bin_table_name, index_col="contig") - markers_table_name = selected_tables_data["markers"] - markers_df = get_table(markers_table_name, index_col="contig") - markers = get_marker_symbols(bin_df, markers_df) + # TODO: Refactor data retrieval/validation + sample = SampleTables.parse_raw(selected_tables_data) + bin_df = sample.binning.table + # TODO: Replace binning table w/coords-data + # Replace binning table w/metagenome-annotations-data[TODO] + # mag_cols=["length", "gc_content", "coverage"] + markers = sample.marker_symbols.table if color_by_col not in bin_df.columns: for col in ["phylum", "class", "order", "family"]: if col in bin_df.columns: @@ -594,8 +634,7 @@ def scatterplot_2d_figure_callback( ) # Subset metagenome-annotations by selections iff selections have been made if hide_selection_toggle: - refine_table_name = selected_tables_data["binning"].replace("-binning","-refinement") - refine_df = get_table(refine_table_name, index_col="contig") + refine_df = sample.refinements.table refine_cols = [col for col in refine_df.columns if "refinement" in col] if refine_cols: latest_refine_col = refine_cols.pop() @@ -610,14 +649,38 @@ def scatterplot_2d_figure_callback( # batches for respective styling,layout,traces, etc. # TODO: Put figure or traces in store, get/update/select # based on current contig selections + # TODO: Should check norm_method, kmer_size prior to retrieving embeddings table... + # Add norm method and kmer_size dropdowns... + if "_x_1" in xaxis_column or "_x_2" in yaxis_column: + if sample.embeddings.exists: + embedding_df = sample.embeddings.table + bin_df = bin_df.join(embedding_df, how='left') + else: + for kmer_table in sample.kmers: + if kmer_table.embedding.name == xaxis_column and kmer_table.embedding.name == yaxis_column: + bin_df = bin_df.join(kmer_table.embedding.table, how='left') + break + + fillnas = { + "cluster":"unclustered", + "superkingdom":"unclassified", + "phylum":"unclassified", + "class":"unclassified", + "order":"unclassified", + "family":"unclassified", + "genus":"unclassified", + "species":"unclassified", + } + fillna = fillnas.get(color_by_col, "unclustered") fig = get_scatterplot_2d( bin_df, x_axis=xaxis_column, y_axis=yaxis_column, color_by_col=color_by_col, - fillna="unclustered", + fillna=fillna, ) + # TODO: If possible, as a separate callback do Output("scatterplot-2d", "layout") with fig.batch_update(): fig.layout.xaxis.title = format_axis_title(xaxis_column) fig.layout.yaxis.title = format_axis_title(yaxis_column) @@ -625,6 +688,7 @@ def scatterplot_2d_figure_callback( fig.layout.showlegend = show_legend # Update markers with symbol and size corresponding to marker count + # TODO: If possible, as a separate callback do Output("scatterplot-2d", "traces") fig.for_each_trace( lambda trace: trace.update( marker_symbol=markers.symbol.loc[trace.text], @@ -643,15 +707,15 @@ def scatterplot_2d_figure_callback( ], ) def taxonomy_distribution_figure_callback( - selected_tables_data: Dict[str, str], + selected_tables_data: SampleTables, selected_contigs: Dict[str, List[Dict[str, str]]], selected_rank: str, ) -> go.Figure: - table_name = selected_tables_data["binning"] - df = get_table(table_name) + sample = SampleTables.parse_raw(selected_tables_data) + df = sample.binning.table if selected_contigs and selected_contigs["points"]: contigs = {point["text"] for point in selected_contigs["points"]} - df = df.loc[df.contig.isin(contigs)] + df = df.loc[df.index.isin(contigs)] fig = taxonomy_sankey(df, selected_rank=selected_rank) return fig @@ -667,21 +731,21 @@ def taxonomy_distribution_figure_callback( ], ) def scatterplot_3d_figure_callback( - selected_tables_data: Dict[str, str], + selected_tables_data: SampleTables, z_axis: str, show_legend: bool, color_by_col: str, selected_contigs: Dict[str, List[Dict[str, str]]], ) -> go.Figure: - table_name = selected_tables_data["binning"] - df = get_table(table_name) + sample = SampleTables.parse_raw(selected_tables_data) + df = sample.binning.table color_by_col = "phylum" if color_by_col not in df.columns else color_by_col if not selected_contigs: - contigs = set(df.contig.tolist()) + contigs = set(df.index.tolist()) else: contigs = {point["text"] for point in selected_contigs["points"]} # Subset DataFrame by selected contigs - df = df[df.contig.isin(contigs)] + df = df[df.index.isin(contigs)] if color_by_col == "cluster": # Categoricals for binning df[color_by_col] = df[color_by_col].fillna("unclustered") @@ -708,14 +772,14 @@ def scatterplot_3d_figure_callback( ], ) def mag_summary_coverage_boxplot_callback( - selected_tables_data: Dict[str, str], selected_data: Dict[str, List[Dict[str, str]]] + selected_tables_data: SampleTables, selected_data: Dict[str, List[Dict[str, str]]] ) -> go.Figure: if not selected_data: raise PreventUpdate - table_name = selected_tables_data["binning"] - df = get_table(table_name) + sample = SampleTables.parse_raw(selected_tables_data) + df = sample.binning.table contigs = {point["text"] for point in selected_data["points"]} - df = df.loc[df.contig.isin(contigs)] + df = df.loc[df.index.isin(contigs)] fig = metric_boxplot(df, metrics=["coverage"], boxmean="sd") return fig @@ -728,14 +792,14 @@ def mag_summary_coverage_boxplot_callback( ], ) def mag_summary_gc_content_boxplot_callback( - selected_tables_data: Dict[str, str], selected_data: Dict[str, List[Dict[str, str]]] + selected_tables_data: SampleTables, selected_data: Dict[str, List[Dict[str, str]]] ) -> go.Figure: if not selected_data: raise PreventUpdate - table_name = selected_tables_data["binning"] - df = get_table(table_name) + sample = SampleTables.parse_raw(selected_tables_data) + df = sample.binning.table contigs = {point["text"] for point in selected_data["points"]} - df = df.loc[df.contig.isin(contigs)] + df = df.loc[df.index.isin(contigs)] fig = metric_boxplot(df, metrics=["gc_content"], boxmean="sd") fig.update_traces(name="GC Content") return fig @@ -749,14 +813,14 @@ def mag_summary_gc_content_boxplot_callback( ], ) def mag_summary_length_boxplot_callback( - selected_tables_data: Dict[str, str], selected_data: Dict[str, List[Dict[str, str]]] + selected_tables_data: SampleTables, selected_data: Dict[str, List[Dict[str, str]]] ) -> go.Figure: if not selected_data: raise PreventUpdate - table_name = selected_tables_data["binning"] - df = get_table(table_name) + sample = SampleTables.parse_raw(selected_tables_data) + df = sample.binning.table contigs = {point["text"] for point in selected_data["points"]} - df = df.loc[df.contig.isin(contigs)] + df = df.loc[df.index.isin(contigs)] fig = metric_boxplot(df, metrics=["length"]) return fig @@ -769,14 +833,13 @@ def mag_summary_length_boxplot_callback( ], ) def refinements_table_callback( - selected_tables_data: Dict[str, str], + selected_tables_data: SampleTables, btn_clicks: int, ) -> DataTable: - refine_table_name = selected_tables_data["binning"].replace("-binning","-refinement") - df = get_table(refine_table_name) + sample = SampleTables.parse_raw(selected_tables_data) return DataTable( - data=df.to_dict("records"), - columns=[{"name": col, "id": col} for col in df.columns], + data=sample.refinements.table.to_dict("records"), + columns=[{"name": col, "id": col} for col in sample.refinements.table.columns], style_cell={"textAlign": "center"}, style_cell_conditional=[{"if": {"column_id": "contig"}, "textAlign": "right"}], virtualization=True, @@ -791,13 +854,13 @@ def refinements_table_callback( ], ) def download_refinements( - n_clicks: int, selected_tables_data: Dict[str, str], + n_clicks: int, + selected_tables_data: SampleTables, ) -> Dict[str, "str | bool"]: if not n_clicks: raise PreventUpdate - refine_table_name = selected_tables_data["binning"].replace("-binning","-refinement") - df = get_table(refine_table_name) - return send_data_frame(df.to_csv, "refinements.csv", index=False) + sample = SampleTables.parse_raw(selected_tables_data) + return send_data_frame(sample.refinements.table.to_csv, "refinements.csv", index=False) @app.callback( @@ -820,14 +883,14 @@ def mag_refinement_save_button_disabled_callback( ) def store_binning_refinement_selections( selected_data: Dict[str, List[Dict[str, str]]], - selected_tables_data: Dict[str, str], + selected_tables_data: SampleTables, n_clicks: int, ) -> int: # Initial load... if not n_clicks or (n_clicks and not selected_data) or not selected_data: raise PreventUpdate - refine_table_name = selected_tables_data["binning"].replace("-binning","-refinement") - bin_df = get_table(refine_table_name, index_col="contig") + sample = SampleTables.parse_raw(selected_tables_data) + bin_df = sample.refinements.table refinement_cols = [col for col in bin_df.columns if "refinement" in col] refinement_num = len(refinement_cols) + 1 refinement_name = f"refinement_{refinement_num}" @@ -835,5 +898,5 @@ def store_binning_refinement_selections( bin_df.loc[contigs, refinement_name] = refinement_name bin_df = bin_df.fillna(axis="columns", method="ffill") bin_df.reset_index(inplace=True) - table_to_db(df=bin_df, name=refine_table_name) + table_to_db(df=bin_df, name=sample.refinements.id) return 0 diff --git a/automappa/apps/mag_summary.py b/automappa/apps/mag_summary.py index ed280da3..b271bbea 100644 --- a/automappa/apps/mag_summary.py +++ b/automappa/apps/mag_summary.py @@ -16,6 +16,7 @@ from automappa.app import app from automappa.utils.figures import metric_barplot, taxonomy_sankey, metric_boxplot +from automappa.utils.models import SampleTables from automappa.utils.serializers import get_table pio.templates.default = "plotly_white" @@ -219,7 +220,7 @@ ], ) def mag_overview_metrics_boxplot_callback( - selected_tables_data: Dict[str, str], cluster_col: str + selected_tables_data: SampleTables, cluster_col: str ) -> go.Figure: """ Writes @@ -230,8 +231,8 @@ def mag_overview_metrics_boxplot_callback( Returns: n_unique_bins - number of unique bins """ - table_name = selected_tables_data["binning"] - mag_summary_df = get_table(table_name) + tables = SampleTables.parse_raw(selected_tables_data) + mag_summary_df = get_table(tables.binning) if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -248,10 +249,10 @@ def mag_overview_metrics_boxplot_callback( ], ) def mag_overview_gc_content_boxplot_callback( - selected_tables_data: Dict[str, str], cluster_col: str + selected_tables_data: SampleTables, cluster_col: str ) -> go.Figure: - table_name = selected_tables_data["binning"] - mag_summary_df = get_table(table_name) + tables = SampleTables.parse_raw(selected_tables_data) + mag_summary_df = get_table(tables.binning) if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -269,10 +270,10 @@ def mag_overview_gc_content_boxplot_callback( ], ) def mag_overview_length_boxplot_callback( - selected_tables_data: Dict[str, str], cluster_col: str + selected_tables_data: SampleTables, cluster_col: str ) -> go.Figure: - table_name = selected_tables_data["binning"] - mag_summary_df = get_table(table_name) + tables = SampleTables.parse_raw(selected_tables_data) + mag_summary_df = get_table(tables.binning) if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -289,10 +290,10 @@ def mag_overview_length_boxplot_callback( ], ) def mag_overview_coverage_boxplot_callback( - selected_tables_data: Dict[str, str], cluster_col: str + selected_tables_data: SampleTables, cluster_col: str ) -> go.Figure: - table_name = selected_tables_data["binning"] - mag_summary_df = get_table(table_name) + tables = SampleTables.parse_raw(selected_tables_data) + mag_summary_df = get_table(tables.binning) if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -305,9 +306,11 @@ def mag_overview_coverage_boxplot_callback( Output("mag-summary-cluster-col-dropdown", "options"), [Input("selected-tables-store", "data")], ) -def mag_summary_cluster_col_dropdown_options_callback(selected_tables_data: Dict[str, str]): - refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") - refinement_df = get_table(refinement_table_name, index_col='contig') +def mag_summary_cluster_col_dropdown_options_callback( + selected_tables_data: SampleTables +): + tables = SampleTables.parse_raw(selected_tables_data) + refinement_df = get_table(tables.refinements, index_col="contig") return [ {"label": col.title(), "value": col} for col in refinement_df.columns @@ -323,15 +326,15 @@ def mag_summary_cluster_col_dropdown_options_callback(selected_tables_data: Dict ], ) def mag_summary_stats_datatable_callback( - selected_tables_data: Dict[str, str], cluster_col:str + selected_tables_data: SampleTables, cluster_col: str ): - binning_table_name = selected_tables_data["binning"] - bin_df = get_table(binning_table_name, index_col='contig') - refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") - refinement_df = get_table(refinement_table_name, index_col='contig').drop(columns="cluster") - bin_df = bin_df.join(refinement_df, how='right') - markers_table_name = selected_tables_data["markers"] - markers = get_table(markers_table_name, index_col="contig") + tables = SampleTables.parse_raw(selected_tables_data) + bin_df = get_table(tables.binning, index_col="contig") + refinement_df = get_table(tables.refinements, index_col="contig").drop( + columns="cluster" + ) + bin_df = bin_df.join(refinement_df, how="right") + markers = get_table(tables.markers, index_col="contig") if cluster_col not in bin_df.columns: num_expected_markers = markers.shape[1] length_weighted_coverage = np.average( @@ -408,13 +411,14 @@ def mag_summary_stats_datatable_callback( ], ) def mag_taxonomy_sankey_callback( - selected_tables_data: Dict[str,str], cluster_col: str, selected_mag: str + selected_tables_data: SampleTables, cluster_col: str, selected_mag: str ) -> go.Figure: - binning_table_name = selected_tables_data["binning"] - bin_df = get_table(binning_table_name, index_col='contig') - refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") - refinement_df = get_table(refinement_table_name, index_col='contig').drop(columns="cluster") - bin_df = bin_df.join(refinement_df, how='right') + tables = SampleTables.parse_raw(selected_tables_data) + bin_df = get_table(tables.binning, index_col="contig") + refinement_df = get_table(tables.refinements, index_col="contig").drop( + columns="cluster" + ) + bin_df = bin_df.join(refinement_df, how="right") if cluster_col not in bin_df.columns: raise PreventUpdate mag_df = bin_df.loc[bin_df[cluster_col].eq(selected_mag)] @@ -431,13 +435,14 @@ def mag_taxonomy_sankey_callback( ], ) def mag_summary_gc_content_boxplot_callback( - selected_tables_data: Dict[str,str], cluster_col: str, selected_mag: str + selected_tables_data: SampleTables, cluster_col: str, selected_mag: str ) -> go.Figure: - binning_table_name = selected_tables_data["binning"] - bin_df = get_table(binning_table_name, index_col='contig') - refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") - refinement_df = get_table(refinement_table_name, index_col='contig').drop(columns="cluster") - bin_df = bin_df.join(refinement_df, how='right') + tables = SampleTables.parse_raw(selected_tables_data) + bin_df = get_table(tables.binning, index_col="contig") + refinement_df = get_table(tables.refinements, index_col="contig").drop( + columns="cluster" + ) + bin_df = bin_df.join(refinement_df, how="right") if cluster_col not in bin_df.columns: raise PreventUpdate bin_df = bin_df.dropna(subset=[cluster_col]) @@ -457,21 +462,24 @@ def mag_summary_gc_content_boxplot_callback( ], ) def mag_metrics_callback( - selected_tables_data: Dict[str,str], cluster_col: str, selected_mag: str + selected_tables_data: SampleTables, cluster_col: str, selected_mag: str ) -> go.Figure: if not selected_mag: raise PreventUpdate - binning_table_name = selected_tables_data["binning"] - bin_df = get_table(binning_table_name, index_col='contig') - refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") - refinement_df = get_table(refinement_table_name, index_col='contig').drop(columns="cluster") - mag_summary_df = bin_df.join(refinement_df, how='right') + tables = SampleTables.parse_raw(selected_tables_data) + bin_df = get_table(tables.binning, index_col="contig") + refinement_df = get_table(tables.refinements, index_col="contig").drop( + columns="cluster" + ) + mag_summary_df = bin_df.join(refinement_df, how="right") if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) mag_df = mag_summary_df.loc[mag_summary_df[cluster_col].eq(selected_mag)] mag_df = mag_df.round(2) - fig = metric_barplot(df=mag_df, metrics=["completeness", "purity"], name=selected_mag) + fig = metric_barplot( + df=mag_df, metrics=["completeness", "purity"], name=selected_mag + ) return fig @@ -484,15 +492,16 @@ def mag_metrics_callback( ], ) def mag_summary_gc_content_boxplot_callback( - selected_tables_data: Dict[str,str], cluster_col: str, selected_mag: str + selected_tables_data: SampleTables, cluster_col: str, selected_mag: str ) -> go.Figure: if not selected_mag: - raise PreventUpdate - binning_table_name = selected_tables_data["binning"] - bin_df = get_table(binning_table_name, index_col='contig') - refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") - refinement_df = get_table(refinement_table_name, index_col='contig').drop(columns="cluster") - mag_summary_df = bin_df.join(refinement_df, how='right') + raise PreventUpdate + tables = SampleTables.parse_raw(selected_tables_data) + bin_df = get_table(tables.binning, index_col="contig") + refinement_df = get_table(tables.refinements, index_col="contig").drop( + columns="cluster" + ) + mag_summary_df = bin_df.join(refinement_df, how="right") if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -511,15 +520,16 @@ def mag_summary_gc_content_boxplot_callback( ], ) def mag_summary_gc_content_boxplot_callback( - selected_tables_data: Dict[str,str], cluster_col: str, selected_mag: str + selected_tables_data: SampleTables, cluster_col: str, selected_mag: str ) -> go.Figure: if not selected_mag: - raise PreventUpdate - binning_table_name = selected_tables_data["binning"] - bin_df = get_table(binning_table_name, index_col='contig') - refinement_table_name = selected_tables_data["binning"].replace("-binning", "-refinement") - refinement_df = get_table(refinement_table_name, index_col='contig').drop(columns="cluster") - mag_summary_df = bin_df.join(refinement_df, how='right') + raise PreventUpdate + tables = SampleTables.parse_raw(selected_tables_data) + bin_df = get_table(tables.binning, index_col="contig") + refinement_df = get_table(tables.refinements, index_col="contig").drop( + columns="cluster" + ) + mag_summary_df = bin_df.join(refinement_df, how="right") if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -537,10 +547,10 @@ def mag_summary_gc_content_boxplot_callback( ], ) def mag_selection_dropdown_options_callback( - selected_tables_data: Dict[str,str], cluster_col: str + selected_tables_data: SampleTables, cluster_col: str ) -> List[Dict[str, str]]: - table_name = selected_tables_data["binning"].replace("-binning", "-refinement") - df = get_table(table_name, index_col="contig") + tables = SampleTables.parse_raw(selected_tables_data) + df = get_table(tables.refinements, index_col="contig") if cluster_col not in df.columns: options = [] else: diff --git a/automappa/conf/__init__.py b/automappa/conf/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/automappa/conf/celeryconfig.py b/automappa/conf/celeryconfig.py new file mode 100644 index 00000000..c6d63de8 --- /dev/null +++ b/automappa/conf/celeryconfig.py @@ -0,0 +1,5 @@ +worker_send_task_events = True +task_send_sent_event = True +worker_prefetch_multiplier = 1 +worker_concurrency = 2 +task_track_started = True diff --git a/automappa/db.py b/automappa/db.py index 7786eb3d..d5227859 100644 --- a/automappa/db.py +++ b/automappa/db.py @@ -1,6 +1,12 @@ #!/usr/bin/env python from sqlalchemy import create_engine, MetaData +# from sqlalchemy import create_engine, MetaData, Table +# from sqlalchemy import inspect + +# User inspector for retrieving db info: +# Inspector: https://docs.sqlalchemy.org/en/14/core/reflection.html?highlight=inspector%20has_table#sqlalchemy.engine.reflection.Inspector +# has_table: https://docs.sqlalchemy.org/en/14/core/reflection.html?highlight=has_table#sqlalchemy.engine.reflection.Inspector.has_table from automappa.settings import db @@ -11,3 +17,7 @@ ) metadata = MetaData(bind=engine) + +# inspector = inspect(engine) +# user_table = Table('user', metadata) +# insp.reflect_table(user_table, None) \ No newline at end of file diff --git a/automappa/index.py b/automappa/index.py index 1315685f..e2b12ba3 100755 --- a/automappa/index.py +++ b/automappa/index.py @@ -12,6 +12,7 @@ from automappa import settings from automappa.apps import home, mag_refinement, mag_summary from automappa.app import app +from automappa.utils.models import SampleTables logging.basicConfig( format="[%(levelname)s] %(name)s: %(message)s", @@ -23,17 +24,20 @@ @app.callback( Output("tab-content", "children"), - [Input("tabs", "active_tab"), - Input("selected-tables-store", "data")] + [ + Input("tabs", "active_tab"), + Input("selected-tables-store", "data") + ], ) -def render_content(active_tab: str, selected_tables_data: Dict[str,str]) -> dbc.Container: +def render_content( + active_tab: str, selected_tables_data: SampleTables, +) -> dbc.Container: # Only alow user to navigate to mag refinement or summary if data is already uploaded if selected_tables_data is None: return home.layout - for data_table in ['markers', 'binning']: - if selected_tables_data[data_table] is None: - return home.layout - + tables = SampleTables.parse_raw(selected_tables_data) + if not tables.binning or not tables.markers: + return home.layout layouts = { "home": home.layout, "mag_refinement": mag_refinement.layout, @@ -133,7 +137,11 @@ def main(): # TODO: Replace cli inputs (as well as updating title once file is uploaded...) # app.title = f"Automappa: {sample_name}" app.title = "Automappa" - app.run_server(host=settings.server.host, port=settings.server.port, debug=settings.server.debug) + app.run_server( + host=settings.server.host, + port=settings.server.port, + debug=settings.server.debug, + ) if __name__ == "__main__": diff --git a/automappa/tasks.py b/automappa/tasks.py index d279e084..2d523477 100644 --- a/automappa/tasks.py +++ b/automappa/tasks.py @@ -1,34 +1,40 @@ #!/usr/bin/env python -import logging -import os -import glob +import tempfile +import random + +# import time +from typing import List import pandas as pd from geom_median.numpy import compute_geometric_median -from celery import Celery + +from celery import Celery, group +from celery.utils.log import get_task_logger from celery.result import AsyncResult -from autometa.common.kmers import normalize, embed, count +from Bio import SeqIO + +from autometa.common import kmers from automappa import settings -from automappa.utils.markers import ( - convert_marker_counts_to_marker_symbols, - get_contig_marker_counts, -) +from automappa.utils.markers import get_marker_symbols +from automappa.utils.serializers import get_metagenome_seqrecords, get_table,table_to_db queue = Celery( __name__, backend=settings.celery.backend_url, broker=settings.celery.broker_url ) +queue.config_from_object("automappa.conf.celeryconfig") -logging.basicConfig( - format="[%(levelname)s] %(name)s: %(message)s", - level=logging.DEBUG, -) -logger = logging.getLogger(__name__) +logger = get_task_logger(__name__) + +if settings.server.debug: + logger.debug( + f"celery config:\n{queue.conf.humanize(with_defaults=False, censored=True)}" + ) def get_job(job_id): @@ -39,40 +45,94 @@ def get_job(job_id): return AsyncResult(job_id, app=queue) -@queue.task -def get_marker_symbols(bin_df: pd.DataFrame, markers_df: pd.DataFrame) -> pd.DataFrame: - marker_counts = get_contig_marker_counts(bin_df, markers_df) - marker_symbols = convert_marker_counts_to_marker_symbols(marker_counts) - return marker_symbols - - -# TODO: Data loader -# TODO: Create 2d-scatterplot figure -# TODO: Marker symbols -# TODO: CheckM annotation -# TODO: kmer freq. analysis pipeline -# TODO: scatterplot 2-d embedding views - -@queue.task -def get_embedding( - assembly: str, norm_method: str = "am_clr", embed_method: str = "densmap" -): - counts = count( - assembly=assembly, - size=5, - out=None, - force=False, - verbose=True, - cpus=1, - ) - norm_df = normalize(counts, method=norm_method) - return embed(norm_df, method=embed_method, embed_dimensions=2) +@queue.task(bind=True) +def preprocess_marker_symbols(self, binning_table: str, markers_table: str) -> str: + bin_df = get_table(binning_table, index_col='contig') + markers_df = get_table(markers_table, index_col='contig') + marker_symbols_df = get_marker_symbols(bin_df, markers_df).set_index('contig') + marker_symbols_table = markers_table.replace('-markers', '-marker-symbols') + table_to_db(marker_symbols_df, marker_symbols_table, index=True) + return marker_symbols_table + + +# TODO: STRETCH GOALS +# Data loader [stretch...] +# CheckM annotation + +# TODO +# Create 2d-scatterplot figure +# Marker symbols table +# kmer freq. analysis pipeline +# scatterplot 2-d embedding views + + +@queue.task(bind=True) +def count_kmer(self, metagenome_table:str, size:int = 5, cpus:int = None) -> str: + records = get_metagenome_seqrecords(metagenome_table) + # Uncomment next line to speed-up debugging... + # FIXME: Comment out below: + # records = random.sample(records, k=1_000) + with tempfile.NamedTemporaryFile(mode="w") as tmp: + SeqIO.write(records, tmp.name, "fasta") + tmp.seek(0) + counts = kmers.count( + assembly=tmp.name, + size=size, + out=None, + force=False, + verbose=False, + cpus=cpus, + ) + logger.info("count finished") + # Strip sample name of -metagenome tag (will append the rest for kmer-pipeline) + counts_table = metagenome_table.replace("-metagenome", f"-{size}mers") + table_to_db(counts, name=counts_table, index=True) + return counts_table + +@queue.task(bind=True) +def normalize_kmer(self, counts_table: str, norm_method:str) -> str: + counts = get_table(counts_table, index_col='contig') + norm_df = kmers.normalize(counts, method=norm_method) + norm_table = f"{counts_table}-{norm_method}" + table_to_db(norm_df, name=norm_table, index=True) + return norm_table + + +@queue.task(bind=True) +def embed_kmer(self, norm_table:str, embed_method:str, n_jobs: int= 1) -> str: + norm_df = get_table(norm_table, index_col='contig') + # FIXME: Refactor renaming of embed_df.columns + prev_kmer_params = norm_table.split('-', 1)[-1] + embed_df = kmers.embed( + # TODO: (on autometa[2.0.4] release): norm_df, method=embed_method, embed_dimensions=2, n_jobs=n_jobs + norm_df, method=embed_method, embed_dimensions=2 + ).rename(columns={"x_1": f"{prev_kmer_params}-{embed_method}_x_1", "x_2": f"{prev_kmer_params}-{embed_method}_x_2"}) + embed_table = f"{norm_table}-{embed_method}" + table_to_db(embed_df, name=embed_table, index=True) + return embed_table + +@queue.task(bind=True) +def aggregate_embeddings(self, embed_tables:List[str], table_name:str) -> str: + df = pd.concat([get_table(embed_table, index_col='contig') for embed_table in embed_tables], axis=1) + table_to_db(df, table_name, index=True) + return table_name + +def preprocess_embeddings(metagenome_table: str, cpus:int=1, kmer_size: int = 5, norm_method:str ="am_clr", embed_methods: List[str]=["bhsne", "densmap", "trimap", "umap"]): + embeddings_table = metagenome_table.replace("-metagenome","-embeddings") + kmer_pipeline = count_kmer.s(metagenome_table, kmer_size, cpus) | normalize_kmer.s(norm_method) | group(embed_kmer.s(embed_method, cpus) for embed_method in embed_methods) | aggregate_embeddings.s(embeddings_table) + result = kmer_pipeline() + # while not result.ready(): + # time.sleep(1) + # with open('graph.dot', 'w') as fh: + # result.parent.parent.parent.graph.to_dot(fh) + logger.debug(result) + return result @queue.task -def get_clusters_geom_medians( - df: pd.DataFrame, cluster_col: str = "cluster", weight_col: str = "length" -) -> pd.DataFrame: +def preprocess_clusters_geom_medians( + binning_table: str, cluster_col: str = "cluster", weight_col: str = "length" +) -> str: """Compute each cluster's (`cluster_col`) geometric median weighted by contig length (`weight_col`) Parameters @@ -93,6 +153,7 @@ def get_clusters_geom_medians( `weighted` denotes the value that was used for weighting the cluster's geometric median (`weight_col`) """ + df = get_table(binning_table, index_col='contig') medians = [] for cluster, dff in df.groupby(cluster_col): points = dff[["x_1", "x_2"]].to_numpy() @@ -110,35 +171,17 @@ def get_clusters_geom_medians( } ) medians_df = pd.DataFrame(medians) - return medians_df + medians_table = binning_table.replace("-binning",f"{cluster_col}-gmedians") + table_to_db(medians_df, medians_table) + return medians_table @queue.task -def get_embedding_traces_df(df: pd.DataFrame) -> pd.DataFrame: +def get_embedding_traces_df(embeddings_table: str) -> pd.DataFrame: # 1. Compute all embeddings for assembly... # 2. groupby cluster # 3. Extract k-mer size, norm method, embed method - embedding_fpaths = glob.glob("data/nubbins/kmers/*5mers*am_clr.*2.tsv.gz") - embeddings = [] - for fp in embedding_fpaths: - df = pd.read_csv(fp, sep="\t", index_col="contig") - basename = os.path.basename(fp) - mers, norm_method, embed_method_dim, *__ = basename.split(".") - match = re.match("(\w+)(\d+)", embed_method_dim) - if match: - embed_method, embed_dim = match.groups() - df.rename( - columns={ - "x_1": f"{embed_method}_x_1", - "x_2": f"{embed_method}_x_2", - }, - inplace=True, - ) - embeddings.append(df) - embeddings_df = pd.concat(embeddings, axis=1) - - df = pd.read_csv("data/nubbins/nubbins.tsv", sep="\t") - main_df = df.drop(columns=["x_1", "x_2"]).set_index("contig").join(embeddings_df) + embed_traces = [] for embed_method in ["trimap", "densmap", "bhsne", "umap", "sksne"]: traces_df = get_scattergl_traces( @@ -151,17 +194,4 @@ def get_embedding_traces_df(df: pd.DataFrame) -> pd.DataFrame: if __name__ == "__main__": - hmmdb_dir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/hmms" - orfs_dir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/orfs" - outdir = "/Users/rees/Wisc/kwan/for_brian/complex_metagenomes/marker_annotater/test_data/hmmscan" - if not os.path.exists(outdir) or not os.path.isdir(outdir): - os.makedirs(outdir) - for seqfile in glob.glob(os.path.join(orfs_dir, "*.orfs.faa")): - hmmdb_filename = os.path.basename(seqfile).replace(".orfs.faa", ".hmm") - hmmdb = os.path.join(hmmdb_dir, hmmdb_filename) - if not os.path.exists(hmmdb): - continue - outfilename = os.path.basename(seqfile).replace(".faa", ".hmmscan.tsv") - out = os.path.join(outdir, outfilename) - hmmdb_formatter.s(hmmdb).apply_async() - scanner.s(seqfile, hmmdb, out).apply_async(countdown=2) + pass diff --git a/automappa/utils/figures.py b/automappa/utils/figures.py index de89e279..7b850e0e 100644 --- a/automappa/utils/figures.py +++ b/automappa/utils/figures.py @@ -113,7 +113,7 @@ def metric_barplot( raise PreventUpdate x = [metric.replace("_", " ").title() for metric in metrics] y = [df[metric].iat[0] for metric in metrics] - orientation = 'h' if horizontal else "v" + orientation = "h" if horizontal else "v" return go.Figure([go.Bar(x=x, y=y, orientation=orientation, name=name)]) @@ -221,7 +221,8 @@ def get_scattergl_traces( ) traces = [] metadata_cols = [col for col in metadata_cols if col in df.columns] - for color_col_name in df[color_by_col].fillna(fillna).unique(): + df = df.fillna(value={color_by_col:fillna}) + for color_col_name in df[color_by_col].unique(): dff = df.loc[df[color_by_col].eq(color_col_name)] customdata = dff[metadata_cols] if metadata_cols else [] trace = go.Scattergl( @@ -238,31 +239,7 @@ def get_scattergl_traces( return pd.DataFrame(traces).set_index(color_by_col) -def get_embedding_traces_df(df: pd.DataFrame) -> pd.DataFrame: - # 1. Compute all embeddings for assembly... - # 2. groupby cluster - # 3. Extract k-mer size, norm method, embed method - embedding_fpaths = glob.glob("data/nubbins/kmers/*5mers*am_clr.*2.tsv.gz") - embeddings = [] - for fp in embedding_fpaths: - df = pd.read_csv(fp, sep="\t", index_col="contig") - basename = os.path.basename(fp) - mers, norm_method, embed_method_dim, *__ = basename.split(".") - match = re.match("(\w+)(\d+)", embed_method_dim) - if match: - embed_method, embed_dim = match.groups() - df.rename( - columns={ - "x_1": f"{embed_method}_x_1", - "x_2": f"{embed_method}_x_2", - }, - inplace=True, - ) - embeddings.append(df) - embeddings_df = pd.concat(embeddings, axis=1) - - df = pd.read_csv("data/nubbins/nubbins.tsv", sep="\t") - main_df = df.drop(columns=["x_1", "x_2"]).set_index("contig").join(embeddings_df) +def get_embedding_traces_df(df:pd.DataFrame) -> pd.DataFrame: embed_traces = [] for embed_method in ["trimap", "densmap", "bhsne", "umap", "sksne"]: traces_df = get_scattergl_traces( @@ -324,12 +301,32 @@ def get_scatterplot_2d( def get_scatterplot_3d( - df, + df: pd.DataFrame, x_axis: str = "x_1", y_axis: str = "x_2", z_axis: str = "coverage", color_by_col: str = "cluster", ) -> go.Figure: + """Create go.Figure from `df` + + Parameters + ---------- + df : pd.DataFrame + index_col=[contig], cols=[`x_axis`, `y_axis`, `z_axis`] + x_axis : str, optional + _description_, by default "x_1" + y_axis : str, optional + _description_, by default "x_2" + z_axis : str, optional + _description_, by default "coverage" + color_by_col : str, optional + _description_, by default "cluster" + + Returns + ------- + go.Figure + _description_ + """ fig = go.Figure( layout=go.Layout( scene=dict( @@ -355,7 +352,7 @@ def get_scatterplot_3d( x=dff[x_axis], y=dff[y_axis], z=dff[z_axis], - text=dff.contig, + text=dff.index, mode="markers", marker={ "size": df.assign(normLen=marker_size_scaler)["normLen"], diff --git a/automappa/utils/markers.py b/automappa/utils/markers.py index ac4d394e..1ec5ef66 100644 --- a/automappa/utils/markers.py +++ b/automappa/utils/markers.py @@ -12,17 +12,32 @@ def get_cluster_marker_counts( def get_contig_marker_counts( - bin_df: pd.DataFrame, markers_df: pd.DataFrame, marker_count_range_end: int = 7 -) -> pd.DataFrame: - df = bin_df.join(markers_df).fillna(0).copy() - df = df[markers_df.columns.tolist()] + df: pd.DataFrame, marker_count_range_end: int = 7) -> pd.DataFrame: + """Retrieves marker counts pd.DataFrame + + Parameters + ---------- + bin_df : pd.DataFrame + _description_ + markers_df : pd.DataFrame + _description_ + marker_count_range_end : int, optional + _description_, by default 7 + + Returns + ------- + pd.DataFrame + _description_ + """ ## Get copy number marker counts dfs = [] marker_counts_range = list( range(marker_count_range_end + 1) ) # range(start=inclusive, end=exclusive) for marker_count in marker_counts_range: - # Check if last in the list of marker_counts_range to apply df.ge(...) instead of df.eq(...) + # Last count is regex str: '\d+' + # To apply df.ge(...) instead of df.eq(...) + # Check if last in the list of marker_counts_range if marker_count + 1 == len(marker_counts_range): marker_count_contig_idx = df.loc[ df.sum(axis=1).ge(marker_count) @@ -36,7 +51,7 @@ def get_contig_marker_counts( count_df = pd.DataFrame(marker_count_contig_idx) count_df["marker_count"] = marker_count dfs.append(count_df) - return pd.concat(dfs).set_index("contig") + return pd.concat(dfs) def convert_marker_counts_to_marker_symbols(df: pd.DataFrame) -> pd.DataFrame: @@ -65,6 +80,8 @@ def convert_marker_counts_to_marker_symbols(df: pd.DataFrame) -> pd.DataFrame: def get_marker_symbols(bin_df: pd.DataFrame, markers_df: pd.DataFrame) -> pd.DataFrame: - marker_counts = get_contig_marker_counts(bin_df, markers_df) + df = bin_df.join(markers_df).fillna(0).copy() + df = df[markers_df.columns.tolist()] + marker_counts = get_contig_marker_counts(df) marker_symbols = convert_marker_counts_to_marker_symbols(marker_counts) return marker_symbols diff --git a/automappa/utils/models.py b/automappa/utils/models.py new file mode 100644 index 00000000..61848972 --- /dev/null +++ b/automappa/utils/models.py @@ -0,0 +1,224 @@ +from pydantic import BaseModel, Field, PydanticValueError, create_model +from pydantic.fields import ModelField +from typing import List, Literal, Optional +# from cached_property import cached_property_with_ttl +from celery.result import AsyncResult + +from automappa.db import engine,metadata +from automappa.utils.serializers import get_table + +class RestrictedAlphabetStr(str): + @classmethod + def __get_validators__(cls): + yield cls.validate + + @classmethod + def validate(cls, value, field: ModelField): + alphabet = field.field_info.extra['alphabet'] + if any(c not in alphabet for c in value): + raise ValueError(f'{value!r} is not restricted to {alphabet!r}') + return cls(value) + + @classmethod + def __modify_schema__(cls, field_schema, field: Optional[ModelField]): + if field: + alphabet = field.field_info.extra['alphabet'] + field_schema['examples'] = [c * 4 for c in alphabet] + +class MetagenomeAnnotations(BaseModel): + contig: str + binning: Optional[str] + refinements: Optional[str] + markers: Optional[str] + # marker_symbols: Union[AsyncResult,str] + metagenome: Optional[str] + + +class NotInDatabaseError(PydanticValueError): + code = 'table_name_not_in_db' + tables = metadata.tables.keys() + msg_template = f'value "TEMPLATE" is not in {tables}' + msg_template = msg_template.replace("TEMPLATE", "{table_name}") + + +class AnnotationTable(BaseModel): + id: str + index_col: Optional[str] = "contig" + + # @validator('id', pre=True) + # @validator('id') + # def table_must_exist_in_database(cls, v): + # if v not in metadata.tables.keys(): + # # if not engine.has_table(v): + # raise NotInDatabaseError(table_name=v) + # return v + + # @validator('index_col') + # def index_col_in_table(cls, v, values): + # table_id = values.get('id') + # table_cols = get_table(table_id).columns + # if v not in table_cols: + # raise ValueError(f'{v} not in {table_id} columns {table_cols}') + # return v + + # class Config: + # keep_untouched=(cached_property,) + + + @property + def sample(self): + return self.id.split('-')[0] + + @property + def name(self): + return '-'.join(self.id.split('-')[1:]) + + @property + def table(self): + return get_table(self.id, index_col=self.index_col) + + @property + def exists(self): + return engine.has_table(self.id) + + @property + def columns(self): + return self.table.columns + +# class ResultTable(AnnotationTable): +# task: Optional[AsyncResult] + +# @cached_property_with_ttl(60) # cache invalidates after 60 sec +# def table(self): +# return get_table(self.id, index_col=self.index_col) + +# class Config: +# arbitrary_types_allowed = True +# smart_union = True +class KmerTable(BaseModel): + assembly: AnnotationTable + size: Literal[3, 4, 5] = 5 + norm_method: Literal["am_clr", "ilr", "clr"] = "am_clr" + embed_dims: Optional[int] = 2 + embed_method: Literal["bhsne", "sksne", "umap", "densmap", "trimap"] = "bhsne" + + @property + def counts(self) -> AnnotationTable: + return AnnotationTable(id=self.assembly.id.replace("-metagenome", f"-{self.size}mers")) + + @property + def norm_freqs(self) -> AnnotationTable: + return AnnotationTable(id=self.assembly.id.replace("-metagenome", f"-{self.size}mers-{self.norm_method}")) + + @property + def embedding(self) -> AnnotationTable: + return AnnotationTable(id=self.assembly.id.replace("-metagenome", f"-{self.size}mers-{self.norm_method}-{self.embed_method}")) + +class SampleTables(BaseModel): + binning: Optional[AnnotationTable] + markers: Optional[AnnotationTable] + metagenome: Optional[AnnotationTable] + # The following are created after user upload + # via: + # db ingestion (see serializers.py) + # celery tasks (see tasks.py) + refinements: Optional[AnnotationTable] + # geom_medians: Optional[str] + + @property + def kmers(self) -> List[KmerTable]: + settings = [] + for size in [3,4,5]: + for norm_method in ["am_clr"]: + for embed_method in ["bhsne", "densmap", "umap", "sksne", "trimap"]: + settings.append(KmerTable( + assembly=self.metagenome, + size=size, + norm_method=norm_method, + embed_dims=2, + embed_method=embed_method, + )) + return settings + + @property + def embeddings(self) -> AnnotationTable: + return AnnotationTable(id=self.metagenome.id.replace("-metagenome","-embeddings")) + + @property + def marker_symbols(self) -> AnnotationTable: + return AnnotationTable(id=self.markers.id.replace('-markers','-marker-symbols')) + + # embeddings: Union[AsyncResult,str] + + # validators + # @validator('*') + # def table_must_exist_in_database(cls, v): + # if not engine.has_table(v): + # raise NotInDatabaseError(table_name=v) + # return v + + # Generic Class Methods + # e.g. tables.binning.get_table() + # or tables.binning.exists() + # or tables.binning.tasks() + # etc. + # Need generic methods (listed above) to apply from namespace + + class Config: + arbitrary_types_allowed = True + smart_union = True + + + +MarkerAnnotations = create_model( + 'MarkerAnnotations', + apple='russet', + banana='yellow', + __base__=MetagenomeAnnotations, +) + +class TaxonomyAnnotations(MetagenomeAnnotations): + taxid: int + superkingdom: Optional[str] + phylum: Optional[str] + klass: Optional[str] + order: Optional[str] + family: Optional[str] + genus: Optional[str] + species: Optional[str] + +class CoverageAnnotations(MetagenomeAnnotations): + coverage: float + +class KmerAnnotations(MetagenomeAnnotations): + x_1: float + x_2: float + kmer_size: Optional[int] + norm_method: Optional[str] + embed_method: Optional[str] + +class Kmer(BaseModel): + value: RestrictedAlphabetStr = Field(alphabet='ATCG') + +class KmerCounts(MetagenomeAnnotations): + # kmers: Field(..., regex = 'ATCG') + kmers: List[Kmer] + +class BinningAnnotations(MetagenomeAnnotations): + cluster: str + completeness: Optional[float] + purity: Optional[float] + gc_stddev: Optional[float] + coverage_stddev: Optional[float] + + +class MetagenomeModel(BaseModel): + # Json + contig: str + gc_content: Optional[float] + length: Optional[int] + markers: Optional[MarkerAnnotations] + kmers: Optional[KmerAnnotations] + binning: Optional[BinningAnnotations] + coverage: Optional[CoverageAnnotations] + taxonomy: Optional[TaxonomyAnnotations] diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py index 73e98f66..9ff875ac 100644 --- a/automappa/utils/serializers.py +++ b/automappa/utils/serializers.py @@ -7,13 +7,16 @@ import uuid import pandas as pd -from automappa.db import engine, metadata -from automappa.settings import server +from Bio.SeqIO import SeqRecord +from Bio.Seq import Seq from autometa.common.markers import load as load_markers from autometa.common.utilities import calc_checksum from autometa.common.metagenome import Metagenome +from automappa.db import engine, metadata +from automappa.settings import server + logging.basicConfig( format="[%(levelname)s] %(name)s: %(message)s", level=logging.DEBUG, @@ -115,14 +118,16 @@ def store_binning_main(filepath: Path, if_exists: str = "replace") -> str: else: df["cluster"].fillna("unclustered", inplace=True) - df_cols = [ + refine_cols = [ col for col in df.columns if "refinement_" in col or "cluster" in col or "contig" in col ] refinement_table_name = table_name.replace("-binning", "-refinement") - df[df_cols].to_sql(refinement_table_name, engine, if_exists=if_exists, index=False) - logger.debug(f"Saved {df[df_cols].info()} refinements to postgres table: {refinement_table_name}") + df[refine_cols].to_sql(refinement_table_name, engine, if_exists=if_exists, index=False) + logger.debug( + f"Saved refinements (shape={df[refine_cols].shape}) to postgres table: {refinement_table_name}" + ) return table_name @@ -206,17 +211,27 @@ def store_metagenome(filepath: Path, if_exists: str = "replace") -> str: return table_name -def table_to_db(df:pd.DataFrame, name:str, if_exists: str = "replace", index:bool = False)->None: +def get_metagenome_seqrecords(table_name) -> List[SeqRecord]: + df = get_table(table_name) + return [ + SeqRecord(seq=Seq(record[1]), id=record[0], name=record[0]) + for record in df.to_records(index=False) + ] + + +def table_to_db( + df: pd.DataFrame, name: str, if_exists: str = "replace", index: bool = False +) -> None: """Write `df` to `table_name` in database. Parameters ---------- df : pd.DataFrame Dataframe of data to write - table_name : str + name : str Name of data table to store in database if_exists : str, optional - What to do if `table_name` exists. + What to do if `name` exists. Choices include: 'fail', 'replace', 'append', by default "replace" index : bool, optional Whether to write the index to the database, by default False diff --git a/docker-compose.yml b/docker-compose.yml index 7ef1f65d..f1cc9844 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -82,7 +82,7 @@ services: restart: always mem_limit: 4GB # Not sure which is more appropriate for production :shrug: - # This command allows live-reloading for dev + # The current command allows live-reloading for dev # command: automappa command: python -m automappa.index volumes: @@ -92,9 +92,7 @@ services: - 8050:8050 depends_on: - postgres - - rabbitmq - celery - - redis volumes: postgres-data: From 977dbbf6dca8cc62f974eb1c6a2f48fc25f2ddb2 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Thu, 5 May 2022 11:56:16 -0400 Subject: [PATCH 25/37] :art: Apply black formatting --- TODO.md | 4 +- automappa/apps/home.py | 70 +++++++++++++-------- automappa/apps/mag_refinement.py | 62 +++++++++---------- automappa/apps/mag_summary.py | 2 +- automappa/db.py | 3 +- automappa/index.py | 8 +-- automappa/tasks.py | 70 ++++++++++++++------- automappa/utils/figures.py | 4 +- automappa/utils/markers.py | 5 +- automappa/utils/models.py | 103 +++++++++++++++++++------------ automappa/utils/serializers.py | 4 +- 11 files changed, 205 insertions(+), 130 deletions(-) diff --git a/TODO.md b/TODO.md index 518b3255..02ed9d19 100644 --- a/TODO.md +++ b/TODO.md @@ -9,7 +9,7 @@ - [x] (`home.py`) binning, markers, metagenome dropdowns for selecting data available in database - [x] (`home.py`) dropdown values correspond to `table_id` and are sent to `mag_refinement.py` callbacks, replacing `pd.read_json(...)` - [x] (`home.py`/`index.py`) dropdowns should have a "NA" or placeholder disabling navigation to other layouts when data for the particular filetype is unavailable -7. [ ] :art::bug: Finish addition of other embeddings within 2D scatterplot in `mag_refinement.py` +7. [x] :art::bug: Finish addition of other embeddings within 2D scatterplot in `mag_refinement.py` 8. [x] :carrot::racehorse: Add celery task-queue 9. [x] :carrot::racehorse: Add celery-monitoring services 10. [ ] :carrot::racehorse: Add uploaded data ingestion to task-queue @@ -20,7 +20,7 @@ 15. [x] :whale::elephant::sunflower: Grafana provision from within `docker-compose.yml` 16. [ ] :art::fire: Refactor `store_binning_main(...)` s.t. refinement-data is stored in a separate async process. 17. [ ] :art::carrot: Add selections/dropdowns/progress updates for metagenome embeddings -18. [ ] :art: Add embeddings selection dropdown in settings for 2d scatterplot axes (place task queueing from these selections) +18. [x] :art: Add embeddings selection dropdown in settings for 2d scatterplot axes (place task queueing from these selections) -------------------------------------------------------------------------------------------------- diff --git a/automappa/apps/home.py b/automappa/apps/home.py index 0cb2e4c0..5bb81796 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -12,7 +12,11 @@ import dash_uploader as du from automappa.app import app -from automappa.tasks import preprocess_clusters_geom_medians, preprocess_embeddings, preprocess_marker_symbols +from automappa.tasks import ( + preprocess_clusters_geom_medians, + preprocess_embeddings, + preprocess_marker_symbols, +) from automappa.utils.models import SampleTables from automappa.utils.serializers import ( get_uploaded_files_table, @@ -519,7 +523,7 @@ def toggle_modal(n_open, n_close, is_open): [ Input("binning-select", "value"), Input("markers-select", "value"), - Input("metagenome-select", "value") + Input("metagenome-select", "value"), ], ) def refine_mags_button_active_callback(binning_value, markers_value, metagenome_value): @@ -528,6 +532,7 @@ def refine_mags_button_active_callback(binning_value, markers_value, metagenome_ else: return False + @app.callback( Output("selected-tables-store", "data"), [ @@ -544,11 +549,13 @@ def on_refine_mags_button_click( raise PreventUpdate tables_dict = {} if binning_select_value is not None and markers_select_value is not None: - marker_symbols_task = preprocess_marker_symbols.delay(binning_select_value, markers_select_value) + marker_symbols_task = preprocess_marker_symbols.delay( + binning_select_value, markers_select_value + ) # logger.debug(f"{type(marker_symbols_task)} {marker_symbols_task}") # tables_dict["marker_symbols"] = markers_select_value.replace("-markers","-marker-symbols") if metagenome_select_value is not None: - tables_dict["metagenome"] = {"id":metagenome_select_value} + tables_dict["metagenome"] = {"id": metagenome_select_value} embeddings_task = preprocess_embeddings( metagenome_table=metagenome_select_value, norm_method="am_clr", @@ -557,16 +564,22 @@ def on_refine_mags_button_click( # logger.debug(f"{type(embeddings_task)} {embeddings_task}") # tables_dict["embeddings"] = metagenome_select_value.replace("-metagenome","-embeddings") if binning_select_value is not None: - tables_dict.update({ - "binning": {"id":binning_select_value}, - "refinements": {"id": binning_select_value.replace("-binning","-refinement")} - }) + tables_dict.update( + { + "binning": {"id": binning_select_value}, + "refinements": { + "id": binning_select_value.replace("-binning", "-refinement") + }, + } + ) cluster_col = "cluster" - clusters_geom_medians_task = preprocess_clusters_geom_medians.delay(binning_select_value, cluster_col) + clusters_geom_medians_task = preprocess_clusters_geom_medians.delay( + binning_select_value, cluster_col + ) # logger.debug(f"{type(clusters_geom_medians_task)} {clusters_geom_medians_task}") # tables_dict["geom_medians"] = binning_select_value.replace("-binning",f"{cluster_col}-gmedians") if markers_select_value is not None: - tables_dict["markers"] = {"id":markers_select_value} + tables_dict["markers"] = {"id": markers_select_value} logger.debug(tables_dict) return SampleTables(**tables_dict).json() @@ -580,7 +593,9 @@ def on_refine_mags_button_click( State("selected-tables-store", "data"), ) def selected_tables_datatable_children( - selected_tables_store_data: SampleTables, active_tab: str, new_selected_tables_store_data: SampleTables + selected_tables_store_data: SampleTables, + active_tab: str, + new_selected_tables_store_data: SampleTables, ): # Why Input("tabs", "active_tab"): Navigating back to home tab triggers rendering of table from store if selected_tables_store_data is None: @@ -588,24 +603,24 @@ def selected_tables_datatable_children( samples = SampleTables.parse_raw(selected_tables_store_data) if new_selected_tables_store_data is not None: new_tables = SampleTables.parse_raw(new_selected_tables_store_data) - if new_tables != samples: + if new_tables != samples: tables_dict = samples.dict() tables_dict.update(new_tables.dict()) samples = SampleTables.parse_obj(tables_dict) has_table = False - for __,table_id in samples: + for __, table_id in samples: if table_id: has_table = True break - + if not has_table: raise PreventUpdate return DataTable( data=[ {"filetype": sample, "table_id": table.id} - for sample,table in samples + for sample, table in samples if sample not in {"kmers"} ], columns=[ @@ -630,6 +645,7 @@ def refine_mags_button_active_callback(metagenome_value): # from automappa.utils.serializers import get_table # embed_df = get_table(embed_table_name, index_col='contig') + @app.callback( Output("embedding-tasks", "children"), Input("kmer-embed-tasks-button", "n_clicks"), @@ -647,19 +663,23 @@ def on_compute_metagenome_kmer_embedding(btn_clicks: int, metagenome_select_valu ) embed_table_name = metagenome_select_value.replace("-metagenome", "-embeddings") # TODO: Should create a polling or dcc.Interval(...) to construct this table for monitoring tasks status - df = pd.DataFrame([{ - "task_name": task.name, - # "started": task.track_started, - "state": task.state, - "task_id": task.id, - "norm_method": norm_method, - "embed_methods": ','.join(embed_methods), - "embed-table-name": embed_table_name, - }]) + df = pd.DataFrame( + [ + { + "task_name": task.name, + # "started": task.track_started, + "state": task.state, + "task_id": task.id, + "norm_method": norm_method, + "embed_methods": ",".join(embed_methods), + "embed-table-name": embed_table_name, + } + ] + ) # logger.debug(df) return DataTable( data=df.to_dict("records"), columns=[{"id": col, "name": col, "editable": False} for col in df.columns], persistence=True, - persistence_type='session', + persistence_type="session", ) diff --git a/automappa/apps/mag_refinement.py b/automappa/apps/mag_refinement.py index dc10c65b..7ebef09b 100644 --- a/automappa/apps/mag_refinement.py +++ b/automappa/apps/mag_refinement.py @@ -452,64 +452,57 @@ def toggle_offcanvas(n1: int, is_open: bool) -> bool: @app.callback( - Output("color-by-column", "options"), - Input("selected-tables-store", "data") + Output("color-by-column", "options"), Input("selected-tables-store", "data") ) def color_by_column_options_callback(selected_tables_data: SampleTables): sample = SampleTables.parse_raw(selected_tables_data) df = sample.binning.table return [ {"label": col.title().replace("_", " "), "value": col} - for col in df.select_dtypes('object').columns + for col in df.select_dtypes("object").columns ] -@app.callback( - Output("x-axis-2d", "options"), - Input("selected-tables-store", "data") -) +@app.callback(Output("x-axis-2d", "options"), Input("selected-tables-store", "data")) def x_axis_2d_options_callback(selected_tables_data: SampleTables): sample = SampleTables.parse_raw(selected_tables_data) binning_df = sample.binning.table binning_cols = [ - {"label": col.title().replace("_", " "), "value": col, 'disabled': False} + {"label": col.title().replace("_", " "), "value": col, "disabled": False} for col in binning_df.select_dtypes({"float64", "int64"}).columns if col not in {"completeness", "purity", "taxid"} ] kmer_cols = [ { - "label": f"{kmer.embedding.name.replace('-',' ')}_x_1", - "value": f"{kmer.embedding.name}_x_1", - 'disabled': not kmer.embedding.exists, + "label": f"{kmer.embedding.name.replace('-',' ')}_x_1", + "value": f"{kmer.embedding.name}_x_1", + "disabled": not kmer.embedding.exists, } for kmer in sample.kmers ] return binning_cols + kmer_cols -@app.callback( - Output("y-axis-2d", "options"), - Input("selected-tables-store", "data") -) + +@app.callback(Output("y-axis-2d", "options"), Input("selected-tables-store", "data")) def y_axis_2d_options_callback(selected_tables_data: Json[SampleTables]): sample = SampleTables.parse_raw(selected_tables_data) binning_df = sample.binning.table binning_cols = [ - {"label": col.title().replace("_", " "), "value": col, 'disabled': False} + {"label": col.title().replace("_", " "), "value": col, "disabled": False} for col in binning_df.select_dtypes({"float64", "int64"}).columns if col not in {"completeness", "purity", "taxid"} ] kmer_cols = [ { - "label": f"{kmer.embedding.name.replace('-',' ')}_x_2", - "value": f"{kmer.embedding.name}_x_2", - 'disabled': not kmer.embedding.exists, + "label": f"{kmer.embedding.name.replace('-',' ')}_x_2", + "value": f"{kmer.embedding.name}_x_2", + "disabled": not kmer.embedding.exists, } for kmer in sample.kmers ] return binning_cols + kmer_cols - @app.callback( Output("mag-metrics-datatable", "children"), [ @@ -654,22 +647,25 @@ def scatterplot_2d_figure_callback( if "_x_1" in xaxis_column or "_x_2" in yaxis_column: if sample.embeddings.exists: embedding_df = sample.embeddings.table - bin_df = bin_df.join(embedding_df, how='left') + bin_df = bin_df.join(embedding_df, how="left") else: for kmer_table in sample.kmers: - if kmer_table.embedding.name == xaxis_column and kmer_table.embedding.name == yaxis_column: - bin_df = bin_df.join(kmer_table.embedding.table, how='left') + if ( + kmer_table.embedding.name == xaxis_column + and kmer_table.embedding.name == yaxis_column + ): + bin_df = bin_df.join(kmer_table.embedding.table, how="left") break fillnas = { - "cluster":"unclustered", - "superkingdom":"unclassified", - "phylum":"unclassified", - "class":"unclassified", - "order":"unclassified", - "family":"unclassified", - "genus":"unclassified", - "species":"unclassified", + "cluster": "unclustered", + "superkingdom": "unclassified", + "phylum": "unclassified", + "class": "unclassified", + "order": "unclassified", + "family": "unclassified", + "genus": "unclassified", + "species": "unclassified", } fillna = fillnas.get(color_by_col, "unclustered") fig = get_scatterplot_2d( @@ -860,7 +856,9 @@ def download_refinements( if not n_clicks: raise PreventUpdate sample = SampleTables.parse_raw(selected_tables_data) - return send_data_frame(sample.refinements.table.to_csv, "refinements.csv", index=False) + return send_data_frame( + sample.refinements.table.to_csv, "refinements.csv", index=False + ) @app.callback( diff --git a/automappa/apps/mag_summary.py b/automappa/apps/mag_summary.py index b271bbea..a1af5fd5 100644 --- a/automappa/apps/mag_summary.py +++ b/automappa/apps/mag_summary.py @@ -307,7 +307,7 @@ def mag_overview_coverage_boxplot_callback( [Input("selected-tables-store", "data")], ) def mag_summary_cluster_col_dropdown_options_callback( - selected_tables_data: SampleTables + selected_tables_data: SampleTables, ): tables = SampleTables.parse_raw(selected_tables_data) refinement_df = get_table(tables.refinements, index_col="contig") diff --git a/automappa/db.py b/automappa/db.py index d5227859..26f8d9bf 100644 --- a/automappa/db.py +++ b/automappa/db.py @@ -1,6 +1,7 @@ #!/usr/bin/env python from sqlalchemy import create_engine, MetaData + # from sqlalchemy import create_engine, MetaData, Table # from sqlalchemy import inspect @@ -20,4 +21,4 @@ # inspector = inspect(engine) # user_table = Table('user', metadata) -# insp.reflect_table(user_table, None) \ No newline at end of file +# insp.reflect_table(user_table, None) diff --git a/automappa/index.py b/automappa/index.py index e2b12ba3..42c91274 100755 --- a/automappa/index.py +++ b/automappa/index.py @@ -24,13 +24,11 @@ @app.callback( Output("tab-content", "children"), - [ - Input("tabs", "active_tab"), - Input("selected-tables-store", "data") - ], + [Input("tabs", "active_tab"), Input("selected-tables-store", "data")], ) def render_content( - active_tab: str, selected_tables_data: SampleTables, + active_tab: str, + selected_tables_data: SampleTables, ) -> dbc.Container: # Only alow user to navigate to mag refinement or summary if data is already uploaded if selected_tables_data is None: diff --git a/automappa/tasks.py b/automappa/tasks.py index 2d523477..ca3f2d87 100644 --- a/automappa/tasks.py +++ b/automappa/tasks.py @@ -21,7 +21,11 @@ from automappa import settings from automappa.utils.markers import get_marker_symbols -from automappa.utils.serializers import get_metagenome_seqrecords, get_table,table_to_db +from automappa.utils.serializers import ( + get_metagenome_seqrecords, + get_table, + table_to_db, +) queue = Celery( @@ -47,10 +51,10 @@ def get_job(job_id): @queue.task(bind=True) def preprocess_marker_symbols(self, binning_table: str, markers_table: str) -> str: - bin_df = get_table(binning_table, index_col='contig') - markers_df = get_table(markers_table, index_col='contig') - marker_symbols_df = get_marker_symbols(bin_df, markers_df).set_index('contig') - marker_symbols_table = markers_table.replace('-markers', '-marker-symbols') + bin_df = get_table(binning_table, index_col="contig") + markers_df = get_table(markers_table, index_col="contig") + marker_symbols_df = get_marker_symbols(bin_df, markers_df).set_index("contig") + marker_symbols_table = markers_table.replace("-markers", "-marker-symbols") table_to_db(marker_symbols_df, marker_symbols_table, index=True) return marker_symbols_table @@ -67,7 +71,7 @@ def preprocess_marker_symbols(self, binning_table: str, markers_table: str) -> s @queue.task(bind=True) -def count_kmer(self, metagenome_table:str, size:int = 5, cpus:int = None) -> str: +def count_kmer(self, metagenome_table: str, size: int = 5, cpus: int = None) -> str: records = get_metagenome_seqrecords(metagenome_table) # Uncomment next line to speed-up debugging... # FIXME: Comment out below: @@ -89,9 +93,10 @@ def count_kmer(self, metagenome_table:str, size:int = 5, cpus:int = None) -> str table_to_db(counts, name=counts_table, index=True) return counts_table + @queue.task(bind=True) -def normalize_kmer(self, counts_table: str, norm_method:str) -> str: - counts = get_table(counts_table, index_col='contig') +def normalize_kmer(self, counts_table: str, norm_method: str) -> str: + counts = get_table(counts_table, index_col="contig") norm_df = kmers.normalize(counts, method=norm_method) norm_table = f"{counts_table}-{norm_method}" table_to_db(norm_df, name=norm_table, index=True) @@ -99,27 +104,50 @@ def normalize_kmer(self, counts_table: str, norm_method:str) -> str: @queue.task(bind=True) -def embed_kmer(self, norm_table:str, embed_method:str, n_jobs: int= 1) -> str: - norm_df = get_table(norm_table, index_col='contig') +def embed_kmer(self, norm_table: str, embed_method: str, n_jobs: int = 1) -> str: + norm_df = get_table(norm_table, index_col="contig") # FIXME: Refactor renaming of embed_df.columns - prev_kmer_params = norm_table.split('-', 1)[-1] + prev_kmer_params = norm_table.split("-", 1)[-1] embed_df = kmers.embed( # TODO: (on autometa[2.0.4] release): norm_df, method=embed_method, embed_dimensions=2, n_jobs=n_jobs - norm_df, method=embed_method, embed_dimensions=2 - ).rename(columns={"x_1": f"{prev_kmer_params}-{embed_method}_x_1", "x_2": f"{prev_kmer_params}-{embed_method}_x_2"}) + norm_df, + method=embed_method, + embed_dimensions=2, + ).rename( + columns={ + "x_1": f"{prev_kmer_params}-{embed_method}_x_1", + "x_2": f"{prev_kmer_params}-{embed_method}_x_2", + } + ) embed_table = f"{norm_table}-{embed_method}" table_to_db(embed_df, name=embed_table, index=True) return embed_table + @queue.task(bind=True) -def aggregate_embeddings(self, embed_tables:List[str], table_name:str) -> str: - df = pd.concat([get_table(embed_table, index_col='contig') for embed_table in embed_tables], axis=1) +def aggregate_embeddings(self, embed_tables: List[str], table_name: str) -> str: + df = pd.concat( + [get_table(embed_table, index_col="contig") for embed_table in embed_tables], + axis=1, + ) table_to_db(df, table_name, index=True) return table_name -def preprocess_embeddings(metagenome_table: str, cpus:int=1, kmer_size: int = 5, norm_method:str ="am_clr", embed_methods: List[str]=["bhsne", "densmap", "trimap", "umap"]): - embeddings_table = metagenome_table.replace("-metagenome","-embeddings") - kmer_pipeline = count_kmer.s(metagenome_table, kmer_size, cpus) | normalize_kmer.s(norm_method) | group(embed_kmer.s(embed_method, cpus) for embed_method in embed_methods) | aggregate_embeddings.s(embeddings_table) + +def preprocess_embeddings( + metagenome_table: str, + cpus: int = 1, + kmer_size: int = 5, + norm_method: str = "am_clr", + embed_methods: List[str] = ["bhsne", "densmap", "trimap", "umap"], +): + embeddings_table = metagenome_table.replace("-metagenome", "-embeddings") + kmer_pipeline = ( + count_kmer.s(metagenome_table, kmer_size, cpus) + | normalize_kmer.s(norm_method) + | group(embed_kmer.s(embed_method, cpus) for embed_method in embed_methods) + | aggregate_embeddings.s(embeddings_table) + ) result = kmer_pipeline() # while not result.ready(): # time.sleep(1) @@ -153,7 +181,7 @@ def preprocess_clusters_geom_medians( `weighted` denotes the value that was used for weighting the cluster's geometric median (`weight_col`) """ - df = get_table(binning_table, index_col='contig') + df = get_table(binning_table, index_col="contig") medians = [] for cluster, dff in df.groupby(cluster_col): points = dff[["x_1", "x_2"]].to_numpy() @@ -171,7 +199,7 @@ def preprocess_clusters_geom_medians( } ) medians_df = pd.DataFrame(medians) - medians_table = binning_table.replace("-binning",f"{cluster_col}-gmedians") + medians_table = binning_table.replace("-binning", f"{cluster_col}-gmedians") table_to_db(medians_df, medians_table) return medians_table @@ -181,7 +209,7 @@ def get_embedding_traces_df(embeddings_table: str) -> pd.DataFrame: # 1. Compute all embeddings for assembly... # 2. groupby cluster # 3. Extract k-mer size, norm method, embed method - + embed_traces = [] for embed_method in ["trimap", "densmap", "bhsne", "umap", "sksne"]: traces_df = get_scattergl_traces( diff --git a/automappa/utils/figures.py b/automappa/utils/figures.py index 7b850e0e..5627b9f5 100644 --- a/automappa/utils/figures.py +++ b/automappa/utils/figures.py @@ -221,7 +221,7 @@ def get_scattergl_traces( ) traces = [] metadata_cols = [col for col in metadata_cols if col in df.columns] - df = df.fillna(value={color_by_col:fillna}) + df = df.fillna(value={color_by_col: fillna}) for color_col_name in df[color_by_col].unique(): dff = df.loc[df[color_by_col].eq(color_col_name)] customdata = dff[metadata_cols] if metadata_cols else [] @@ -239,7 +239,7 @@ def get_scattergl_traces( return pd.DataFrame(traces).set_index(color_by_col) -def get_embedding_traces_df(df:pd.DataFrame) -> pd.DataFrame: +def get_embedding_traces_df(df: pd.DataFrame) -> pd.DataFrame: embed_traces = [] for embed_method in ["trimap", "densmap", "bhsne", "umap", "sksne"]: traces_df = get_scattergl_traces( diff --git a/automappa/utils/markers.py b/automappa/utils/markers.py index 1ec5ef66..bbadbab1 100644 --- a/automappa/utils/markers.py +++ b/automappa/utils/markers.py @@ -12,8 +12,9 @@ def get_cluster_marker_counts( def get_contig_marker_counts( - df: pd.DataFrame, marker_count_range_end: int = 7) -> pd.DataFrame: - """Retrieves marker counts pd.DataFrame + df: pd.DataFrame, marker_count_range_end: int = 7 +) -> pd.DataFrame: + """Retrieves marker counts pd.DataFrame Parameters ---------- diff --git a/automappa/utils/models.py b/automappa/utils/models.py index 61848972..d1d49a69 100644 --- a/automappa/utils/models.py +++ b/automappa/utils/models.py @@ -1,12 +1,14 @@ from pydantic import BaseModel, Field, PydanticValueError, create_model from pydantic.fields import ModelField from typing import List, Literal, Optional + # from cached_property import cached_property_with_ttl from celery.result import AsyncResult -from automappa.db import engine,metadata +from automappa.db import engine, metadata from automappa.utils.serializers import get_table + class RestrictedAlphabetStr(str): @classmethod def __get_validators__(cls): @@ -14,16 +16,17 @@ def __get_validators__(cls): @classmethod def validate(cls, value, field: ModelField): - alphabet = field.field_info.extra['alphabet'] + alphabet = field.field_info.extra["alphabet"] if any(c not in alphabet for c in value): - raise ValueError(f'{value!r} is not restricted to {alphabet!r}') + raise ValueError(f"{value!r} is not restricted to {alphabet!r}") return cls(value) @classmethod def __modify_schema__(cls, field_schema, field: Optional[ModelField]): if field: - alphabet = field.field_info.extra['alphabet'] - field_schema['examples'] = [c * 4 for c in alphabet] + alphabet = field.field_info.extra["alphabet"] + field_schema["examples"] = [c * 4 for c in alphabet] + class MetagenomeAnnotations(BaseModel): contig: str @@ -35,7 +38,7 @@ class MetagenomeAnnotations(BaseModel): class NotInDatabaseError(PydanticValueError): - code = 'table_name_not_in_db' + code = "table_name_not_in_db" tables = metadata.tables.keys() msg_template = f'value "TEMPLATE" is not in {tables}' msg_template = msg_template.replace("TEMPLATE", "{table_name}") @@ -64,19 +67,18 @@ class AnnotationTable(BaseModel): # class Config: # keep_untouched=(cached_property,) - @property def sample(self): - return self.id.split('-')[0] - + return self.id.split("-")[0] + @property def name(self): - return '-'.join(self.id.split('-')[1:]) + return "-".join(self.id.split("-")[1:]) @property def table(self): return get_table(self.id, index_col=self.index_col) - + @property def exists(self): return engine.has_table(self.id) @@ -85,41 +87,55 @@ def exists(self): def columns(self): return self.table.columns + # class ResultTable(AnnotationTable): # task: Optional[AsyncResult] - -# @cached_property_with_ttl(60) # cache invalidates after 60 sec # def table(self): # return get_table(self.id, index_col=self.index_col) # class Config: # arbitrary_types_allowed = True # smart_union = True + + class KmerTable(BaseModel): assembly: AnnotationTable size: Literal[3, 4, 5] = 5 norm_method: Literal["am_clr", "ilr", "clr"] = "am_clr" embed_dims: Optional[int] = 2 + # embed_dims: conint(gt=1, lt=100) = 2 embed_method: Literal["bhsne", "sksne", "umap", "densmap", "trimap"] = "bhsne" @property def counts(self) -> AnnotationTable: - return AnnotationTable(id=self.assembly.id.replace("-metagenome", f"-{self.size}mers")) - + return AnnotationTable( + id=self.assembly.id.replace("-metagenome", f"-{self.size}mers") + ) + @property def norm_freqs(self) -> AnnotationTable: - return AnnotationTable(id=self.assembly.id.replace("-metagenome", f"-{self.size}mers-{self.norm_method}")) - + return AnnotationTable( + id=self.assembly.id.replace( + "-metagenome", f"-{self.size}mers-{self.norm_method}" + ) + ) + @property def embedding(self) -> AnnotationTable: - return AnnotationTable(id=self.assembly.id.replace("-metagenome", f"-{self.size}mers-{self.norm_method}-{self.embed_method}")) + return AnnotationTable( + id=self.assembly.id.replace( + "-metagenome", + f"-{self.size}mers-{self.norm_method}-{self.embed_method}", + ) + ) + class SampleTables(BaseModel): binning: Optional[AnnotationTable] markers: Optional[AnnotationTable] metagenome: Optional[AnnotationTable] # The following are created after user upload - # via: + # via: # db ingestion (see serializers.py) # celery tasks (see tasks.py) refinements: Optional[AnnotationTable] @@ -128,28 +144,34 @@ class SampleTables(BaseModel): @property def kmers(self) -> List[KmerTable]: settings = [] - for size in [3,4,5]: + for size in [3, 4, 5]: for norm_method in ["am_clr"]: for embed_method in ["bhsne", "densmap", "umap", "sksne", "trimap"]: - settings.append(KmerTable( - assembly=self.metagenome, - size=size, - norm_method=norm_method, - embed_dims=2, - embed_method=embed_method, - )) + settings.append( + KmerTable( + assembly=self.metagenome, + size=size, + norm_method=norm_method, + embed_dims=2, + embed_method=embed_method, + ) + ) return settings - + @property def embeddings(self) -> AnnotationTable: - return AnnotationTable(id=self.metagenome.id.replace("-metagenome","-embeddings")) + return AnnotationTable( + id=self.metagenome.id.replace("-metagenome", "-embeddings") + ) @property def marker_symbols(self) -> AnnotationTable: - return AnnotationTable(id=self.markers.id.replace('-markers','-marker-symbols')) - + return AnnotationTable( + id=self.markers.id.replace("-markers", "-marker-symbols") + ) + # embeddings: Union[AsyncResult,str] - + # validators # @validator('*') # def table_must_exist_in_database(cls, v): @@ -167,16 +189,16 @@ def marker_symbols(self) -> AnnotationTable: class Config: arbitrary_types_allowed = True smart_union = True - MarkerAnnotations = create_model( - 'MarkerAnnotations', - apple='russet', - banana='yellow', + "MarkerAnnotations", + apple="russet", + banana="yellow", __base__=MetagenomeAnnotations, ) + class TaxonomyAnnotations(MetagenomeAnnotations): taxid: int superkingdom: Optional[str] @@ -186,10 +208,12 @@ class TaxonomyAnnotations(MetagenomeAnnotations): family: Optional[str] genus: Optional[str] species: Optional[str] - + + class CoverageAnnotations(MetagenomeAnnotations): coverage: float + class KmerAnnotations(MetagenomeAnnotations): x_1: float x_2: float @@ -197,13 +221,16 @@ class KmerAnnotations(MetagenomeAnnotations): norm_method: Optional[str] embed_method: Optional[str] + class Kmer(BaseModel): - value: RestrictedAlphabetStr = Field(alphabet='ATCG') + value: RestrictedAlphabetStr = Field(alphabet="ATCG") + class KmerCounts(MetagenomeAnnotations): # kmers: Field(..., regex = 'ATCG') kmers: List[Kmer] + class BinningAnnotations(MetagenomeAnnotations): cluster: str completeness: Optional[float] diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py index 9ff875ac..46ef0ce6 100644 --- a/automappa/utils/serializers.py +++ b/automappa/utils/serializers.py @@ -124,7 +124,9 @@ def store_binning_main(filepath: Path, if_exists: str = "replace") -> str: if "refinement_" in col or "cluster" in col or "contig" in col ] refinement_table_name = table_name.replace("-binning", "-refinement") - df[refine_cols].to_sql(refinement_table_name, engine, if_exists=if_exists, index=False) + df[refine_cols].to_sql( + refinement_table_name, engine, if_exists=if_exists, index=False + ) logger.debug( f"Saved refinements (shape={df[refine_cols].shape}) to postgres table: {refinement_table_name}" ) From 4810ba2d5d0335dc91f1017fbd1476ab1835d61e Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Mon, 16 May 2022 18:05:54 -0400 Subject: [PATCH 26/37] :art::see_no_evil: WIP for fetching/visualizing data using services :art: Fetch data from postgres service :art::broccoli: Retrieve scatterplot-2d coordinates from celery task-queue results (selected from auto-generated dropdown) :art::sunflower: Add flower configuration env variable to settings.py :art: generate product of all combinations of kmer pipeline params (kmer_size, norm_method, embed_method) for 2d-coords retrieval :art::racehorse::bug: Retrieve mag summary data from SampleTables properties instead of using get_table(...) :fire::bug: Remove default value of 'cluster' for mag_summary_cluster_col_dropdown :art::fire: Remove unnecessary nested lists of Input(...) in mag_summary callbacks :art: Add long_callback_manager to app (WIP: currently not being used) :art: Add handling of kmer param titles and metagenome annotation titles to format_axis_title(...) in figures.py :art::whale::rabbit: Change rabbitmq image from latest to 3.10.1-management-alpine (w/port) to use amqp management UI --- .env | 2 + TODO.md | 5 + automappa/app.py | 2 + automappa/apps/home.py | 52 ++++--- automappa/apps/mag_refinement.py | 146 ++++++++++++-------- automappa/apps/mag_summary.py | 223 +++++++++---------------------- automappa/index.py | 8 +- automappa/settings.py | 10 ++ automappa/tasks.py | 97 ++++++++++++-- automappa/utils/figures.py | 25 +++- automappa/utils/models.py | 48 ++++--- docker-compose.yml | 58 ++++---- 12 files changed, 378 insertions(+), 298 deletions(-) diff --git a/.env b/.env index e307a7b9..5adf2d79 100644 --- a/.env +++ b/.env @@ -14,6 +14,8 @@ RABBITMQ_DEFAULT_PASS="pass" RABBITMQ_URL="amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/" CELERY_BACKEND_URL='redis://redis:6379/0' CELERY_BROKER_URL="amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672//" +FLOWER_BROKER_API_URL="http://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:15672/api" +# https://github.com/mher/flower/issues/1036 # Grafana configuration # Allow anonymous authentication or not diff --git a/TODO.md b/TODO.md index 02ed9d19..23643033 100644 --- a/TODO.md +++ b/TODO.md @@ -21,6 +21,11 @@ 16. [ ] :art::fire: Refactor `store_binning_main(...)` s.t. refinement-data is stored in a separate async process. 17. [ ] :art::carrot: Add selections/dropdowns/progress updates for metagenome embeddings 18. [x] :art: Add embeddings selection dropdown in settings for 2d scatterplot axes (place task queueing from these selections) +19. [ ] :art: Show some type of progress for metagenome ingestion to postgres DB +20. [ ] :art: Dynamically Format scatterplot 2-d axes (i.e. Gc_Content to GC Content, etc.) +21. [ ] :racehorse: Reduce queries to postgres DB +22. [ ] :bug::wrench: Fix scatterplot-2d.figure callback (LINENO#685) `ValueError: columns overlap but no suffix specified: Index(['5mers-ilr-umap_x_1', '5mers-ilr-umap_x_2'], dtype='object')` +23. [ ] :bug::wrench: Add `dcc.Interval`? or `interval` argument? to Scatterplot 2D Axes dropdown s.t. disabled is updated appropriately (poll availability of embeddings in pg DB) -------------------------------------------------------------------------------------------------- diff --git a/automappa/app.py b/automappa/app.py index 6434d6c3..6910cacc 100755 --- a/automappa/app.py +++ b/automappa/app.py @@ -3,6 +3,7 @@ import dash_uploader as du from automappa.settings import server +from automappa.tasks import long_callback_manager app = dash.Dash( name=__name__, @@ -10,6 +11,7 @@ external_stylesheets=[dbc.themes.LUX, dbc.icons.BOOTSTRAP], update_title="Automapping...", suppress_callback_exceptions=True, + long_callback_manager=long_callback_manager, ) du.configure_upload(app=app, folder=server.root_upload_folder) diff --git a/automappa/apps/home.py b/automappa/apps/home.py index 5bb81796..acda0111 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import itertools import logging from dash import html, dcc from dash.dash_table import DataTable @@ -65,6 +66,7 @@ }, max_files=1, # 10240 MB = 10GB + # TODO: Add text to modal with max_file_size info... max_file_size=10240, ) @@ -83,6 +85,7 @@ }, max_files=1, # 10240 MB = 10GB + # TODO: Add text to modal with max_file_size info... max_file_size=10240, ) @@ -101,6 +104,7 @@ }, max_files=1, # 10240 MB = 10GB + # TODO: Add text to modal with max_file_size info... max_file_size=10240, ) @@ -548,21 +552,8 @@ def on_refine_mags_button_click( if n is None: raise PreventUpdate tables_dict = {} - if binning_select_value is not None and markers_select_value is not None: - marker_symbols_task = preprocess_marker_symbols.delay( - binning_select_value, markers_select_value - ) - # logger.debug(f"{type(marker_symbols_task)} {marker_symbols_task}") - # tables_dict["marker_symbols"] = markers_select_value.replace("-markers","-marker-symbols") if metagenome_select_value is not None: tables_dict["metagenome"] = {"id": metagenome_select_value} - embeddings_task = preprocess_embeddings( - metagenome_table=metagenome_select_value, - norm_method="am_clr", - embed_methods=["densmap", "umap", "bhsne"], - ) - # logger.debug(f"{type(embeddings_task)} {embeddings_task}") - # tables_dict["embeddings"] = metagenome_select_value.replace("-metagenome","-embeddings") if binning_select_value is not None: tables_dict.update( { @@ -572,16 +563,35 @@ def on_refine_mags_button_click( }, } ) - cluster_col = "cluster" - clusters_geom_medians_task = preprocess_clusters_geom_medians.delay( - binning_select_value, cluster_col - ) - # logger.debug(f"{type(clusters_geom_medians_task)} {clusters_geom_medians_task}") - # tables_dict["geom_medians"] = binning_select_value.replace("-binning",f"{cluster_col}-gmedians") if markers_select_value is not None: tables_dict["markers"] = {"id": markers_select_value} - logger.debug(tables_dict) - return SampleTables(**tables_dict).json() + sample = SampleTables(**tables_dict) + # BEGIN Queueing tasks from annotations... + # TODO: Refactor to separate bg-task submission? + # Show table of running tasks for user to monitor... + if sample.binning and sample.markers: + marker_symbols_task = preprocess_marker_symbols.delay( + sample.binning.id, sample.markers.id + ) + if sample.metagenome: + embedding_tasks = [] + kmer_sizes = set([kmer_table.size for kmer_table in sample.kmers]) + norm_methods = set([kmer_table.norm_method for kmer_table in sample.kmers]) + embed_methods = set([kmer_table.embed_method for kmer_table in sample.kmers]) + embed_methods = ["umap", "densmap", "bhsne"] + for kmer_size, norm_method in itertools.product(kmer_sizes, norm_methods): + embeddings_task = preprocess_embeddings( + metagenome_table=sample.metagenome.id, + kmer_size=kmer_size, + norm_method=norm_method, + embed_methods=embed_methods, + ) + embedding_tasks.append(embeddings_task) + if sample.binning: + clusters_geom_medians_task = preprocess_clusters_geom_medians.delay( + sample.binning.id, "cluster" + ) + return sample.json() @app.callback( diff --git a/automappa/apps/mag_refinement.py b/automappa/apps/mag_refinement.py index 7ebef09b..a83120e3 100644 --- a/automappa/apps/mag_refinement.py +++ b/automappa/apps/mag_refinement.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +import itertools import logging from pydantic import Json @@ -74,22 +75,38 @@ value=True, ) -scatterplot_2d_xaxis_dropdown = [ - html.Label("X-axis:"), +# TODO: Link x-axis-2d w/ y-axis-2d +# Combinations of kmer embeddings +# kmer-size [dropdown] +# kmer-norm-method [dropdown] +# kmer-embed-method [dropdown] +# All are filters to main dropdown combinations of kmer axes + +kmer_size_dropdown = [ + html.Label("K-mer size:"), dcc.Dropdown( - id="x-axis-2d", - options=[], - value="coverage", + id="kmer-size-dropdown", + options=[3, 4, 5], + value=5, clearable=False, ), ] -scatterplot_2d_yaxis_dropdown = [ - html.Label("Y-axis:"), +norm_method_dropdown = [ + html.Label("K-mer norm. method:"), dcc.Dropdown( - id="y-axis-2d", - options=[], - value="gc_content", + id="norm-method-dropdown", + options=["am_clr", "ilr"], + value="am_clr", + clearable=False, + ), +] + +scatterplot_2d_axes_dropdown = [ + html.Label("Axes:"), + dcc.Dropdown( + id="axes-2d", + value="coverage|gc_content", clearable=False, ), ] @@ -169,12 +186,9 @@ dbc.Col(scatterplot_2d_legend_toggle), ] ), - dbc.Row( - [ - dbc.Col(scatterplot_2d_xaxis_dropdown), - dbc.Col(scatterplot_2d_yaxis_dropdown), - ] - ), + dbc.Row(dbc.Col(kmer_size_dropdown)), + dbc.Row(dbc.Col(norm_method_dropdown)), + dbc.Row(dbc.Col(scatterplot_2d_axes_dropdown)), ], title="Figure 1: 2D Metagenome Overview", ), @@ -463,44 +477,44 @@ def color_by_column_options_callback(selected_tables_data: SampleTables): ] -@app.callback(Output("x-axis-2d", "options"), Input("selected-tables-store", "data")) -def x_axis_2d_options_callback(selected_tables_data: SampleTables): +@app.callback( + Output("axes-2d", "options"), + Input("selected-tables-store", "data"), + Input("kmer-size-dropdown", "value"), + Input("norm-method-dropdown", "value"), +) +def axes_2d_options_callback( + selected_tables_data: Json[SampleTables], + kmer_size_dropdown_value: int, + norm_method_dropdown_value: str, +) -> List[Dict[str, str]]: sample = SampleTables.parse_raw(selected_tables_data) binning_df = sample.binning.table - binning_cols = [ - {"label": col.title().replace("_", " "), "value": col, "disabled": False} - for col in binning_df.select_dtypes({"float64", "int64"}).columns - if col not in {"completeness", "purity", "taxid"} - ] - kmer_cols = [ + binning_combinations = [ { - "label": f"{kmer.embedding.name.replace('-',' ')}_x_1", - "value": f"{kmer.embedding.name}_x_1", - "disabled": not kmer.embedding.exists, + "label": " vs. ".join( + [x_axis.title().replace("_", " "), y_axis.title().replace("_", " ")] + ), + "value": "|".join([x_axis, y_axis]), + "disabled": False, } - for kmer in sample.kmers - ] - return binning_cols + kmer_cols - - -@app.callback(Output("y-axis-2d", "options"), Input("selected-tables-store", "data")) -def y_axis_2d_options_callback(selected_tables_data: Json[SampleTables]): - sample = SampleTables.parse_raw(selected_tables_data) - binning_df = sample.binning.table - binning_cols = [ - {"label": col.title().replace("_", " "), "value": col, "disabled": False} - for col in binning_df.select_dtypes({"float64", "int64"}).columns - if col not in {"completeness", "purity", "taxid"} + for x_axis, y_axis in itertools.combinations( + binning_df.select_dtypes({"float64", "int64"}).columns, 2 + ) + if x_axis not in {"completeness", "purity", "taxid"} + and y_axis not in {"completeness", "purity", "taxid"} ] - kmer_cols = [ + embeddings = [ { - "label": f"{kmer.embedding.name.replace('-',' ')}_x_2", - "value": f"{kmer.embedding.name}_x_2", + "label": kmer.embedding.name, + "value": f"{kmer.embedding.name}_x_1|{kmer.embedding.name}_x_2", "disabled": not kmer.embedding.exists, } for kmer in sample.kmers + if kmer.size == kmer_size_dropdown_value + and kmer.norm_method == norm_method_dropdown_value ] - return binning_cols + kmer_cols + return binning_combinations + embeddings @app.callback( @@ -589,8 +603,9 @@ def update_mag_metrics_datatable_callback( Output("scatterplot-2d", "figure"), [ Input("selected-tables-store", "data"), - Input("x-axis-2d", "value"), - Input("y-axis-2d", "value"), + Input("kmer-size-dropdown", "value"), + Input("norm-method-dropdown", "value"), + Input("axes-2d", "value"), Input("scatterplot-2d-legend-toggle", "value"), Input("color-by-column", "value"), Input("hide-selections-toggle", "value"), @@ -600,8 +615,9 @@ def update_mag_metrics_datatable_callback( def scatterplot_2d_figure_callback( # selected_tables_data: MetagenomeAnnotationsTables, selected_tables_data: Json[SampleTables], - xaxis_column: str, - yaxis_column: str, + kmer_size_dropdown_value: int, + norm_method_dropdown_value: str, + axes_columns: str, show_legend: bool, color_by_col: str, hide_selection_toggle: bool, @@ -609,6 +625,7 @@ def scatterplot_2d_figure_callback( ) -> go.Figure: # NOTE: btn_clicks is an input so this figure is updated when new refinements are saved # TODO: #23 refactor scatterplot callbacks + # - Add Input("scatterplot-2d", "layout") ? # TODO: Refactor data retrieval/validation sample = SampleTables.parse_raw(selected_tables_data) bin_df = sample.binning.table @@ -644,18 +661,29 @@ def scatterplot_2d_figure_callback( # based on current contig selections # TODO: Should check norm_method, kmer_size prior to retrieving embeddings table... # Add norm method and kmer_size dropdowns... + xaxis_column, yaxis_column = axes_columns.split("|") if "_x_1" in xaxis_column or "_x_2" in yaxis_column: - if sample.embeddings.exists: - embedding_df = sample.embeddings.table - bin_df = bin_df.join(embedding_df, how="left") - else: - for kmer_table in sample.kmers: - if ( - kmer_table.embedding.name == xaxis_column - and kmer_table.embedding.name == yaxis_column - ): - bin_df = bin_df.join(kmer_table.embedding.table, how="left") - break + # TODO: Fix retrieval of axes with embeddings... + for embeddings in sample.embeddings: + sizemers, norm_method, __ = embeddings.name.split("-") + kmer_size = int(sizemers.replace("mers", "")) + if ( + norm_method == norm_method_dropdown_value + and kmer_size == kmer_size_dropdown_value + and embeddings.exists + ): + embedding_df = embeddings.table + bin_df = bin_df.join(embedding_df, how="left") + else: + for kmer in sample.kmers: + if ( + f"{kmer.embedding.name}_x_1" == xaxis_column + and f"{kmer.embedding.name}_x_2" == yaxis_column + and kmer.size == kmer_size_dropdown_value + and kmer.norm_method == norm_method_dropdown_value + ): + bin_df = bin_df.join(kmer.embedding.table, how="left") + break fillnas = { "cluster": "unclustered", diff --git a/automappa/apps/mag_summary.py b/automappa/apps/mag_summary.py index a1af5fd5..42b3601c 100644 --- a/automappa/apps/mag_summary.py +++ b/automappa/apps/mag_summary.py @@ -2,11 +2,6 @@ from typing import Dict, List from dash.exceptions import PreventUpdate -import numpy as np -import pandas as pd - -from autometa.binning.summary import fragmentation_metric, get_metabin_stats - from dash import dcc, html from dash.dash_table import DataTable from dash.dependencies import Input, Output @@ -17,7 +12,7 @@ from automappa.app import app from automappa.utils.figures import metric_barplot, taxonomy_sankey, metric_boxplot from automappa.utils.models import SampleTables -from automappa.utils.serializers import get_table +from automappa.tasks import get_metabin_stats_summary pio.templates.default = "plotly_white" @@ -151,7 +146,6 @@ html.Label("MAG Summary Cluster Column Dropdown"), dcc.Dropdown( id="mag-summary-cluster-col-dropdown", - value="cluster", placeholder="Select a cluster column to compute MAG summary metrics", clearable=False, ), @@ -214,25 +208,14 @@ @app.callback( Output("mag-overview-metrics-boxplot", "figure"), - [ - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - ], + Input("selected-tables-store", "data"), + Input("mag-summary-cluster-col-dropdown", "value"), ) def mag_overview_metrics_boxplot_callback( selected_tables_data: SampleTables, cluster_col: str ) -> go.Figure: - """ - Writes - Given dataframe as json and cluster column: - Input: - - binning dataframe - - binning column - Returns: - n_unique_bins - number of unique bins - """ - tables = SampleTables.parse_raw(selected_tables_data) - mag_summary_df = get_table(tables.binning) + sample = SampleTables.parse_raw(selected_tables_data) + mag_summary_df = sample.binning.table if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -243,16 +226,14 @@ def mag_overview_metrics_boxplot_callback( @app.callback( Output("mag-overview-gc-content-boxplot", "figure"), - [ - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - ], + Input("selected-tables-store", "data"), + Input("mag-summary-cluster-col-dropdown", "value"), ) def mag_overview_gc_content_boxplot_callback( selected_tables_data: SampleTables, cluster_col: str ) -> go.Figure: - tables = SampleTables.parse_raw(selected_tables_data) - mag_summary_df = get_table(tables.binning) + sample = SampleTables.parse_raw(selected_tables_data) + mag_summary_df = sample.binning.table if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -264,16 +245,14 @@ def mag_overview_gc_content_boxplot_callback( @app.callback( Output("mag-overview-length-boxplot", "figure"), - [ - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - ], + Input("selected-tables-store", "data"), + Input("mag-summary-cluster-col-dropdown", "value"), ) def mag_overview_length_boxplot_callback( selected_tables_data: SampleTables, cluster_col: str ) -> go.Figure: - tables = SampleTables.parse_raw(selected_tables_data) - mag_summary_df = get_table(tables.binning) + sample = SampleTables.parse_raw(selected_tables_data) + mag_summary_df = sample.binning.table if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -284,16 +263,14 @@ def mag_overview_length_boxplot_callback( @app.callback( Output("mag-overview-coverage-boxplot", "figure"), - [ - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - ], + Input("selected-tables-store", "data"), + Input("mag-summary-cluster-col-dropdown", "value"), ) def mag_overview_coverage_boxplot_callback( selected_tables_data: SampleTables, cluster_col: str ) -> go.Figure: - tables = SampleTables.parse_raw(selected_tables_data) - mag_summary_df = get_table(tables.binning) + sample = SampleTables.parse_raw(selected_tables_data) + mag_summary_df = sample.binning.table if cluster_col not in mag_summary_df.columns: raise PreventUpdate mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) @@ -304,13 +281,13 @@ def mag_overview_coverage_boxplot_callback( @app.callback( Output("mag-summary-cluster-col-dropdown", "options"), - [Input("selected-tables-store", "data")], + Input("selected-tables-store", "data"), ) def mag_summary_cluster_col_dropdown_options_callback( selected_tables_data: SampleTables, -): - tables = SampleTables.parse_raw(selected_tables_data) - refinement_df = get_table(tables.refinements, index_col="contig") +) -> List[Dict[str, str]]: + sample = SampleTables.parse_raw(selected_tables_data) + refinement_df = sample.refinements.table return [ {"label": col.title(), "value": col} for col in refinement_df.columns @@ -320,67 +297,19 @@ def mag_summary_cluster_col_dropdown_options_callback( @app.callback( Output("mag-summary-stats-datatable", "children"), - [ - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - ], + Input("selected-tables-store", "data"), + Input("mag-summary-cluster-col-dropdown", "value"), ) def mag_summary_stats_datatable_callback( selected_tables_data: SampleTables, cluster_col: str -): - tables = SampleTables.parse_raw(selected_tables_data) - bin_df = get_table(tables.binning, index_col="contig") - refinement_df = get_table(tables.refinements, index_col="contig").drop( - columns="cluster" +) -> DataTable: + sample = SampleTables.parse_raw(selected_tables_data) + stats_df = get_metabin_stats_summary( + binning_table=sample.binning.id, + refinements_table=sample.refinements.id, + markers_table=sample.markers.id, + cluster_col=cluster_col, ) - bin_df = bin_df.join(refinement_df, how="right") - markers = get_table(tables.markers, index_col="contig") - if cluster_col not in bin_df.columns: - num_expected_markers = markers.shape[1] - length_weighted_coverage = np.average( - a=bin_df.coverage, weights=bin_df.length / bin_df.length.sum() - ) - length_weighted_gc = np.average( - a=bin_df.gc_content, weights=bin_df.length / bin_df.length.sum() - ) - cluster_pfams = markers[markers.index.isin(bin_df.index)] - pfam_counts = cluster_pfams.sum() - total_markers = pfam_counts.sum() - num_single_copy_markers = pfam_counts[pfam_counts == 1].count() - num_markers_present = pfam_counts[pfam_counts >= 1].count() - stats_df = pd.DataFrame( - [ - { - cluster_col: "metagenome", - "nseqs": bin_df.shape[0], - "size (bp)": bin_df.length.sum(), - "N90": fragmentation_metric(bin_df, quality_measure=0.9), - "N50": fragmentation_metric(bin_df, quality_measure=0.5), - "N10": fragmentation_metric(bin_df, quality_measure=0.1), - "length_weighted_gc_content": length_weighted_gc, - "min_gc_content": bin_df.gc_content.min(), - "max_gc_content": bin_df.gc_content.max(), - "std_gc_content": bin_df.gc_content.std(), - "length_weighted_coverage": length_weighted_coverage, - "min_coverage": bin_df.coverage.min(), - "max_coverage": bin_df.coverage.max(), - "std_coverage": bin_df.coverage.std(), - "num_total_markers": total_markers, - f"num_unique_markers (expected {num_expected_markers})": num_markers_present, - "num_single_copy_markers": num_single_copy_markers, - } - ] - ).convert_dtypes() - else: - stats_df = ( - get_metabin_stats( - bin_df=bin_df, - markers=markers, - cluster_col=cluster_col, - ) - .reset_index() - .fillna(0) - ) return DataTable( data=stats_df.to_dict("records"), columns=[ @@ -390,8 +319,8 @@ def mag_summary_stats_datatable_callback( style_cell={ "height": "auto", # all three widths are needed - "minWidth": "120px", "width": "120px", + "minWidth": "120px", "maxWidth": "120px", "whiteSpace": "normal", }, @@ -404,20 +333,16 @@ def mag_summary_stats_datatable_callback( @app.callback( Output("mag-taxonomy-sankey", "figure"), - [ - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - Input("mag-selection-dropdown", "value"), - ], + Input("selected-tables-store", "data"), + Input("mag-summary-cluster-col-dropdown", "value"), + Input("mag-selection-dropdown", "value"), ) def mag_taxonomy_sankey_callback( selected_tables_data: SampleTables, cluster_col: str, selected_mag: str ) -> go.Figure: - tables = SampleTables.parse_raw(selected_tables_data) - bin_df = get_table(tables.binning, index_col="contig") - refinement_df = get_table(tables.refinements, index_col="contig").drop( - columns="cluster" - ) + sample = SampleTables.parse_raw(selected_tables_data) + bin_df = sample.binning.table + refinement_df = sample.refinements.table.drop(columns="cluster") bin_df = bin_df.join(refinement_df, how="right") if cluster_col not in bin_df.columns: raise PreventUpdate @@ -428,20 +353,16 @@ def mag_taxonomy_sankey_callback( @app.callback( Output("mag-gc-content-boxplot", "figure"), - [ - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - Input("mag-selection-dropdown", "value"), - ], + Input("selected-tables-store", "data"), + Input("mag-summary-cluster-col-dropdown", "value"), + Input("mag-selection-dropdown", "value"), ) def mag_summary_gc_content_boxplot_callback( selected_tables_data: SampleTables, cluster_col: str, selected_mag: str ) -> go.Figure: - tables = SampleTables.parse_raw(selected_tables_data) - bin_df = get_table(tables.binning, index_col="contig") - refinement_df = get_table(tables.refinements, index_col="contig").drop( - columns="cluster" - ) + sample = SampleTables.parse_raw(selected_tables_data) + bin_df = sample.binning.table + refinement_df = sample.refinements.table.drop(columns="cluster") bin_df = bin_df.join(refinement_df, how="right") if cluster_col not in bin_df.columns: raise PreventUpdate @@ -455,22 +376,18 @@ def mag_summary_gc_content_boxplot_callback( @app.callback( Output("mag-metrics-barplot", "figure"), - [ - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - Input("mag-selection-dropdown", "value"), - ], + Input("selected-tables-store", "data"), + Input("mag-summary-cluster-col-dropdown", "value"), + Input("mag-selection-dropdown", "value"), ) def mag_metrics_callback( selected_tables_data: SampleTables, cluster_col: str, selected_mag: str ) -> go.Figure: if not selected_mag: raise PreventUpdate - tables = SampleTables.parse_raw(selected_tables_data) - bin_df = get_table(tables.binning, index_col="contig") - refinement_df = get_table(tables.refinements, index_col="contig").drop( - columns="cluster" - ) + sample = SampleTables.parse_raw(selected_tables_data) + bin_df = sample.binning.table + refinement_df = sample.refinements.table.drop(columns="cluster") mag_summary_df = bin_df.join(refinement_df, how="right") if cluster_col not in mag_summary_df.columns: raise PreventUpdate @@ -485,22 +402,18 @@ def mag_metrics_callback( @app.callback( Output("mag-coverage-boxplot", "figure"), - [ - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - Input("mag-selection-dropdown", "value"), - ], + Input("selected-tables-store", "data"), + Input("mag-summary-cluster-col-dropdown", "value"), + Input("mag-selection-dropdown", "value"), ) def mag_summary_gc_content_boxplot_callback( selected_tables_data: SampleTables, cluster_col: str, selected_mag: str ) -> go.Figure: if not selected_mag: raise PreventUpdate - tables = SampleTables.parse_raw(selected_tables_data) - bin_df = get_table(tables.binning, index_col="contig") - refinement_df = get_table(tables.refinements, index_col="contig").drop( - columns="cluster" - ) + sample = SampleTables.parse_raw(selected_tables_data) + bin_df = sample.binning.table + refinement_df = sample.refinements.table.drop(columns="cluster") mag_summary_df = bin_df.join(refinement_df, how="right") if cluster_col not in mag_summary_df.columns: raise PreventUpdate @@ -513,22 +426,18 @@ def mag_summary_gc_content_boxplot_callback( @app.callback( Output("mag-length-boxplot", "figure"), - [ - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - Input("mag-selection-dropdown", "value"), - ], + Input("selected-tables-store", "data"), + Input("mag-summary-cluster-col-dropdown", "value"), + Input("mag-selection-dropdown", "value"), ) def mag_summary_gc_content_boxplot_callback( selected_tables_data: SampleTables, cluster_col: str, selected_mag: str ) -> go.Figure: if not selected_mag: raise PreventUpdate - tables = SampleTables.parse_raw(selected_tables_data) - bin_df = get_table(tables.binning, index_col="contig") - refinement_df = get_table(tables.refinements, index_col="contig").drop( - columns="cluster" - ) + sample = SampleTables.parse_raw(selected_tables_data) + bin_df = sample.binning.table + refinement_df = sample.refinements.table.drop(columns="cluster") mag_summary_df = bin_df.join(refinement_df, how="right") if cluster_col not in mag_summary_df.columns: raise PreventUpdate @@ -541,16 +450,14 @@ def mag_summary_gc_content_boxplot_callback( @app.callback( Output("mag-selection-dropdown", "options"), - [ - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - ], + Input("selected-tables-store", "data"), + Input("mag-summary-cluster-col-dropdown", "value"), ) def mag_selection_dropdown_options_callback( selected_tables_data: SampleTables, cluster_col: str ) -> List[Dict[str, str]]: - tables = SampleTables.parse_raw(selected_tables_data) - df = get_table(tables.refinements, index_col="contig") + sample = SampleTables.parse_raw(selected_tables_data) + df = sample.refinements.table if cluster_col not in df.columns: options = [] else: diff --git a/automappa/index.py b/automappa/index.py index 42c91274..1145863b 100755 --- a/automappa/index.py +++ b/automappa/index.py @@ -3,7 +3,6 @@ import argparse import logging -from typing import Dict from dash.dependencies import Input, Output from dash import dcc, html @@ -24,7 +23,8 @@ @app.callback( Output("tab-content", "children"), - [Input("tabs", "active_tab"), Input("selected-tables-store", "data")], + Input("tabs", "active_tab"), + Input("selected-tables-store", "data"), ) def render_content( active_tab: str, @@ -33,8 +33,8 @@ def render_content( # Only alow user to navigate to mag refinement or summary if data is already uploaded if selected_tables_data is None: return home.layout - tables = SampleTables.parse_raw(selected_tables_data) - if not tables.binning or not tables.markers: + sample = SampleTables.parse_raw(selected_tables_data) + if not sample.binning or not sample.markers: return home.layout layouts = { "home": home.layout, diff --git a/automappa/settings.py b/automappa/settings.py index eabb3ffe..4e4d18a4 100644 --- a/automappa/settings.py +++ b/automappa/settings.py @@ -4,6 +4,7 @@ from typing import Optional from pydantic import ( BaseSettings, + HttpUrl, RedisDsn, PostgresDsn, AmqpDsn, @@ -43,6 +44,15 @@ class Config: env_file_encoding: str = "utf-8" +class FlowerSettings(BaseSettings): + broker_api_url: HttpUrl + + class Config: + env_prefix: str = "FLOWER_" + env_file: str = ".env" + env_file_encoding: str = "utf-8" + + class ServerSettings(BaseSettings): root_upload_folder: Path # Dash/Plotly diff --git a/automappa/tasks.py b/automappa/tasks.py index ca3f2d87..06ff8d6c 100644 --- a/automappa/tasks.py +++ b/automappa/tasks.py @@ -1,11 +1,12 @@ #!/usr/bin/env python -import tempfile import random +import tempfile # import time from typing import List +import numpy as np import pandas as pd from geom_median.numpy import compute_geometric_median @@ -13,12 +14,15 @@ from celery import Celery, group from celery.utils.log import get_task_logger from celery.result import AsyncResult +from dash.long_callback import CeleryLongCallbackManager from Bio import SeqIO from autometa.common import kmers +from autometa.binning.summary import fragmentation_metric, get_metabin_stats from automappa import settings +from automappa.utils.figures import get_scattergl_traces from automappa.utils.markers import get_marker_symbols from automappa.utils.serializers import ( @@ -32,7 +36,7 @@ __name__, backend=settings.celery.backend_url, broker=settings.celery.broker_url ) queue.config_from_object("automappa.conf.celeryconfig") - +long_callback_manager = CeleryLongCallbackManager(queue) logger = get_task_logger(__name__) if settings.server.debug: @@ -75,7 +79,7 @@ def count_kmer(self, metagenome_table: str, size: int = 5, cpus: int = None) -> records = get_metagenome_seqrecords(metagenome_table) # Uncomment next line to speed-up debugging... # FIXME: Comment out below: - # records = random.sample(records, k=1_000) + records = random.sample(records, k=1_000) with tempfile.NamedTemporaryFile(mode="w") as tmp: SeqIO.write(records, tmp.name, "fasta") tmp.seek(0) @@ -104,15 +108,28 @@ def normalize_kmer(self, counts_table: str, norm_method: str) -> str: @queue.task(bind=True) -def embed_kmer(self, norm_table: str, embed_method: str, n_jobs: int = 1) -> str: +def embed_kmer( + self, norm_table: str, embed_method: str, embed_dims: int = 2, n_jobs: int = 1 +) -> str: norm_df = get_table(norm_table, index_col="contig") # FIXME: Refactor renaming of embed_df.columns prev_kmer_params = norm_table.split("-", 1)[-1] + method_kwargs = {"output_dens": True} if embed_method in {"umap", "densmap"} else {} + # output_dens: float (optional, default False) + # Determines whether the local radii of the final embedding (an inverse + # measure of local density) are computed and returned in addition to + # the embedding. If set to True, local radii of the original data + # are also included in the output for comparison; the output is a tuple + # (embedding, original local radii, embedding local radii). This option + # can also be used when densmap=False to calculate the densities for + # UMAP embeddings. embed_df = kmers.embed( - # TODO: (on autometa[2.0.4] release): norm_df, method=embed_method, embed_dimensions=2, n_jobs=n_jobs norm_df, method=embed_method, - embed_dimensions=2, + embed_dimensions=embed_dims, + # FIXME: add in below lines when autometa 2.1 available... + # n_jobs=n_jobs, + # **method_kwargs, ).rename( columns={ "x_1": f"{prev_kmer_params}-{embed_method}_x_1", @@ -139,13 +156,19 @@ def preprocess_embeddings( cpus: int = 1, kmer_size: int = 5, norm_method: str = "am_clr", + embed_dims: int = 2, embed_methods: List[str] = ["bhsne", "densmap", "trimap", "umap"], ): - embeddings_table = metagenome_table.replace("-metagenome", "-embeddings") + embeddings_table = metagenome_table.replace( + "-metagenome", f"-{kmer_size}mers-{norm_method}-embeddings" + ) kmer_pipeline = ( count_kmer.s(metagenome_table, kmer_size, cpus) | normalize_kmer.s(norm_method) - | group(embed_kmer.s(embed_method, cpus) for embed_method in embed_methods) + | group( + embed_kmer.s(embed_method, embed_dims, cpus) + for embed_method in embed_methods + ) | aggregate_embeddings.s(embeddings_table) ) result = kmer_pipeline() @@ -209,7 +232,7 @@ def get_embedding_traces_df(embeddings_table: str) -> pd.DataFrame: # 1. Compute all embeddings for assembly... # 2. groupby cluster # 3. Extract k-mer size, norm method, embed method - + df = get_table(embeddings_table, index_col="contig") embed_traces = [] for embed_method in ["trimap", "densmap", "bhsne", "umap", "sksne"]: traces_df = get_scattergl_traces( @@ -220,6 +243,62 @@ def get_embedding_traces_df(embeddings_table: str) -> pd.DataFrame: embed_traces_df = pd.concat(embed_traces, axis=1) return embed_traces_df +# @queue.task(bind=True) +# def get_metabin_stats_summary(self, binning_table:str, refinements_table:str, markers_table:str, cluster_col:str = "cluster"): +def get_metabin_stats_summary(binning_table:str, refinements_table:str, markers_table:str, cluster_col:str = "cluster") -> pd.DataFrame: + bin_df = get_table(binning_table, index_col="contig") + refinements_df = get_table(refinements_table, index_col='contig').drop( + columns="cluster" + ) + bin_df = bin_df.join(refinements_df, how="right") + markers = get_table(markers_table, index_col='contig') + if cluster_col not in bin_df.columns: + num_expected_markers = markers.shape[1] + length_weighted_coverage = np.average( + a=bin_df.coverage, weights=bin_df.length / bin_df.length.sum() + ) + length_weighted_gc = np.average( + a=bin_df.gc_content, weights=bin_df.length / bin_df.length.sum() + ) + cluster_pfams = markers[markers.index.isin(bin_df.index)] + pfam_counts = cluster_pfams.sum() + total_markers = pfam_counts.sum() + num_single_copy_markers = pfam_counts[pfam_counts == 1].count() + num_markers_present = pfam_counts[pfam_counts >= 1].count() + stats_df = pd.DataFrame( + [ + { + cluster_col: "metagenome", + "nseqs": bin_df.shape[0], + "size (bp)": bin_df.length.sum(), + "N90": fragmentation_metric(bin_df, quality_measure=0.9), + "N50": fragmentation_metric(bin_df, quality_measure=0.5), + "N10": fragmentation_metric(bin_df, quality_measure=0.1), + "length_weighted_gc_content": length_weighted_gc, + "min_gc_content": bin_df.gc_content.min(), + "max_gc_content": bin_df.gc_content.max(), + "std_gc_content": bin_df.gc_content.std(), + "length_weighted_coverage": length_weighted_coverage, + "min_coverage": bin_df.coverage.min(), + "max_coverage": bin_df.coverage.max(), + "std_coverage": bin_df.coverage.std(), + "num_total_markers": total_markers, + f"num_unique_markers (expected {num_expected_markers})": num_markers_present, + "num_single_copy_markers": num_single_copy_markers, + } + ] + ).convert_dtypes() + else: + stats_df = ( + get_metabin_stats( + bin_df=bin_df, + markers=markers, + cluster_col=cluster_col, + ) + .reset_index() + .fillna(0) + ) + return stats_df if __name__ == "__main__": pass diff --git a/automappa/utils/figures.py b/automappa/utils/figures.py index 5627b9f5..64af8c37 100644 --- a/automappa/utils/figures.py +++ b/automappa/utils/figures.py @@ -143,10 +143,29 @@ def format_axis_title(axis_title: str) -> str: formatted axis title """ if "_x_" in axis_title: + method_titles = { + "bhsne": "BH-tSNE", + "sksne": "(sklearn) BH-tSNE", + "umap": "UMAP", + "trimap": "TriMap", + "densmap": "DensMap", + } + # {kmer_size}mers-{norm_method}-{embed_method}_x_{1,2} + mers, norm_method, embed_method_embed_dim = axis_title.split("-") + norm_method_titles = {"am_clr": "CLR", "ilr": "ILR"} + norm_method = norm_method_titles.get(norm_method, norm_method.upper()) + embed_method, embed_dim = embed_method_embed_dim.split("_", 1) + embed_method = method_titles.get(embed_method, embed_method.upper()) + kmer_size = mers.replace("mers", "") + # formatted_axis_title = f"(k={kmer_size}, norm={norm_method}) {embed_method} {embed_dim}" + formatted_axis_title = embed_dim + elif "_" in axis_title: + metagenome_metadata_titles = {"gc_content": "GC Content"} col_list = axis_title.split("_") - embed_method = col_list[0] - embed_dim = "_".join(col_list[1:]) - formatted_axis_title = f"{embed_method.upper()} {embed_dim}" + metadata_title = " ".join(col.upper() for col in col_list) + formatted_axis_title = metagenome_metadata_titles.get( + axis_title, metadata_title + ) else: formatted_axis_title = axis_title.title() return formatted_axis_title diff --git a/automappa/utils/models.py b/automappa/utils/models.py index d1d49a69..4a163512 100644 --- a/automappa/utils/models.py +++ b/automappa/utils/models.py @@ -1,3 +1,4 @@ +import itertools from pydantic import BaseModel, Field, PydanticValueError, create_model from pydantic.fields import ModelField from typing import List, Literal, Optional @@ -68,7 +69,7 @@ class AnnotationTable(BaseModel): # keep_untouched=(cached_property,) @property - def sample(self): + def sample_checksum(self): return self.id.split("-")[0] @property @@ -144,25 +145,38 @@ class SampleTables(BaseModel): @property def kmers(self) -> List[KmerTable]: settings = [] - for size in [3, 4, 5]: - for norm_method in ["am_clr"]: - for embed_method in ["bhsne", "densmap", "umap", "sksne", "trimap"]: - settings.append( - KmerTable( - assembly=self.metagenome, - size=size, - norm_method=norm_method, - embed_dims=2, - embed_method=embed_method, - ) - ) + sizes = [3, 4, 5] + norm_methods = ["am_clr", "ilr"] + embed_methods = ["bhsne", "densmap", "umap", "sksne", "trimap"] + for size, norm_method, embed_method in itertools.product( + sizes, norm_methods, embed_methods + ): + settings.append( + KmerTable( + assembly=self.metagenome, + size=size, + norm_method=norm_method, + embed_dims=2, + embed_method=embed_method, + ) + ) return settings @property - def embeddings(self) -> AnnotationTable: - return AnnotationTable( - id=self.metagenome.id.replace("-metagenome", "-embeddings") - ) + def embeddings(self) -> List[AnnotationTable]: + kmer_sizes = set([kmer_table.size for kmer_table in self.kmers]) + norm_methods = set([kmer_table.norm_method for kmer_table in self.kmers]) + # NOTE: The corresponding table-name suffix format (AnnotationTable(id=...)) + # is at https://github.com/WiscEvan/Automappa/blob/977dbbf6dca8cc62f974eb1c6a2f48fc25f2ddb2/automappa/tasks.py#L149 + return [ + AnnotationTable( + id=self.metagenome.id.replace( + "-metagenome", f"-{kmer_size}mers-{norm_method}-embeddings" + ), + index_col="contig", + ) + for kmer_size, norm_method in itertools.product(kmer_sizes, norm_methods) + ] @property def marker_symbols(self) -> AnnotationTable: diff --git a/docker-compose.yml b/docker-compose.yml index f1cc9844..398e8552 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -14,10 +14,13 @@ services: - postgres-data:/var/lib/postgresql/data rabbitmq: - image: rabbitmq:latest + image: rabbitmq:3.10.1-management-alpine restart: always ports: + # AMQP protocol port - 5672:5672 + # HTTP management UI + - 15672:15672 env_file: - .env @@ -46,30 +49,31 @@ services: - .env depends_on: - celery + - rabbitmq - prometheus: - image: prom/prometheus:latest - ports: - - 9090:9090 - volumes: - - prometheus-data:/prometheus - # - ./prometheus.yml:/etc/prometheus/prometheus.yml - depends_on: - - flower + # prometheus: + # image: prom/prometheus:latest + # ports: + # - 9090:9090 + # volumes: + # - prometheus-data:/prometheus + # # - ./prometheus.yml:/etc/prometheus/prometheus.yml + # depends_on: + # - flower - grafana: - image: grafana/grafana:latest - ports: - - 3000:3000 - env_file: - - .env - volumes: - - grafana-storage:/var/lib/grafana - - ./docker/grafana/provisioning:/etc/grafana/provisioning - - ./docker/grafana/grafana.ini:/etc/grafana/grafana.ini - - ./docker/grafana/dashboards:/etc/grafana/dashboards - depends_on: - - prometheus + # grafana: + # image: grafana/grafana:latest + # ports: + # - 3000:3000 + # env_file: + # - .env + # volumes: + # - grafana-storage:/var/lib/grafana + # - ./docker/grafana/provisioning:/etc/grafana/provisioning + # - ./docker/grafana/grafana.ini:/etc/grafana/grafana.ini + # - ./docker/grafana/dashboards:/etc/grafana/dashboards + # depends_on: + # - prometheus redis: image: "redis:latest" @@ -96,8 +100,8 @@ services: volumes: postgres-data: - grafana-storage: - driver: local - prometheus-data: - driver: local + # grafana-storage: + # driver: local + # prometheus-data: + # driver: local From ef3f205f11aa0f5225a988fd85a2edb28c69a7eb Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Wed, 18 May 2022 14:24:01 -0400 Subject: [PATCH 27/37] :green_heart: Add test SampleTables model instance :memo: Move misc. markdown docs to docs directory --- automappa/utils/tests/test_models.py | 26 +++++++++++++++++ docs/IDEA.md | 0 TODO.md => docs/TODO.md | 42 ++-------------------------- docs/TROUBLESHOOTING.md | 39 ++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 39 deletions(-) create mode 100644 automappa/utils/tests/test_models.py create mode 100644 docs/IDEA.md rename TODO.md => docs/TODO.md (64%) create mode 100644 docs/TROUBLESHOOTING.md diff --git a/automappa/utils/tests/test_models.py b/automappa/utils/tests/test_models.py new file mode 100644 index 00000000..26dd2c07 --- /dev/null +++ b/automappa/utils/tests/test_models.py @@ -0,0 +1,26 @@ +import pytest + +from automappa.utils.models import SampleTables, AnnotationTable + +# @pytest.fixture() +# def sampletables_fixture(): + + +def get_sampletable(): + return SampleTables( + **{ + "metagenome": {"id": "2a32c68b68deb1b2c5c82871b592d392-metagenome"}, + "binning": {"id": "d92e6e59d3fb4f7d988ed6adf5e71cc4-binning"}, + "refinements": {"id": "d92e6e59d3fb4f7d988ed6adf5e71cc4-refinement"}, + "markers": {"id": "1bba604b9ebb737e20af35c66fa9cff9-markers"}, + } + ) + + +def get_no_binning_sampletable(): + return SampleTables( + **{ + "metagenome": {"id": "2a32c68b68deb1b2c5c82871b592d392-metagenome"}, + "markers": {"id": "1bba604b9ebb737e20af35c66fa9cff9-markers"}, + } + ) diff --git a/docs/IDEA.md b/docs/IDEA.md new file mode 100644 index 00000000..e69de29b diff --git a/TODO.md b/docs/TODO.md similarity index 64% rename from TODO.md rename to docs/TODO.md index 23643033..0f6aaf28 100644 --- a/TODO.md +++ b/docs/TODO.md @@ -22,52 +22,16 @@ 17. [ ] :art::carrot: Add selections/dropdowns/progress updates for metagenome embeddings 18. [x] :art: Add embeddings selection dropdown in settings for 2d scatterplot axes (place task queueing from these selections) 19. [ ] :art: Show some type of progress for metagenome ingestion to postgres DB -20. [ ] :art: Dynamically Format scatterplot 2-d axes (i.e. Gc_Content to GC Content, etc.) +20. [x] :art: Dynamically Format scatterplot 2-d axes (i.e. Gc_Content to GC Content, etc.) 21. [ ] :racehorse: Reduce queries to postgres DB -22. [ ] :bug::wrench: Fix scatterplot-2d.figure callback (LINENO#685) `ValueError: columns overlap but no suffix specified: Index(['5mers-ilr-umap_x_1', '5mers-ilr-umap_x_2'], dtype='object')` +22. [x] :bug::wrench: Fix scatterplot-2d.figure callback (LINENO#685) `ValueError: columns overlap but no suffix specified: Index(['5mers-ilr-umap_x_1', '5mers-ilr-umap_x_2'], dtype='object')` 23. [ ] :bug::wrench: Add `dcc.Interval`? or `interval` argument? to Scatterplot 2D Axes dropdown s.t. disabled is updated appropriately (poll availability of embeddings in pg DB) -------------------------------------------------------------------------------------------------- -## Troubleshooting Celery, RabbitMQ task-queue - -### Resources - -- [celery rabbitmq tutorial](https://suzannewang.com/celery-rabbitmq-tutorial/) - -## docker-compose services configuration - -***NOTE: All of this assumes you have all docker services running via `make up` or `docker-compose up`*** - -> ~Provision grafana from `docker-compose.yml`. See: [Grafana provisioning example data source config file](https://grafana.com/docs/grafana/latest/administration/provisioning/#example-data-source-config-file)~ -> Found a nice blog post and accompanying GitHub repo to follow: -> -> - [Medium blog post](https://medium.com/swlh/easy-grafana-and-docker-compose-setup-d0f6f9fcec13) -> - [github.com/annea-ai/grafana-infrastructure]() -> - [Grafana docs on Provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/) - -## Monitoring Services - -- flower link - [http://localhost:5555](http://localhost:5555) -- prometheus link - [http://localhost:9090](http://localhost:9090) -- grafana link - [http://localhost:3000](http://localhost:3000) - -### Grafana configuration - -- flower+prometheus+grafana [add prometheus as a data source in grafana]( "flower+prometheus+grafana add prometheus as a data source in grafana") -- grafana link - [http://localhost:3000](http://localhost:3000) - -Add the prometheus url as: - -```bash -http://prometheus:9090 -``` - -Notice the tutorial mentions `http://localhost:9090`, but since this is running as a service using `docker-compose` the hostname changes to the -`prometheus` alias (this is the name of the service in the `docker-compose.yml` file) - ## Misc. Resources +- [Dash Extensions Enrich Module](https://www.dash-extensions.com/getting-started/enrich) - [docker-compose networking docs]() - [live mongoDB dash example]() - [plotly dash `dcc.Store` docs]() diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md new file mode 100644 index 00000000..4865de63 --- /dev/null +++ b/docs/TROUBLESHOOTING.md @@ -0,0 +1,39 @@ +# Troubleshooting + +## Celery, RabbitMQ task-queue + +### Resources + +- [celery rabbitmq tutorial](https://suzannewang.com/celery-rabbitmq-tutorial/) + +## docker-compose services configuration + +***NOTE: All of this assumes you have all docker services running via `make up` or `docker-compose up`*** + +> ~Provision grafana from `docker-compose.yml`. See: [Grafana provisioning example data source config file](https://grafana.com/docs/grafana/latest/administration/provisioning/#example-data-source-config-file)~ +> Found a nice blog post and accompanying GitHub repo to follow: +> +> - [Medium blog post](https://medium.com/swlh/easy-grafana-and-docker-compose-setup-d0f6f9fcec13) +> - [github.com/annea-ai/grafana-infrastructure]() +> - [Grafana docs on Provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/) + +### Monitoring Services + +- RabbitMQ management - [http://localhost:15672](http://localhost:15672) +- Flower link - [http://localhost:5555](http://localhost:5555) +- Prometheus link - [http://localhost:9090](http://localhost:9090) +- Grafana link - [http://localhost:3000](http://localhost:3000) + +### Grafana configuration + +- flower+prometheus+grafana [add prometheus as a data source in grafana]( "flower+prometheus+grafana add prometheus as a data source in grafana") +- grafana link - [http://localhost:3000](http://localhost:3000) + +Add the prometheus url as: + +```bash +http://prometheus:9090 +``` + +Notice the tutorial mentions `http://localhost:9090`, but since this is running as a service using `docker-compose` the hostname changes to the +`prometheus` alias (this is the name of the service in the `docker-compose.yml` file) From 2a83bfab639c778b1007be35b1ec407fa792d3f8 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Fri, 20 May 2022 11:31:23 -0400 Subject: [PATCH 28/37] :art::bulb: Add items to IDEAs --- docs/IDEA.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/IDEA.md b/docs/IDEA.md index e69de29b..da2f50d6 100644 --- a/docs/IDEA.md +++ b/docs/IDEA.md @@ -0,0 +1,5 @@ +## Random Ideas for potential incorporation to Automappa + +1. [Dash Mantine Components](https://www.dash-mantine-components.com/) +2. [Multipage web app using dash /pages](https://community.plotly.com/t/introducing-dash-pages-a-dash-2-x-feature-preview/57775#introducing-dash-pages-2) +3. [dashlabs.pythonanywhere.com](https://dashlabs.pythonanywhere.com/) From 90494cd889aa8cb175e2d72e3bfcb314c4df7cb5 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Tue, 2 Aug 2022 12:30:35 -0400 Subject: [PATCH 29/37] :whale::green_heart::bug: pin scipy to 1.8 :whale: Ignore .vscode for docker context :whale: Install mamba and prune prior to env update --- .dockerignore | 1 + Dockerfile | 3 ++- environment.yml | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index 7b9126c0..c5c7f20d 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,3 +3,4 @@ test Automappa.egg-info build dist +.vscode \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index aa6991ac..70885535 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,8 +1,9 @@ FROM condaforge/miniforge3:latest +RUN conda install --prune --name base mamba --yes + COPY environment.yml ./environment.yml -RUN conda install --prune --name base mamba --yes RUN mamba env update --name base --file=./environment.yml \ && mamba clean --all --force-pkgs-dirs --yes diff --git a/environment.yml b/environment.yml index f345d791..2f09bb8c 100644 --- a/environment.yml +++ b/environment.yml @@ -19,6 +19,7 @@ dependencies: - psycopg2 - python-dotenv - pydantic + - scipy==1.8.* - sqlalchemy - pip - pip: From 90b2990a297fe8e702a2634a50dc97be1d631cb4 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Tue, 2 Aug 2022 13:15:45 -0400 Subject: [PATCH 30/37] :art::whale::racehorse: Add dash-extensions :green_heart::racehorse: Add dash-extensions to enable caching :fire: Reduce kmer tasks to am_clr norm method and kmer_size of 5 (bhsne, densmap and umap embeddings) --- automappa/app.py | 17 +++++--- automappa/apps/home.py | 73 ++++---------------------------- automappa/apps/mag_refinement.py | 34 +++++++++------ automappa/apps/mag_summary.py | 4 +- automappa/conf/rabbitmq.conf | 5 +++ automappa/index.py | 2 +- automappa/tasks.py | 31 ++++++++++---- automappa/utils/figures.py | 15 ++++--- docker-compose.yml | 2 + environment.yml | 2 +- 10 files changed, 86 insertions(+), 99 deletions(-) create mode 100644 automappa/conf/rabbitmq.conf diff --git a/automappa/app.py b/automappa/app.py index 6910cacc..c9796b50 100755 --- a/automappa/app.py +++ b/automappa/app.py @@ -1,17 +1,22 @@ -import dash import dash_bootstrap_components as dbc import dash_uploader as du -from automappa.settings import server -from automappa.tasks import long_callback_manager +from dash_extensions.enrich import DashProxy +from flask_caching import Cache +from automappa.settings import server,celery +# from automappa.tasks import long_callback_manager -app = dash.Dash( +app = DashProxy( name=__name__, title="Automappa", external_stylesheets=[dbc.themes.LUX, dbc.icons.BOOTSTRAP], update_title="Automapping...", suppress_callback_exceptions=True, - long_callback_manager=long_callback_manager, + # long_callback_manager=long_callback_manager, ) - +cache = Cache(app.server, config={ + # try 'filesystem' if you don't want to setup redis + 'CACHE_TYPE': 'redis', + 'CACHE_REDIS_URL': celery.backend_url +}) du.configure_upload(app=app, folder=server.root_upload_folder) diff --git a/automappa/apps/home.py b/automappa/apps/home.py index acda0111..63b98db9 100644 --- a/automappa/apps/home.py +++ b/automappa/apps/home.py @@ -5,7 +5,7 @@ from dash import html, dcc from dash.dash_table import DataTable from dash.exceptions import PreventUpdate -from dash.dependencies import Input, Output, State +from dash_extensions.enrich import Input, Output, State import dash_bootstrap_components as dbc import pandas as pd import plotly.io as pio @@ -14,6 +14,7 @@ from automappa.app import app from automappa.tasks import ( + # get_task, preprocess_clusters_geom_medians, preprocess_embeddings, preprocess_marker_symbols, @@ -247,11 +248,6 @@ children="Refine MAGs", ) -kmer_embed_tasks_button = dbc.Button( - id="kmer-embed-tasks-button", - children="Submit k-mer embedding tasks", -) - tasks_table = html.Div(id="embedding-tasks") layout = dbc.Container( @@ -267,7 +263,6 @@ html.Br(), dbc.Row(selected_tables_datatable), html.Br(), - kmer_embed_tasks_button, dbc.Row(tasks_table), ], fluid=True, @@ -575,8 +570,10 @@ def on_refine_mags_button_click( ) if sample.metagenome: embedding_tasks = [] - kmer_sizes = set([kmer_table.size for kmer_table in sample.kmers]) - norm_methods = set([kmer_table.norm_method for kmer_table in sample.kmers]) + # kmer_sizes = set([kmer_table.size for kmer_table in sample.kmers]) + kmer_sizes = set([5]) + # norm_methods = set([kmer_table.norm_method for kmer_table in sample.kmers]) + norm_methods = set(["am_clr"]) embed_methods = set([kmer_table.embed_method for kmer_table in sample.kmers]) embed_methods = ["umap", "densmap", "bhsne"] for kmer_size, norm_method in itertools.product(kmer_sizes, norm_methods): @@ -591,6 +588,9 @@ def on_refine_mags_button_click( clusters_geom_medians_task = preprocess_clusters_geom_medians.delay( sample.binning.id, "cluster" ) + # df = pd.DataFrame([get_task(t.id) for t in [marker_symbols_task, clusters_geom_medians_task, *embedding_tasks]]) + # TODO: Monitor tasks progress with dcc.Interval in another callback... + # logger.debug(df) return sample.json() @@ -638,58 +638,3 @@ def selected_tables_datatable_children( {"id": "table_id", "name": "table_id", "editable": False}, ], ) - - -@app.callback( - Output("kmer-embed-tasks-button", "disabled"), - Input("metagenome-select", "value"), -) -def refine_mags_button_active_callback(metagenome_value): - if metagenome_value is None: - return True - else: - return False - - -# TODO: Store final embeddings-table and retrieve for 2d scatterplot axes dropdowns/views -# from automappa.utils.serializers import get_table -# embed_df = get_table(embed_table_name, index_col='contig') - - -@app.callback( - Output("embedding-tasks", "children"), - Input("kmer-embed-tasks-button", "n_clicks"), - Input("metagenome-select", "value"), -) -def on_compute_metagenome_kmer_embedding(btn_clicks: int, metagenome_select_value: str): - if btn_clicks is None or metagenome_select_value is None: - raise PreventUpdate - embed_methods = ["densmap", "umap", "bhsne"] - norm_method = "am_clr" - task = preprocess_embeddings( - metagenome_table=metagenome_select_value, - norm_method=norm_method, - embed_methods=embed_methods, - ) - embed_table_name = metagenome_select_value.replace("-metagenome", "-embeddings") - # TODO: Should create a polling or dcc.Interval(...) to construct this table for monitoring tasks status - df = pd.DataFrame( - [ - { - "task_name": task.name, - # "started": task.track_started, - "state": task.state, - "task_id": task.id, - "norm_method": norm_method, - "embed_methods": ",".join(embed_methods), - "embed-table-name": embed_table_name, - } - ] - ) - # logger.debug(df) - return DataTable( - data=df.to_dict("records"), - columns=[{"id": col, "name": col, "editable": False} for col in df.columns], - persistence=True, - persistence_type="session", - ) diff --git a/automappa/apps/mag_refinement.py b/automappa/apps/mag_refinement.py index a83120e3..b2d1cbf9 100644 --- a/automappa/apps/mag_refinement.py +++ b/automappa/apps/mag_refinement.py @@ -5,8 +5,6 @@ from pydantic import Json -from automappa.utils.models import SampleTables - numba_logger = logging.getLogger("numba") numba_logger.setLevel(logging.WARNING) h5py_logger = logging.getLogger("h5py") @@ -19,18 +17,18 @@ from dash import dcc, html from dash.dash_table import DataTable -from dash.dependencies import Input, Output, State from dash.exceptions import PreventUpdate -from dash_extensions import Download -from dash_extensions.snippets import send_data_frame +from dash_extensions.enrich import ServersideOutput,RedisStore,Input, Output, State from plotly import graph_objects as go import dash_bootstrap_components as dbc import plotly.io as pio -from automappa.app import app +from automappa.app import app,cache +from automappa.utils.models import SampleTables +from automappa import settings -from automappa.utils.serializers import get_table, table_to_db +from automappa.utils.serializers import table_to_db from automappa.utils.figures import ( format_axis_title, @@ -45,6 +43,7 @@ level=logging.DEBUG, ) logger = logging.getLogger(__name__) +backend = RedisStore(settings.celery.backend_url) pio.templates.default = "plotly_white" @@ -131,7 +130,7 @@ {"label": "GC%", "value": "gc_content"}, {"label": "Length", "value": "length"}, ], - value="coverage", + value="length", clearable=False, ), ] @@ -160,7 +159,7 @@ n_clicks=0, color="primary", ), - Download(id="refinements-download"), + dcc.Download(id="refinements-download"), ] # Summarize Refinements Button @@ -427,9 +426,11 @@ # 1. Only use Row and Col inside a Container. # 2. The immediate children of any Row component should always be Col components. # 3. Your content should go inside the Col components. +binning_store = dcc.Loading(dcc.Store("binning-store"), type='dot') layout = dbc.Container( children=[ + binning_store, dbc.Row([dbc.Col(mag_refinement_buttons)]), dbc.Row( [dbc.Col(scatterplot_2d, width=9), dbc.Col(mag_metrics_table, width=3)] @@ -453,6 +454,12 @@ # CALLBACKS # ###################################################################### +# @cache.memoize(timeout=3600) +@app.callback(ServersideOutput("binning-store", "data", backend=backend), Input("selected-tables-store", "data"), memoize=True) +def query_binning_in_db(selected_tables_data: SampleTables): + sample = SampleTables.parse_raw(selected_tables_data) + return sample.binning.table.reset_index().to_json('records') + @app.callback( Output("settings-offcanvas", "is_open"), @@ -748,6 +755,7 @@ def taxonomy_distribution_figure_callback( Output("scatterplot-3d", "figure"), [ Input("selected-tables-store", "data"), + Input("axes-2d", "value"), Input("scatterplot-3d-zaxis-dropdown", "value"), Input("scatterplot-3d-legend-toggle", "value"), Input("color-by-column", "value"), @@ -756,6 +764,7 @@ def taxonomy_distribution_figure_callback( ) def scatterplot_3d_figure_callback( selected_tables_data: SampleTables, + axes_columns: str, z_axis: str, show_legend: bool, color_by_col: str, @@ -777,10 +786,11 @@ def scatterplot_3d_figure_callback( # Other possible categorical columns all relate to taxonomy df[color_by_col] = df[color_by_col].fillna("unclassified") + x_axis, y_axis = axes_columns.split("|") fig = get_scatterplot_3d( df=df, - x_axis="x_1", - y_axis="x_2", + x_axis=x_axis, + y_axis=y_axis, z_axis=z_axis, color_by_col=color_by_col, ) @@ -884,7 +894,7 @@ def download_refinements( if not n_clicks: raise PreventUpdate sample = SampleTables.parse_raw(selected_tables_data) - return send_data_frame( + return dcc.send_data_frame( sample.refinements.table.to_csv, "refinements.csv", index=False ) diff --git a/automappa/apps/mag_summary.py b/automappa/apps/mag_summary.py index 42b3601c..5123de37 100644 --- a/automappa/apps/mag_summary.py +++ b/automappa/apps/mag_summary.py @@ -4,7 +4,7 @@ from dash import dcc, html from dash.dash_table import DataTable -from dash.dependencies import Input, Output +from dash_extensions.enrich import Input, Output import dash_bootstrap_components as dbc from plotly import graph_objects as go import plotly.io as pio @@ -303,6 +303,8 @@ def mag_summary_cluster_col_dropdown_options_callback( def mag_summary_stats_datatable_callback( selected_tables_data: SampleTables, cluster_col: str ) -> DataTable: + if cluster_col is None: + raise PreventUpdate sample = SampleTables.parse_raw(selected_tables_data) stats_df = get_metabin_stats_summary( binning_table=sample.binning.id, diff --git a/automappa/conf/rabbitmq.conf b/automappa/conf/rabbitmq.conf new file mode 100644 index 00000000..3faeaae0 --- /dev/null +++ b/automappa/conf/rabbitmq.conf @@ -0,0 +1,5 @@ +# Default is 30 mins... +# 1 hour in millisecs +# consumer_timeout = 3600000 +# 4 hours in millisecs +consumer_timeout = 14400000 diff --git a/automappa/index.py b/automappa/index.py index 1145863b..6261d321 100755 --- a/automappa/index.py +++ b/automappa/index.py @@ -4,7 +4,7 @@ import argparse import logging -from dash.dependencies import Input, Output +from dash_extensions.enrich import Input, Output from dash import dcc, html import dash_bootstrap_components as dbc diff --git a/automappa/tasks.py b/automappa/tasks.py index 06ff8d6c..d353bc4f 100644 --- a/automappa/tasks.py +++ b/automappa/tasks.py @@ -1,7 +1,7 @@ #!/usr/bin/env python -import random +# import random import tempfile # import time @@ -14,7 +14,7 @@ from celery import Celery, group from celery.utils.log import get_task_logger from celery.result import AsyncResult -from dash.long_callback import CeleryLongCallbackManager +# from dash.long_callback import CeleryLongCallbackManager from Bio import SeqIO @@ -35,8 +35,9 @@ queue = Celery( __name__, backend=settings.celery.backend_url, broker=settings.celery.broker_url ) + queue.config_from_object("automappa.conf.celeryconfig") -long_callback_manager = CeleryLongCallbackManager(queue) +# long_callback_manager = CeleryLongCallbackManager(queue) logger = get_task_logger(__name__) if settings.server.debug: @@ -52,6 +53,21 @@ def get_job(job_id): """ return AsyncResult(job_id, app=queue) +def get_task(job_id) -> dict[str,str]: + """ + To be called from automappa web app. + The job ID is passed and the celery job is returned. + """ + task = AsyncResult(job_id, app=queue) + task_attrs = { + "task_name": task.name, + "state": task.state, + "task_id": task.id, + } + if hasattr(task, "track_started"): + task_attrs.update({"started": task.track_started}) + return task_attrs + @queue.task(bind=True) def preprocess_marker_symbols(self, binning_table: str, markers_table: str) -> str: @@ -79,7 +95,7 @@ def count_kmer(self, metagenome_table: str, size: int = 5, cpus: int = None) -> records = get_metagenome_seqrecords(metagenome_table) # Uncomment next line to speed-up debugging... # FIXME: Comment out below: - records = random.sample(records, k=1_000) + # records = random.sample(records, k=1_000) with tempfile.NamedTemporaryFile(mode="w") as tmp: SeqIO.write(records, tmp.name, "fasta") tmp.seek(0) @@ -127,9 +143,8 @@ def embed_kmer( norm_df, method=embed_method, embed_dimensions=embed_dims, - # FIXME: add in below lines when autometa 2.1 available... - # n_jobs=n_jobs, - # **method_kwargs, + n_jobs=n_jobs, + **method_kwargs, ).rename( columns={ "x_1": f"{prev_kmer_params}-{embed_method}_x_1", @@ -176,7 +191,7 @@ def preprocess_embeddings( # time.sleep(1) # with open('graph.dot', 'w') as fh: # result.parent.parent.parent.graph.to_dot(fh) - logger.debug(result) + logger.debug(f"k-mer pipeline result: {result}") return result diff --git a/automappa/utils/figures.py b/automappa/utils/figures.py index 64af8c37..72a1172d 100644 --- a/automappa/utils/figures.py +++ b/automappa/utils/figures.py @@ -346,12 +346,15 @@ def get_scatterplot_3d( go.Figure _description_ """ + x_axis_title = format_axis_title(x_axis) + y_axis_title = format_axis_title(y_axis) + z_axis_title = format_axis_title(z_axis) fig = go.Figure( layout=go.Layout( scene=dict( - xaxis=dict(title=x_axis.title()), - yaxis=dict(title=y_axis.title()), - zaxis=dict(title=z_axis.replace("_", " ").title()), + xaxis=dict(title=x_axis_title), + yaxis=dict(title=y_axis_title), + zaxis=dict(title=z_axis_title), ), legend={"x": 1, "y": 1}, autosize=True, @@ -359,9 +362,9 @@ def get_scatterplot_3d( hovermode="closest", ) ) - x_hover_label = f"{x_axis.title()}: " + "%{x:.2f}" - y_hover_label = f"{y_axis.title()}: " + "%{y:.2f}" - z_hover_label = f"{z_axis.title()}: " + "%{z:.2f}" + x_hover_label = f"{x_axis_title}: " + "%{x:.2f}" + y_hover_label = f"{y_axis_title}: " + "%{y:.2f}" + z_hover_label = f"{z_axis_title}: " + "%{z:.2f}" text_hover_label = "Contig: %{text}" hovertemplate = "
".join( [text_hover_label, z_hover_label, x_hover_label, y_hover_label] diff --git a/docker-compose.yml b/docker-compose.yml index 398e8552..2a609a86 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,6 +23,8 @@ services: - 15672:15672 env_file: - .env + volumes: + - ./automappa/conf/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf celery: build: diff --git a/environment.yml b/environment.yml index 2f09bb8c..69805506 100644 --- a/environment.yml +++ b/environment.yml @@ -9,7 +9,6 @@ dependencies: - dash - dash-bootstrap-components - dash-daq - - dash-extensions - flask - flower - msgpack-python @@ -23,6 +22,7 @@ dependencies: - sqlalchemy - pip - pip: + - dash-extensions - celery[redis] - geom-median - dash-uploader From df22b6eda85a4500f1b220f04f7ba6d4352daa3b Mon Sep 17 00:00:00 2001 From: Evan Rees <25933122+WiscEvan@users.noreply.github.com> Date: Wed, 3 Aug 2022 11:06:23 -0400 Subject: [PATCH 31/37] Update .env Turn off debug mode by default --- .env | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.env b/.env index 5adf2d79..64e1b1e7 100644 --- a/.env +++ b/.env @@ -2,7 +2,8 @@ SERVER_ROOT_UPLOAD_FOLDER="${HOME}/.automappa/uploads" SERVER_HOST="0.0.0.0" SERVER_PORT=8050 -SERVER_DEBUG=True +# By, default remove debugging tools --> For development switch this to `True` +SERVER_DEBUG=False POSTGRES_USER="admin" POSTGRES_PASSWORD="mypass" POSTGRES_DB="automappa" From ebaccf449f3ea79f9a73495d991bd8e7fdb97eb3 Mon Sep 17 00:00:00 2001 From: Evan Rees <25933122+WiscEvan@users.noreply.github.com> Date: Wed, 3 Aug 2022 11:09:46 -0400 Subject: [PATCH 32/37] Update README.md :fire: Change clone of `home-tab` branch to `develop` branch --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f15ed46c..2fadbe27 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ ### clone the Automappa Repository ```bash -git clone -b home-tab https://github.com/WiscEvan/Automappa +git clone -b develop https://github.com/WiscEvan/Automappa ``` ### build images for automappa services From be091f9c284cba54fa8ddac11db6a1863b39a8d5 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Wed, 3 Aug 2022 11:34:59 -0400 Subject: [PATCH 33/37] :memo: Add documentation for using test data --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 2fadbe27..f7b30c85 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,15 @@ NOTE: you can skip `make build` if you’d like, as this command will build and make up ``` +> NOTE: If your computer is already using most of its resources, you may need to close +some applications so docker may construct all of the necessary Automappa services + ### Navigate to Automappa page Once you see `automappa_web_1` running from the terminal logs, you should be able to navigate to 🥳 + +Test data to try out Automappa may be downloaded from here: + +This data is not yet binned, so you can easily try out different settings and perform your own refinements on some example data. + +Happy binning! From 27fea70a2d600d5dbed4e623770a4cf08217a391 Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Wed, 3 Aug 2022 11:52:27 -0400 Subject: [PATCH 34/37] :memo::fire: Update README, remove autometa-lite mention --- README.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.md b/README.md index 54c62507..c58d709d 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,6 @@ ## Automappa :deciduous_tree: -***Follow this link to get started using the fully-featured Automappa application:*** https://github.com/WiscEvan/Automappa/tree/develop#getting-started - ## Test Data Test data to try out Automappa may be downloaded from here: https://drive.google.com/drive/folders/1nBk0AZC3EJV4t-9KdJBShGCfWbdP2kOp?usp=sharing @@ -24,12 +22,6 @@ This data is not yet binned, so you can easily try out different settings and pe Happy binning! ------ - -## Automappa-lite :seedling: - -> NOTE: The following section is a lightweight version of Automappa that may be slow and is not constructed for larger datasets. -> If you would like to use all of Automappa's features see the [Automappa section](#automappa) above or navigate to the [getting started page](https://github.com/WiscEvan/Automappa/tree/develop#getting-started) for the full-featured app. - [Clone the Automappa Repo](#clone-the-repository) - [Run `make build` using Makefile](#build-images-for-automappa-services) From bc82dd4bbf34399834572cf5ced74615df88aace Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Wed, 3 Aug 2022 11:55:10 -0400 Subject: [PATCH 35/37] :memo::fire: Remove automappa-lite details --- README.md | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index c58d709d..9ed67759 100644 --- a/README.md +++ b/README.md @@ -12,24 +12,13 @@ ![automappa_demo_920](https://user-images.githubusercontent.com/25933122/158899748-bf21c1fc-6f67-4fd8-af89-4e732fa2edcd.gif) -## Automappa :deciduous_tree: - -## Test Data - -Test data to try out Automappa may be downloaded from here: https://drive.google.com/drive/folders/1nBk0AZC3EJV4t-9KdJBShGCfWbdP2kOp?usp=sharing - -This data is not yet binned, so you can easily try out different settings and perform your own refinements on some example data. - -Happy binning! - +## :deciduous_tree: Automappa testing setup/run commands - [Clone the Automappa Repo](#clone-the-repository) - [Run `make build` using Makefile](#build-images-for-automappa-services) - [Run `make up` using Makefile](#build-and-run-automappa-services) - [Open the Automappa url](#navigate-to-automappa-page) -## Clone the repository - -## Automappa testing setup/run commands +- [Download test data](#download-test-data) ### clone the Automappa Repository @@ -58,6 +47,8 @@ some applications so docker may construct all of the necessary Automappa service Once you see `automappa_web_1` running from the terminal logs, you should be able to navigate to 🥳 +### Download Test Data + Test data to try out Automappa may be downloaded from here: This data is not yet binned, so you can easily try out different settings and perform your own refinements on some example data. From 763ec7c73cbc74ec8af2877ab135c787fd4fdeb0 Mon Sep 17 00:00:00 2001 From: Evan Rees <25933122+WiscEvan@users.noreply.github.com> Date: Fri, 21 Jul 2023 16:04:02 -0400 Subject: [PATCH 36/37] Refactor with fixes, features and improved directory structure (#39) * :art: Apply black formatting * :art::arrow_up::cowboy_hat_face: refactor to use dash-extensions DashBlueprints * Significantly simplified code base with modular components that are each passed their own page's app (DashBlueprint) * components and layouts are standardized to use render function that may receive the app with or without arguments * :fire: Remove (now) unnecessary index.py * :arrow_up: Pin dash-extensions to first major release * :arrow_up: Pin dash to 2.10 * :arrow_up: Add dash-mantine-components * :arrow_up: Add dash-iconify * :memo: Add contributing docs * :art::bug::memo::whale: Fix docs, prop id and docker specs * :whale: Add quotes around ports per docker-compose spec * :memo::art::fire: add Development resources :art::fire: Add icons to not found page and remove unnecessary text * :art: Refactor mag refinement page components * :art: Refactor marker symbols legend to use icons rather than naming shapes * :art: Restyle save selections and settings buttons * :art: Refactor mag-refinement offsettings canvas components * :art: Add minimum example contig_cytoscape component * :art: Add specific backend configurations * :art: Add REDIS_BACKEND details to .env * :art: Add specific backend params to Serverside(..., backend=...) from automappa.data.db import {redis,file_system}_backend * :memo::fire: Refactor dash components to DashProxy components * :arrow_up: pin dash-mantine-components to 0.12 in env using pip rather than conda * :art: Add icons in page registry with blueprints * :art: Add coverage range slider component * :art: Add Refinement model to track contig cluster refinements * :art: Implement data source to connect MAG refinements table * :art: Add methods to get latest user refinements contig headers * :art: Add interaction b/w coverage range slider and scatterplot2d * :art: Connect download refinements button to data source * :art: Hide MAG refinements switch is now disabled when there are no user refinements that have been made * :art: Refinements table has been styled with new timestamp string and removed 'initial_cluster' column * :art: Refinements table now only retrieves user refinements (i.e. excludes initial clusters) * :art: Add refinements clear button * Add working data source for completeness/purity boxplot * :art: Add minimal working AgGrid for MAG Summary * :art: Replace dbc.Offcanvas with dmc.Drawer * :art: Replace many of the Dropdowns in Offcanvas with RadioGroup Radio options (less clicks for the user) * :carrot::rabbit: Add logic for modular task-queue * :memo: Add documentation for tasks in CONTRIBUTING and reference tasks README.md * :carrot: Add minimum working example for task submission, progress display for a task status badge * :carrot: Add task discovery for home page task in celeryconfig.py * :whale::snake: Replace base image of miniforge with mambaforge * :carrot::art: Notification system tells user of current state of tasks processing * :bug::art: Move dcc.Store(...) within respective du.Upload component * :art: Add tasks store for getting tasks statuses * :rabbit::carrot: Add tasks in home for handling sample data ingestion * :art: Add mag length sum computations * :art: Add MIMAG cluster count computations * :art: Add metadata to sample data card info * :bug: Replace na values from cluster col during contig loader pre-processing * :art::fire: Add sample removal feature * :art: Affix settings and save mag buttons --- .dockerignore | 3 +- .env | 8 +- .gitignore | 6 +- Dockerfile | 21 +- Makefile | 14 +- automappa/Procfile | 3 +- automappa/__main__.py | 59 +- automappa/app.py | 29 +- automappa/apps/home.py | 640 --------- automappa/apps/mag_refinement.py | 938 ------------- automappa/apps/mag_summary.py | 470 ------- automappa/{apps => components}/__init__.py | 0 automappa/components/ids.py | 130 ++ automappa/components/layout.py | 94 ++ automappa/components/metagenome_id_store.py | 45 + automappa/components/pages_navbar.py | 57 + automappa/components/samples_store.py | 84 ++ automappa/components/selected_tables_store.py | 107 ++ automappa/components/tasks_store.py | 15 + automappa/conf/celeryconfig.py | 2 + automappa/data/__init__.py | 0 automappa/data/database.py | 44 + automappa/data/loader.py | 513 +++++++ automappa/data/models.py | 94 ++ automappa/data/schemas.py | 47 + automappa/db.py | 24 - automappa/index.py | 146 -- automappa/pages/__init__.py | 0 automappa/pages/home/__init__.py | 0 automappa/pages/home/components/__init__.py | 0 .../pages/home/components/binning_select.py | 52 + .../pages/home/components/binning_upload.py | 61 + .../cytoscape_connections_select.py | 55 + .../cytoscape_connections_upload.py | 60 + .../pages/home/components/markers_select.py | 57 + .../pages/home/components/markers_upload.py | 62 + .../home/components/metagenome_select.py | 52 + .../home/components/metagenome_upload.py | 63 + .../home/components/refine_mags_button.py | 39 + .../pages/home/components/sample_card.py | 260 ++++ .../pages/home/components/sample_cards.py | 239 ++++ .../home/components/sample_name_text_input.py | 61 + .../home/components/samples_datatable.py | 56 + .../components/selected_tables_datatable.py | 66 + .../home/components/task_status_badge.py | 93 ++ .../pages/home/components/tasks_table.py | 10 + .../pages/home/components/upload_modal.py | 66 + .../home/components/upload_modal_button.py | 38 + .../pages/home/components/upload_stepper.py | 383 +++++ automappa/pages/home/layout.py | 35 + automappa/pages/home/source.py | 312 +++++ automappa/pages/home/tasks/README.md | 100 ++ automappa/pages/home/tasks/__init__.py | 17 + automappa/pages/home/tasks/sample_cards.py | 129 ++ .../pages/home/tasks/task_status_badge.py | 14 + automappa/pages/mag_refinement/__init__.py | 0 .../mag_refinement/components/__init__.py | 0 .../binning_refinements_clear_button.py | 67 + .../binning_refinements_download_button.py | 66 + .../binning_refinements_summary_button.py | 21 + .../components/color_by_col_dropdown.py | 30 + .../components/contig_cytoscape.py | 116 ++ .../components/coverage_range_slider.py | 53 + .../components/hide_selections_switch.py | 48 + .../components/kmer_size_dropdown.py | 19 + .../components/mag_metrics_table.py | 116 ++ .../mag_refinement_coverage_boxplot.py | 56 + .../mag_refinement_gc_content_boxplot.py | 58 + .../mag_refinement_length_boxplot.py | 60 + .../components/marker_symbols_legend.py | 109 ++ .../components/norm_method_dropdown.py | 22 + .../components/refinements_table.py | 67 + .../components/save_selection_button.py | 73 + .../components/scatterplot_2d.py | 185 +++ .../scatterplot_2d_axes_dropdown.py | 34 + .../scatterplot_2d_legend_toggle.py | 23 + .../components/scatterplot_3d.py | 143 ++ .../scatterplot_3d_legend_toggle.py | 24 + .../scatterplot_3d_zaxis_dropdown.py | 35 + .../components/settings_button.py | 25 + .../components/settings_offcanvas.py | 103 ++ .../components/taxa_rank_dropdown.py | 33 + .../components/taxonomy_distribution.py | 67 + automappa/pages/mag_refinement/layout.py | 94 ++ automappa/pages/mag_refinement/source.py | 705 ++++++++++ automappa/pages/mag_summary/__init__.py | 0 .../pages/mag_summary/components/__init__.py | 0 .../components/mag_coverage_boxplot.py | 46 + .../components/mag_gc_content_boxplot.py | 47 + .../components/mag_length_boxplot.py | 49 + .../components/mag_metrics_barplot.py | 47 + .../mag_overview_coverage_boxplot.py | 43 + .../mag_overview_gc_content_boxplot.py | 44 + .../components/mag_overview_length_boxplot.py | 42 + .../mag_overview_metrics_boxplot.py | 39 + .../components/mag_selection_dropdown.py | 38 + .../components/mag_summary_stats_datatable.py | 123 ++ .../components/mag_taxonomy_sankey.py | 40 + automappa/pages/mag_summary/layout.py | 56 + automappa/pages/mag_summary/source.py | 258 ++++ automappa/pages/not_found_404.py | 197 +++ automappa/settings.py | 15 +- automappa/tasks.py | 320 +---- automappa/utils/figures.py | 136 +- automappa/utils/kmers.py | 59 - automappa/utils/models.py | 265 ---- automappa/utils/serializers.py | 361 ----- automappa/utils/tests/test_models.py | 26 - docker-compose.yml | 73 +- docs/CONTRIBUTING.md | 1238 +++++++++++++++++ docs/IDEA.md | 7 +- docs/TODO.md | 152 +- docs/data.md | 54 + environment.yml | 20 +- requirements-dev.txt | 3 + scripts/test_environment.py | 14 - setup.py | 2 +- 117 files changed, 8805 insertions(+), 3434 deletions(-) delete mode 100644 automappa/apps/home.py delete mode 100644 automappa/apps/mag_refinement.py delete mode 100644 automappa/apps/mag_summary.py rename automappa/{apps => components}/__init__.py (100%) create mode 100644 automappa/components/ids.py create mode 100644 automappa/components/layout.py create mode 100644 automappa/components/metagenome_id_store.py create mode 100644 automappa/components/pages_navbar.py create mode 100644 automappa/components/samples_store.py create mode 100644 automappa/components/selected_tables_store.py create mode 100644 automappa/components/tasks_store.py create mode 100644 automappa/data/__init__.py create mode 100644 automappa/data/database.py create mode 100644 automappa/data/loader.py create mode 100644 automappa/data/models.py create mode 100644 automappa/data/schemas.py delete mode 100644 automappa/db.py delete mode 100755 automappa/index.py create mode 100644 automappa/pages/__init__.py create mode 100644 automappa/pages/home/__init__.py create mode 100644 automappa/pages/home/components/__init__.py create mode 100644 automappa/pages/home/components/binning_select.py create mode 100644 automappa/pages/home/components/binning_upload.py create mode 100644 automappa/pages/home/components/cytoscape_connections_select.py create mode 100644 automappa/pages/home/components/cytoscape_connections_upload.py create mode 100644 automappa/pages/home/components/markers_select.py create mode 100644 automappa/pages/home/components/markers_upload.py create mode 100644 automappa/pages/home/components/metagenome_select.py create mode 100644 automappa/pages/home/components/metagenome_upload.py create mode 100644 automappa/pages/home/components/refine_mags_button.py create mode 100644 automappa/pages/home/components/sample_card.py create mode 100644 automappa/pages/home/components/sample_cards.py create mode 100644 automappa/pages/home/components/sample_name_text_input.py create mode 100644 automappa/pages/home/components/samples_datatable.py create mode 100644 automappa/pages/home/components/selected_tables_datatable.py create mode 100644 automappa/pages/home/components/task_status_badge.py create mode 100644 automappa/pages/home/components/tasks_table.py create mode 100644 automappa/pages/home/components/upload_modal.py create mode 100644 automappa/pages/home/components/upload_modal_button.py create mode 100644 automappa/pages/home/components/upload_stepper.py create mode 100644 automappa/pages/home/layout.py create mode 100644 automappa/pages/home/source.py create mode 100644 automappa/pages/home/tasks/README.md create mode 100644 automappa/pages/home/tasks/__init__.py create mode 100644 automappa/pages/home/tasks/sample_cards.py create mode 100644 automappa/pages/home/tasks/task_status_badge.py create mode 100644 automappa/pages/mag_refinement/__init__.py create mode 100644 automappa/pages/mag_refinement/components/__init__.py create mode 100644 automappa/pages/mag_refinement/components/binning_refinements_clear_button.py create mode 100644 automappa/pages/mag_refinement/components/binning_refinements_download_button.py create mode 100644 automappa/pages/mag_refinement/components/binning_refinements_summary_button.py create mode 100644 automappa/pages/mag_refinement/components/color_by_col_dropdown.py create mode 100644 automappa/pages/mag_refinement/components/contig_cytoscape.py create mode 100644 automappa/pages/mag_refinement/components/coverage_range_slider.py create mode 100644 automappa/pages/mag_refinement/components/hide_selections_switch.py create mode 100644 automappa/pages/mag_refinement/components/kmer_size_dropdown.py create mode 100644 automappa/pages/mag_refinement/components/mag_metrics_table.py create mode 100644 automappa/pages/mag_refinement/components/mag_refinement_coverage_boxplot.py create mode 100644 automappa/pages/mag_refinement/components/mag_refinement_gc_content_boxplot.py create mode 100644 automappa/pages/mag_refinement/components/mag_refinement_length_boxplot.py create mode 100644 automappa/pages/mag_refinement/components/marker_symbols_legend.py create mode 100644 automappa/pages/mag_refinement/components/norm_method_dropdown.py create mode 100644 automappa/pages/mag_refinement/components/refinements_table.py create mode 100644 automappa/pages/mag_refinement/components/save_selection_button.py create mode 100644 automappa/pages/mag_refinement/components/scatterplot_2d.py create mode 100644 automappa/pages/mag_refinement/components/scatterplot_2d_axes_dropdown.py create mode 100644 automappa/pages/mag_refinement/components/scatterplot_2d_legend_toggle.py create mode 100644 automappa/pages/mag_refinement/components/scatterplot_3d.py create mode 100644 automappa/pages/mag_refinement/components/scatterplot_3d_legend_toggle.py create mode 100644 automappa/pages/mag_refinement/components/scatterplot_3d_zaxis_dropdown.py create mode 100644 automappa/pages/mag_refinement/components/settings_button.py create mode 100644 automappa/pages/mag_refinement/components/settings_offcanvas.py create mode 100644 automappa/pages/mag_refinement/components/taxa_rank_dropdown.py create mode 100644 automappa/pages/mag_refinement/components/taxonomy_distribution.py create mode 100644 automappa/pages/mag_refinement/layout.py create mode 100644 automappa/pages/mag_refinement/source.py create mode 100644 automappa/pages/mag_summary/__init__.py create mode 100644 automappa/pages/mag_summary/components/__init__.py create mode 100644 automappa/pages/mag_summary/components/mag_coverage_boxplot.py create mode 100644 automappa/pages/mag_summary/components/mag_gc_content_boxplot.py create mode 100644 automappa/pages/mag_summary/components/mag_length_boxplot.py create mode 100644 automappa/pages/mag_summary/components/mag_metrics_barplot.py create mode 100644 automappa/pages/mag_summary/components/mag_overview_coverage_boxplot.py create mode 100644 automappa/pages/mag_summary/components/mag_overview_gc_content_boxplot.py create mode 100644 automappa/pages/mag_summary/components/mag_overview_length_boxplot.py create mode 100644 automappa/pages/mag_summary/components/mag_overview_metrics_boxplot.py create mode 100644 automappa/pages/mag_summary/components/mag_selection_dropdown.py create mode 100644 automappa/pages/mag_summary/components/mag_summary_stats_datatable.py create mode 100644 automappa/pages/mag_summary/components/mag_taxonomy_sankey.py create mode 100644 automappa/pages/mag_summary/layout.py create mode 100644 automappa/pages/mag_summary/source.py create mode 100644 automappa/pages/not_found_404.py delete mode 100644 automappa/utils/kmers.py delete mode 100644 automappa/utils/models.py delete mode 100644 automappa/utils/serializers.py delete mode 100644 automappa/utils/tests/test_models.py create mode 100644 docs/CONTRIBUTING.md create mode 100644 docs/data.md create mode 100644 requirements-dev.txt delete mode 100644 scripts/test_environment.py diff --git a/.dockerignore b/.dockerignore index c5c7f20d..8e5d4beb 100644 --- a/.dockerignore +++ b/.dockerignore @@ -3,4 +3,5 @@ test Automappa.egg-info build dist -.vscode \ No newline at end of file +.vscode +.pytest_cache \ No newline at end of file diff --git a/.env b/.env index 64e1b1e7..c7bccb0a 100644 --- a/.env +++ b/.env @@ -1,8 +1,8 @@ # Required for docker-compose.yml -SERVER_ROOT_UPLOAD_FOLDER="${HOME}/.automappa/uploads" +SERVER_ROOT_UPLOAD_FOLDER="/usr/src/app/uploads" SERVER_HOST="0.0.0.0" SERVER_PORT=8050 -# By, default remove debugging tools --> For development switch this to `True` +# By default remove debugging tools --> For development switch this to `True` SERVER_DEBUG=False POSTGRES_USER="admin" POSTGRES_PASSWORD="mypass" @@ -13,6 +13,10 @@ POSTGRES_POOL_PRE_PING=False RABBITMQ_DEFAULT_USER="user" RABBITMQ_DEFAULT_PASS="pass" RABBITMQ_URL="amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672/" +REDIS_BACKEND_HOST='redis' +REDIS_BACKEND_PORT='6379' +REDIS_BACKEND_DB='0' +REDIS_BACKEND_PASSWORD='RedisPassword' CELERY_BACKEND_URL='redis://redis:6379/0' CELERY_BROKER_URL="amqp://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:5672//" FLOWER_BROKER_API_URL="http://${RABBITMQ_DEFAULT_USER}:${RABBITMQ_DEFAULT_PASS}@rabbitmq:15672/api" diff --git a/.gitignore b/.gitignore index eb4e08d5..10ae6932 100644 --- a/.gitignore +++ b/.gitignore @@ -11,4 +11,8 @@ build Automappa.egg-info .env data -db-data \ No newline at end of file +!automappa/data +db-data +file_system_backend +*.db +uploads \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 70885535..3ad7bab3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,17 @@ -FROM condaforge/miniforge3:latest +FROM condaforge/mambaforge:latest -RUN conda install --prune --name base mamba --yes - -COPY environment.yml ./environment.yml - -RUN mamba env update --name base --file=./environment.yml \ - && mamba clean --all --force-pkgs-dirs --yes +COPY environment.yml /tmp/environment.yml +RUN mamba env update -n base -f /tmp/environment.yml && \ + mamba clean --all --force-pkgs-dirs --yes COPY . /usr/src/app WORKDIR /usr/src/app + RUN python -m pip install . --ignore-installed --no-deps -vvv -# Test command is functional -RUN automappa -h -# Create an unprivileged user for running our Python code. +# Create an unprivileged user for automappa celery worker RUN adduser --disabled-password --gecos '' automappa +RUN mkdir -p /usr/src/app/uploads && \ + chown -R automappa:automappa /usr/src/app -# CMD [ "-h" ] -# ENTRYPOINT [ "automappa" ] \ No newline at end of file +# CMD ["automappa", "-h"] \ No newline at end of file diff --git a/Makefile b/Makefile index bc792276..1271cf4c 100644 --- a/Makefile +++ b/Makefile @@ -45,25 +45,31 @@ endif image: Dockerfile docker build . -f $< -t evanrees/automappa:`git branch --show-current` +## Remove automappa-{web,flower,queue} docker images +rm-images: Dockerfile + docker rmi -f `docker images -q automappa-web` + docker rmi -f `docker images -q automappa-queue` + docker rmi -f `docker images -q automappa-flower` + ## Install automappa entrypoint into current environment install: $(PYTHON_INTERPRETER) -m pip install . --ignore-installed --no-deps -vvv ## docker compose build from docker-compose.yml build: docker-compose.yml - docker-compose build + docker compose build ## alias for docker-compose up --always-recreate-deps --remove-orphans --force-recreate up: docker-compose.yml - docker-compose up --always-recreate-deps --remove-orphans --force-recreate + docker compose up --always-recreate-deps --remove-orphans --force-recreate ## alias for docker-compose down --remove-orphans down: docker-compose.yml - docker-compose down --remove-orphans + docker compose down --remove-orphans ## alias for docker-compose down --remove-orphans --volumes down-v: docker-compose.yml - docker-compose down --remove-orphans -v + docker compose down --remove-orphans -v # Run Automappa on test data # test: test_data diff --git a/automappa/Procfile b/automappa/Procfile index 41135e6a..986d06fe 100644 --- a/automappa/Procfile +++ b/automappa/Procfile @@ -1 +1,2 @@ -web: gunicorn index:server \ No newline at end of file +web: gunicorn app:server --workers 4 +queue: celery --concurrency=2 --app=automappa.tasks.queue worker --loglevel=INFO --uid automappa -E \ No newline at end of file diff --git a/automappa/__main__.py b/automappa/__main__.py index b8fcab02..c5fdc9ec 100644 --- a/automappa/__main__.py +++ b/automappa/__main__.py @@ -1,11 +1,64 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from automappa import index +import argparse +import logging +from automappa import settings +from automappa.components import layout +from automappa.data.database import create_db_and_tables +from automappa.app import app -def main(): - index.main() +logging.basicConfig( + format="[%(levelname)s] %(name)s: %(message)s", + level=logging.DEBUG, +) + +logger = logging.getLogger(__name__) +numba_logger = logging.getLogger("numba") +numba_logger.setLevel(logging.WARNING) +numba_logger.propagate = False +h5py_logger = logging.getLogger("h5py") +h5py_logger.setLevel(logging.WARNING) +h5py_logger.propagate = False +root_logger = logging.getLogger() +root_logger.setLevel(logging.WARNING) + + +def main() -> None: + parser = argparse.ArgumentParser( + description="Automappa: An interactive interface for exploration of metagenomes", + formatter_class=argparse.RawTextHelpFormatter, + ) + parser.add_argument( + "--storage-type", + help=( + "The type of the web storage. (default: %(default)s)\n" + "- memory: only kept in memory, reset on page refresh.\n" + "- session: data is cleared once the browser quit.\n" + "- local: data is kept after the browser quit.\n" + ), + choices=["memory", "session", "local"], + default="session", + ) + parser.add_argument( + "--clear-store-data", + help=( + "Clear storage data (default: %(default)s)\n" + "(only required if using 'session' or 'local' for `--storage-type`)" + ), + action="store_true", + default=False, + ) + args = parser.parse_args() + + create_db_and_tables() + app.layout = layout.render(app, args.storage_type, args.clear_store_data) + app.run( + host=settings.server.host, + port=settings.server.port, + debug=settings.server.debug, + ) if __name__ == "__main__": diff --git a/automappa/app.py b/automappa/app.py index c9796b50..ee2fa92c 100755 --- a/automappa/app.py +++ b/automappa/app.py @@ -1,10 +1,13 @@ import dash_bootstrap_components as dbc + +from dash_extensions.enrich import ( + DashProxy, + ServersideOutputTransform, +) import dash_uploader as du +from automappa.data.database import redis_backend, file_system_backend +from automappa import settings -from dash_extensions.enrich import DashProxy -from flask_caching import Cache -from automappa.settings import server,celery -# from automappa.tasks import long_callback_manager app = DashProxy( name=__name__, @@ -12,11 +15,15 @@ external_stylesheets=[dbc.themes.LUX, dbc.icons.BOOTSTRAP], update_title="Automapping...", suppress_callback_exceptions=True, - # long_callback_manager=long_callback_manager, + prevent_initial_callbacks=False, + use_pages=True, + pages_folder="", + transforms=[ + ServersideOutputTransform( + default_backend=[file_system_backend], + backends=[redis_backend, file_system_backend], + ), + ], ) -cache = Cache(app.server, config={ - # try 'filesystem' if you don't want to setup redis - 'CACHE_TYPE': 'redis', - 'CACHE_REDIS_URL': celery.backend_url -}) -du.configure_upload(app=app, folder=server.root_upload_folder) + +du.configure_upload(app=app, folder=settings.server.root_upload_folder) diff --git a/automappa/apps/home.py b/automappa/apps/home.py deleted file mode 100644 index 63b98db9..00000000 --- a/automappa/apps/home.py +++ /dev/null @@ -1,640 +0,0 @@ -# -*- coding: utf-8 -*- - -import itertools -import logging -from dash import html, dcc -from dash.dash_table import DataTable -from dash.exceptions import PreventUpdate -from dash_extensions.enrich import Input, Output, State -import dash_bootstrap_components as dbc -import pandas as pd -import plotly.io as pio - -import dash_uploader as du - -from automappa.app import app -from automappa.tasks import ( - # get_task, - preprocess_clusters_geom_medians, - preprocess_embeddings, - preprocess_marker_symbols, -) -from automappa.utils.models import SampleTables -from automappa.utils.serializers import ( - get_uploaded_files_table, - file_to_db, - validate_uploader, -) - -logging.basicConfig( - format="[%(levelname)s] %(name)s: %(message)s", - level=logging.DEBUG, -) - -logger = logging.getLogger(__name__) - -pio.templates.default = "plotly_white" - -######################################################################## -# LAYOUT -# ###################################################################### - -# https://dash-bootstrap-components.opensource.faculty.ai/docs/components/layout/ -# For best results, make sure you adhere to the following two rules when constructing your layouts: -# -# 1. Only use Row and Col inside a Container. -# 2. The immediate children of any Row component should always be Col components. -# 3. Your content should go inside the Col components. - - -# dbc.Card() NOTE: Titles, text and links -# Use the 'card-title', 'card-subtitle', and 'card-text' classes to add margins -# and spacing that have been optimized for cards to titles, subtitles and -# text respectively. - -binning_main_upload = du.Upload( - id="upload-binning-main-data", - text="Drag and Drop or Select binning-main file", - default_style={ - "width": "100%", - "height": "60px", - "lineHeight": "60px", - "borderWidth": "1px", - "borderStyle": "dashed", - "borderRadius": "5px", - "textAlign": "center", - "margin": "10px", - }, - max_files=1, - # 10240 MB = 10GB - # TODO: Add text to modal with max_file_size info... - max_file_size=10240, -) - -markers_upload = du.Upload( - id="upload-markers-data", - text="Drag and Drop or Select marker annotations file", - default_style={ - "width": "100%", - "height": "60px", - "lineHeight": "60px", - "borderWidth": "1px", - "borderStyle": "dashed", - "borderRadius": "5px", - "textAlign": "center", - "margin": "10px", - }, - max_files=1, - # 10240 MB = 10GB - # TODO: Add text to modal with max_file_size info... - max_file_size=10240, -) - -metagenome_upload = du.Upload( - id="upload-metagenome-data", - text="Drag and Drop or Select metagenome assembly", - default_style={ - "width": "100%", - "height": "60px", - "lineHeight": "60px", - "borderWidth": "1px", - "borderStyle": "dashed", - "borderRadius": "5px", - "textAlign": "center", - "margin": "10px", - }, - max_files=1, - # 10240 MB = 10GB - # TODO: Add text to modal with max_file_size info... - max_file_size=10240, -) - -upload_modal = html.Div( - [ - dbc.Button("Upload data", id="open-dismiss"), - dbc.Modal( - [ - dbc.ModalHeader( - dbc.ModalTitle("Upload Metagenome annotations"), close_button=False - ), - dbc.ModalBody( - [ - binning_main_upload, - markers_upload, - metagenome_upload, - ] - ), - dbc.ModalFooter( - dbc.Button( - "Close", id="close-dismiss", style={"textAlign": "center"} - ) - ), - ], - id="modal-dismiss", - keyboard=False, - backdrop="static", - fullscreen=True, - ), - ], -) - -example_card = dbc.Card( - [ - dbc.CardHeader(id="binning-main-samples-cardheader"), - dbc.CardBody( - [ - html.H4("{Filename}", className="card-title"), - html.H6("Last Updated: {timestamp}", className="card-subtitle"), - html.Br(), - html.Ul( - [ - html.Li("Uploaded: {timestamp}", className="card-text"), - html.Li("Checksum: {md5sum}", className="card-text"), - ] - ), - html.Hr(), - html.H4("Autometa", className="card-subtitle"), - html.Ul( - [ - html.Li("lengths - (done)"), - html.Li("gc-content - (done)"), - html.Li("coverage - (done)"), - html.Li("markers - (in progress)"), - html.Li("taxonomy - (in progress)"), - html.Li("kmers - (queued)"), - html.Li("binning - (queued)"), - html.Li("binning-summary - (queued)"), - html.Li("CheckM - (queued)"), - html.Li("GTDB-Tk - (queued)"), - html.Li("AntiSMASH - (queued)"), - ], - className="card-text", - ), - html.Div( - [ - dbc.Button("Refine MAGs"), - dbc.Button("MAG Summary"), - ], - className="d-grid gap-2 d-md-flex justify-content-md-center", - ), - ] - ), - dbc.CardFooter("Processing Status: {status}"), - ] -) - -card_widths = 3 -row_example_cards = dbc.Row( - [ - dbc.Col(example_card, width=card_widths), - ] -) - -samples_datatable = ( - dcc.Loading( - id="loading-samples-datatable", - children=[html.Label("Uploaded Datasets"), html.Div(id="samples-datatable")], - type="dot", - color="#646569", - ), -) - -selected_tables_datatable = ( - dcc.Loading( - id="loading-selected-tables-datatable", - children=[ - html.Label("Selected Datasets for Refinement & Summary:"), - html.Div(id="selected-tables-datatable"), - ], - type="dot", - color="#646569", - ), -) - -refine_mags_input_groups = html.Div( - [ - dbc.InputGroup( - [ - dbc.InputGroupText("Binning"), - dbc.Select( - id="binning-select", - placeholder="Select binning annotations", - ), - ] - ), - dbc.InputGroup( - [ - dbc.InputGroupText("Markers"), - dbc.Select( - id="markers-select", - placeholder="Select marker annotations", - ), - ] - ), - dbc.InputGroup( - [ - dbc.InputGroupText("Metagenome"), - dbc.Select( - id="metagenome-select", - placeholder="Select metagenome annotations", - ), - ] - ), - ] -) - -refine_mags_button = dbc.Button( - id="refine-mags-button", - children="Refine MAGs", -) - -tasks_table = html.Div(id="embedding-tasks") - -layout = dbc.Container( - children=[ - dbc.Row(upload_modal), - html.Br(), - # row_example_cards, - dbc.Row(samples_datatable), - html.Br(), - dbc.Row(refine_mags_input_groups), - html.Br(), - dbc.Row(refine_mags_button), - html.Br(), - dbc.Row(selected_tables_datatable), - html.Br(), - dbc.Row(tasks_table), - ], - fluid=True, -) - - -@app.callback( - Output("samples-store", "data"), - [ - Input("binning-main-upload-store", "modified_timestamp"), - Input("markers-upload-store", "modified_timestamp"), - Input("metagenome-upload-store", "modified_timestamp"), - ], - [ - State("binning-main-upload-store", "data"), - State("markers-upload-store", "data"), - State("metagenome-upload-store", "data"), - State("samples-store", "data"), - ], -) -def on_upload_stores_data( - binning_uploads_timestamp, - markers_uploads_timestamp, - metagenome_uploads_timestamp, - binning_uploads, - markers_uploads, - metagenome_uploads, - samples_store_data, -): - if ( - binning_uploads is None - and markers_uploads is None - and metagenome_uploads is None - ) or ( - binning_uploads_timestamp is None - and markers_uploads_timestamp is None - and metagenome_uploads_timestamp is None - ): - # Check if db has any samples in table - uploaded_files_df = get_uploaded_files_table() - if not uploaded_files_df.empty: - return uploaded_files_df.to_json(orient="split") - raise PreventUpdate - samples = [] - for data_upload in [ - binning_uploads, - markers_uploads, - metagenome_uploads, - samples_store_data, - ]: - df = ( - pd.read_json(data_upload, orient="split") if data_upload else pd.DataFrame() - ) - samples.append(df) - samples_df = pd.concat(samples).drop_duplicates(subset=["table_id"]) - - logger.debug(f"{samples_df.shape[0]:,} samples retrieved from data upload stores") - - return samples_df.to_json(orient="split") - - -@app.callback( - Output("binning-select", "options"), - [Input("samples-store", "data")], - State("samples-store", "data"), -) -def binning_select_options(samples_store_data, new_samples_store_data): - if samples_store_data is None: - raise PreventUpdate - samples_df = pd.read_json(samples_store_data, orient="split") - if new_samples_store_data is not None: - new_samples_df = pd.read_json(new_samples_store_data, orient="split") - samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( - subset=["table_id"] - ) - - if samples_df.empty: - raise PreventUpdate - - df = samples_df.loc[samples_df.filetype.eq("binning")] - logger.debug(f"{df.shape[0]:,} binning available for mag_refinement") - return [ - { - "label": filename, - "value": table_id, - } - for filename, table_id in zip(df.filename.tolist(), df.table_id.tolist()) - ] - - -@app.callback( - Output("markers-select", "options"), - [Input("samples-store", "data")], - State("samples-store", "data"), -) -def markers_select_options(samples_store_data, new_samples_store_data): - if samples_store_data is None: - raise PreventUpdate - samples_df = pd.read_json(samples_store_data, orient="split") - if new_samples_store_data is not None: - new_samples_df = pd.read_json(new_samples_store_data, orient="split") - samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( - subset=["table_id"] - ) - - if samples_df.empty: - raise PreventUpdate - - markers_samples = samples_df.loc[samples_df.filetype.eq("markers")] - logger.debug(f"{markers_samples.shape[0]:,} markers available for mag_refinement") - return [ - { - "label": filename, - "value": table_id, - } - for filename, table_id in zip( - markers_samples.filename.tolist(), markers_samples.table_id.tolist() - ) - ] - - -@app.callback( - Output("metagenome-select", "options"), - [Input("samples-store", "data")], - State("samples-store", "data"), -) -def metagenome_select_options(samples_store_data, new_samples_store_data): - if samples_store_data is None: - raise PreventUpdate - samples_df = pd.read_json(samples_store_data, orient="split") - if new_samples_store_data is not None: - new_samples_df = pd.read_json(new_samples_store_data, orient="split") - samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( - subset=["table_id"] - ) - - if samples_df.empty: - raise PreventUpdate - - df = samples_df.loc[samples_df.filetype.eq("metagenome")] - logger.debug(f"{df.shape[0]:,} metagenomes available for mag_refinement") - return [ - { - "label": filename, - "value": table_id, - } - for filename, table_id in zip(df.filename.tolist(), df.table_id.tolist()) - ] - - -@app.callback( - Output("samples-datatable", "children"), - [Input("samples-store", "data")], - State("samples-store", "data"), -) -def on_samples_store_data(samples_store_data, new_samples_store_data): - if samples_store_data is None: - raise PreventUpdate - samples_df = pd.read_json(samples_store_data, orient="split") - if new_samples_store_data is not None: - new_samples_df = pd.read_json(new_samples_store_data, orient="split") - samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( - subset=["table_id"] - ) - - logger.debug(f"retrieved {samples_df.shape[0]:,} samples from samples store") - - if samples_df.empty: - raise PreventUpdate - - return DataTable( - data=samples_df.to_dict("records"), - columns=[ - {"id": col, "name": col, "editable": False} for col in samples_df.columns - ], - ) - - -@app.callback( - Output("binning-main-upload-store", "data"), - [Input("upload-binning-main-data", "isCompleted")], - [ - State("upload-binning-main-data", "fileNames"), - State("upload-binning-main-data", "upload_id"), - ], -) -def on_binning_main_upload(iscompleted, filenames, upload_id): - try: - filepath = validate_uploader(iscompleted, filenames, upload_id) - except ValueError as err: - logger.warn(err) - raise PreventUpdate - if not filepath: - raise PreventUpdate - df = file_to_db( - filepath=filepath, - filetype="binning", - ) - return df.to_json(orient="split") - - -@app.callback( - Output("markers-upload-store", "data"), - [Input("upload-markers-data", "isCompleted")], - [ - State("upload-markers-data", "fileNames"), - State("upload-markers-data", "upload_id"), - ], -) -def on_markers_upload(iscompleted, filenames, upload_id): - try: - filepath = validate_uploader(iscompleted, filenames, upload_id) - except ValueError as err: - logger.warn(err) - raise PreventUpdate - if not filepath: - raise PreventUpdate - df = file_to_db(filepath, "markers") - return df.to_json(orient="split") - - -# For information on the dash_uploader component and callbacks... -# See https://github.com/np-8/dash-uploader#example-with-callback-and-other-options -@app.callback( - Output("metagenome-upload-store", "data"), - [Input("upload-metagenome-data", "isCompleted")], - [ - State("upload-metagenome-data", "fileNames"), - State("upload-metagenome-data", "upload_id"), - ], -) -def on_metagenome_upload(iscompleted, filenames, upload_id): - try: - filepath = validate_uploader(iscompleted, filenames, upload_id) - except ValueError as err: - logger.warn(err) - raise PreventUpdate - if not filepath: - raise PreventUpdate - df = file_to_db(filepath, "metagenome") - return df.to_json(orient="split") - - -@app.callback( - Output("modal-dismiss", "is_open"), - [Input("open-dismiss", "n_clicks"), Input("close-dismiss", "n_clicks")], - [State("modal-dismiss", "is_open")], -) -def toggle_modal(n_open, n_close, is_open): - if n_open or n_close: - return not is_open - return is_open - - -@app.callback( - Output("refine-mags-button", "disabled"), - [ - Input("binning-select", "value"), - Input("markers-select", "value"), - Input("metagenome-select", "value"), - ], -) -def refine_mags_button_active_callback(binning_value, markers_value, metagenome_value): - if binning_value is None or markers_value is None or metagenome_value is None: - return True - else: - return False - - -@app.callback( - Output("selected-tables-store", "data"), - [ - Input("refine-mags-button", "n_clicks"), - Input("binning-select", "value"), - Input("markers-select", "value"), - Input("metagenome-select", "value"), - ], -) -def on_refine_mags_button_click( - n, binning_select_value, markers_select_value, metagenome_select_value -): - if n is None: - raise PreventUpdate - tables_dict = {} - if metagenome_select_value is not None: - tables_dict["metagenome"] = {"id": metagenome_select_value} - if binning_select_value is not None: - tables_dict.update( - { - "binning": {"id": binning_select_value}, - "refinements": { - "id": binning_select_value.replace("-binning", "-refinement") - }, - } - ) - if markers_select_value is not None: - tables_dict["markers"] = {"id": markers_select_value} - sample = SampleTables(**tables_dict) - # BEGIN Queueing tasks from annotations... - # TODO: Refactor to separate bg-task submission? - # Show table of running tasks for user to monitor... - if sample.binning and sample.markers: - marker_symbols_task = preprocess_marker_symbols.delay( - sample.binning.id, sample.markers.id - ) - if sample.metagenome: - embedding_tasks = [] - # kmer_sizes = set([kmer_table.size for kmer_table in sample.kmers]) - kmer_sizes = set([5]) - # norm_methods = set([kmer_table.norm_method for kmer_table in sample.kmers]) - norm_methods = set(["am_clr"]) - embed_methods = set([kmer_table.embed_method for kmer_table in sample.kmers]) - embed_methods = ["umap", "densmap", "bhsne"] - for kmer_size, norm_method in itertools.product(kmer_sizes, norm_methods): - embeddings_task = preprocess_embeddings( - metagenome_table=sample.metagenome.id, - kmer_size=kmer_size, - norm_method=norm_method, - embed_methods=embed_methods, - ) - embedding_tasks.append(embeddings_task) - if sample.binning: - clusters_geom_medians_task = preprocess_clusters_geom_medians.delay( - sample.binning.id, "cluster" - ) - # df = pd.DataFrame([get_task(t.id) for t in [marker_symbols_task, clusters_geom_medians_task, *embedding_tasks]]) - # TODO: Monitor tasks progress with dcc.Interval in another callback... - # logger.debug(df) - return sample.json() - - -@app.callback( - Output("selected-tables-datatable", "children"), - [ - Input("selected-tables-store", "data"), - Input("tabs", "active_tab"), - ], - State("selected-tables-store", "data"), -) -def selected_tables_datatable_children( - selected_tables_store_data: SampleTables, - active_tab: str, - new_selected_tables_store_data: SampleTables, -): - # Why Input("tabs", "active_tab"): Navigating back to home tab triggers rendering of table from store - if selected_tables_store_data is None: - raise PreventUpdate - samples = SampleTables.parse_raw(selected_tables_store_data) - if new_selected_tables_store_data is not None: - new_tables = SampleTables.parse_raw(new_selected_tables_store_data) - if new_tables != samples: - tables_dict = samples.dict() - tables_dict.update(new_tables.dict()) - samples = SampleTables.parse_obj(tables_dict) - - has_table = False - for __, table_id in samples: - if table_id: - has_table = True - break - - if not has_table: - raise PreventUpdate - - return DataTable( - data=[ - {"filetype": sample, "table_id": table.id} - for sample, table in samples - if sample not in {"kmers"} - ], - columns=[ - {"id": "filetype", "name": "filetype", "editable": False}, - {"id": "table_id", "name": "table_id", "editable": False}, - ], - ) diff --git a/automappa/apps/mag_refinement.py b/automappa/apps/mag_refinement.py deleted file mode 100644 index b2d1cbf9..00000000 --- a/automappa/apps/mag_refinement.py +++ /dev/null @@ -1,938 +0,0 @@ -# -*- coding: utf-8 -*- - -import itertools -import logging - -from pydantic import Json - -numba_logger = logging.getLogger("numba") -numba_logger.setLevel(logging.WARNING) -h5py_logger = logging.getLogger("h5py") -h5py_logger.setLevel(logging.WARNING) - -from typing import Dict, List - -import pandas as pd -import dash_daq as daq - -from dash import dcc, html -from dash.dash_table import DataTable -from dash.exceptions import PreventUpdate -from dash_extensions.enrich import ServersideOutput,RedisStore,Input, Output, State -from plotly import graph_objects as go - -import dash_bootstrap_components as dbc -import plotly.io as pio - -from automappa.app import app,cache -from automappa.utils.models import SampleTables -from automappa import settings - -from automappa.utils.serializers import table_to_db - -from automappa.utils.figures import ( - format_axis_title, - get_scatterplot_2d, - taxonomy_sankey, - get_scatterplot_3d, - metric_boxplot, -) - -logging.basicConfig( - format="[%(levelname)s] %(name)s: %(message)s", - level=logging.DEBUG, -) -logger = logging.getLogger(__name__) -backend = RedisStore(settings.celery.backend_url) - -pio.templates.default = "plotly_white" - - -######################################################################## -# COMPONENTS: OFFCANVAS SETTINGS -# ###################################################################### - - -color_by_col_dropdown = [ - html.Label("Contigs colored by:"), - dcc.Dropdown( - id="color-by-column", - options=[], - value="cluster", - clearable=False, - ), -] - -# Scatterplot 2D Legend Toggle -scatterplot_2d_legend_toggle = daq.ToggleSwitch( - id="scatterplot-2d-legend-toggle", - size=40, - color="#c5040d", - label="Legend", - labelPosition="top", - vertical=False, - value=True, -) - -# TODO: Link x-axis-2d w/ y-axis-2d -# Combinations of kmer embeddings -# kmer-size [dropdown] -# kmer-norm-method [dropdown] -# kmer-embed-method [dropdown] -# All are filters to main dropdown combinations of kmer axes - -kmer_size_dropdown = [ - html.Label("K-mer size:"), - dcc.Dropdown( - id="kmer-size-dropdown", - options=[3, 4, 5], - value=5, - clearable=False, - ), -] - -norm_method_dropdown = [ - html.Label("K-mer norm. method:"), - dcc.Dropdown( - id="norm-method-dropdown", - options=["am_clr", "ilr"], - value="am_clr", - clearable=False, - ), -] - -scatterplot_2d_axes_dropdown = [ - html.Label("Axes:"), - dcc.Dropdown( - id="axes-2d", - value="coverage|gc_content", - clearable=False, - ), -] - -# Scatterplot 3D Legend Toggle -scatterplot_3d_legend_toggle = daq.ToggleSwitch( - id="scatterplot-3d-legend-toggle", - size=40, - color="#c5040d", - label="Legend", - labelPosition="top", - vertical=False, - value=True, -) - -scatterplot_3d_zaxis_dropdown = [ - html.Label("Z-axis:"), - dcc.Dropdown( - id="scatterplot-3d-zaxis-dropdown", - options=[ - {"label": "Coverage", "value": "coverage"}, - {"label": "GC%", "value": "gc_content"}, - {"label": "Length", "value": "length"}, - ], - value="length", - clearable=False, - ), -] - -taxa_rank_dropdown = [ - html.Label("Distribute taxa by rank:"), - dcc.Dropdown( - id="taxonomy-distribution-dropdown", - options=[ - {"label": "Class", "value": "class"}, - {"label": "Order", "value": "order"}, - {"label": "Family", "value": "family"}, - {"label": "Genus", "value": "genus"}, - {"label": "Species", "value": "species"}, - ], - value="species", - clearable=False, - ), -] - -# Download Refinements Button -binning_refinements_download_button = [ - dbc.Button( - "Download Refinements", - id="refinements-download-button", - n_clicks=0, - color="primary", - ), - dcc.Download(id="refinements-download"), -] - -# Summarize Refinements Button -binning_refinements_summary_button = [ - dbc.Button( - "Summarize Refinements", - id="refinements-summary-button", - n_clicks=0, - color="primary", - ), -] - - -refinement_settings_offcanvas = dbc.Offcanvas( - [ - dbc.Accordion( - [ - dbc.AccordionItem( - [ - dbc.Row( - [ - dbc.Col(color_by_col_dropdown), - dbc.Col(scatterplot_2d_legend_toggle), - ] - ), - dbc.Row(dbc.Col(kmer_size_dropdown)), - dbc.Row(dbc.Col(norm_method_dropdown)), - dbc.Row(dbc.Col(scatterplot_2d_axes_dropdown)), - ], - title="Figure 1: 2D Metagenome Overview", - ), - dbc.AccordionItem( - [ - dbc.Row( - [ - dbc.Col(scatterplot_3d_zaxis_dropdown), - dbc.Col(scatterplot_3d_legend_toggle), - ] - ), - ], - title="Figure 2: 3D Metagenome Overview", - ), - dbc.AccordionItem( - [ - dbc.Col(taxa_rank_dropdown), - ], - title="Figure 3: Taxonomic Distribution", - ), - ], - start_collapsed=True, - flush=True, - ), - dbc.Row( - [ - dbc.Col(binning_refinements_download_button), - dbc.Col(binning_refinements_summary_button), - ] - ), - ], - id="settings-offcanvas", - title="Settings", - is_open=False, - placement="end", - scrollable=True, -) - -######################################################################## -# COMPONENTS: Buttons and Toggle -# ###################################################################### - -refinement_settings_button = dbc.Button("Settings", id="settings-button", n_clicks=0) - -mag_refinement_save_button = dbc.Button( - "Save selection to MAG refinement", - id="mag-refinement-save-button", - n_clicks=0, - disabled=True, -) - -# Tooltip for info on store selections behavior -hide_selections_tooltip = dbc.Tooltip( - 'Toggling this to the "on" state will hide your manually-curated MAG refinement groups', - target="hide-selections-toggle", - placement="auto", -) - -# add hide selection toggle -hide_selections_toggle = daq.ToggleSwitch( - id="hide-selections-toggle", - size=40, - color="#c5040d", - label="Hide MAG Refinements", - labelPosition="top", - vertical=False, - value=False, -) - -# TODO: Refactor to update scatterplot legend with update marker symbol traces... -marker_symbols_label = html.Pre( - """ -Marker Symbol Circle: 0 Diamond: 2 X: 4 Hexagon: 6 - Count Legend Square: 1 Triangle: 3 Pentagon: 5 Hexagram: 7+ -""" -) - -mag_refinement_buttons = html.Div( - [ - refinement_settings_button, - refinement_settings_offcanvas, - mag_refinement_save_button, - hide_selections_toggle, - hide_selections_tooltip, - marker_symbols_label, - ], - className="d-grid gap-2 d-md-flex justify-content-md-start", -) - -######################################################################## -# COMPONENTS: FIGURES AND TABLES -# ###################################################################### - -# Add metrics as alerts using MIMAG standards -# TODO: Add progress bar to emit MAG curation progress -# See: https://dash-bootstrap-components.opensource.faculty.ai/docs/components/progress -# Color using MIMAG thresholds listed below: -# For current standards see the following links: -# contamination: https://genomicsstandardsconsortium.github.io/mixs/contam_score/ -# completeness: https://genomicsstandardsconsortium.github.io/mixs/compl_score/ -# (success) alert --> passing thresholds (completeness >= 90%, contamination <= 5%) -# (warning) alert --> within 10% thresholds, e.g. (completeness >=80%, contam. <= 15%) -# (danger) alert --> failing thresholds (completeness less than 80%, contam. >15%) -# TODO: Add callbacks for updating `color`, `value` and `label` with computed completeness and purity values -completeness_progress = dbc.Progress(id="mag-refinement-completeness-progress") -purity_progress = dbc.Progress(id="mag-refinement-purity-progress") - - -mag_metrics_table = [ - html.Label("Table 1. MAG Marker Metrics"), - dcc.Loading( - id="loading-mag-metrics-datatable", - children=[html.Div(id="mag-metrics-datatable")], - type="dot", - color="#646569", - ), -] - -scatterplot_2d = [ - html.Label("Figure 1: 2D Metagenome Overview"), - dcc.Loading( - id="loading-scatterplot-2d", - children=[ - dcc.Graph( - id="scatterplot-2d", - clear_on_unhover=True, - config={"displayModeBar": True, "displaylogo": False}, - ) - ], - type="graph", - ), -] - -scatterplot_3d = [ - html.Label("Figure 2: 3D Metagenome Overview"), - dcc.Loading( - id="loading-scatterplot-3d", - children=[ - dcc.Graph( - id="scatterplot-3d", - clear_on_unhover=True, - config={ - "toImageButtonOptions": dict( - format="svg", - filename="figure_2_3D_metagenome_overview", - ), - "displayModeBar": True, - "displaylogo": False, - }, - ) - ], - type="graph", - ), -] - - -taxonomy_figure = [ - html.Label("Figure 3: Taxonomic Distribution"), - dcc.Loading( - id="loading-taxonomy-distribution", - children=[ - dcc.Graph( - id="taxonomy-distribution", - config={ - "displayModeBar": False, - "displaylogo": False, - "staticPlot": True, - }, - ) - ], - type="graph", - ), -] - -mag_refinement_coverage_boxplot = [ - html.Label("Figure 4: MAG Refinement Coverage Boxplot"), - dcc.Loading( - id="loading-mag-refinement-coverage-boxplot", - children=[ - dcc.Graph( - id="mag-refinement-coverage-boxplot", - config={"displayModeBar": False, "displaylogo": False}, - ) - ], - type="dot", - color="#646569", - ), -] - -mag_refinement_gc_content_boxplot = [ - html.Label("Figure 5: MAG Refinement GC Content Boxplot"), - dcc.Loading( - id="loading-mag-refinement-gc-content-boxplot", - children=[ - dcc.Graph( - id="mag-refinement-gc-content-boxplot", - config={"displayModeBar": False, "displaylogo": False}, - ) - ], - type="dot", - color="#0479a8", - ), -] - -mag_refinement_length_boxplot = [ - html.Label("Figure 6: MAG Refinement Length Boxplot"), - dcc.Loading( - id="loading-mag-refinement-length-boxplot", - children=[ - dcc.Graph( - id="mag-refinement-length-boxplot", - config={"displayModeBar": False, "displaylogo": False}, - ) - ], - type="dot", - color="#0479a8", - ), -] - - -refinements_table = dcc.Loading( - id="loading-refinements-table", - children=[html.Div(id="refinements-table")], - type="circle", - color="#646569", -) - - -######################################################################## -# LAYOUT -# ###################################################################### - -# https://dash-bootstrap-components.opensource.faculty.ai/docs/components/layout/ -# For best results, make sure you adhere to the following two rules when constructing your layouts: -# -# 1. Only use Row and Col inside a Container. -# 2. The immediate children of any Row component should always be Col components. -# 3. Your content should go inside the Col components. -binning_store = dcc.Loading(dcc.Store("binning-store"), type='dot') - -layout = dbc.Container( - children=[ - binning_store, - dbc.Row([dbc.Col(mag_refinement_buttons)]), - dbc.Row( - [dbc.Col(scatterplot_2d, width=9), dbc.Col(mag_metrics_table, width=3)] - ), - # TODO: Add MAG assembly metrics table - dbc.Row([dbc.Col(taxonomy_figure, width=9), dbc.Col(scatterplot_3d, width=3)]), - dbc.Row( - [ - dbc.Col(mag_refinement_coverage_boxplot, width=4), - dbc.Col(mag_refinement_gc_content_boxplot, width=4), - dbc.Col(mag_refinement_length_boxplot, width=4), - ] - ), - dbc.Row([dbc.Col(refinements_table, width=12)]), - ], - fluid=True, -) - - -######################################################################## -# CALLBACKS -# ###################################################################### - -# @cache.memoize(timeout=3600) -@app.callback(ServersideOutput("binning-store", "data", backend=backend), Input("selected-tables-store", "data"), memoize=True) -def query_binning_in_db(selected_tables_data: SampleTables): - sample = SampleTables.parse_raw(selected_tables_data) - return sample.binning.table.reset_index().to_json('records') - - -@app.callback( - Output("settings-offcanvas", "is_open"), - Input("settings-button", "n_clicks"), - [State("settings-offcanvas", "is_open")], -) -def toggle_offcanvas(n1: int, is_open: bool) -> bool: - if n1: - return not is_open - return is_open - - -@app.callback( - Output("color-by-column", "options"), Input("selected-tables-store", "data") -) -def color_by_column_options_callback(selected_tables_data: SampleTables): - sample = SampleTables.parse_raw(selected_tables_data) - df = sample.binning.table - return [ - {"label": col.title().replace("_", " "), "value": col} - for col in df.select_dtypes("object").columns - ] - - -@app.callback( - Output("axes-2d", "options"), - Input("selected-tables-store", "data"), - Input("kmer-size-dropdown", "value"), - Input("norm-method-dropdown", "value"), -) -def axes_2d_options_callback( - selected_tables_data: Json[SampleTables], - kmer_size_dropdown_value: int, - norm_method_dropdown_value: str, -) -> List[Dict[str, str]]: - sample = SampleTables.parse_raw(selected_tables_data) - binning_df = sample.binning.table - binning_combinations = [ - { - "label": " vs. ".join( - [x_axis.title().replace("_", " "), y_axis.title().replace("_", " ")] - ), - "value": "|".join([x_axis, y_axis]), - "disabled": False, - } - for x_axis, y_axis in itertools.combinations( - binning_df.select_dtypes({"float64", "int64"}).columns, 2 - ) - if x_axis not in {"completeness", "purity", "taxid"} - and y_axis not in {"completeness", "purity", "taxid"} - ] - embeddings = [ - { - "label": kmer.embedding.name, - "value": f"{kmer.embedding.name}_x_1|{kmer.embedding.name}_x_2", - "disabled": not kmer.embedding.exists, - } - for kmer in sample.kmers - if kmer.size == kmer_size_dropdown_value - and kmer.norm_method == norm_method_dropdown_value - ] - return binning_combinations + embeddings - - -@app.callback( - Output("mag-metrics-datatable", "children"), - [ - Input("selected-tables-store", "data"), - Input("scatterplot-2d", "selectedData"), - ], -) -def update_mag_metrics_datatable_callback( - selected_tables_data: Json[SampleTables], - selected_contigs: Dict[str, List[Dict[str, str]]], -) -> DataTable: - sample = SampleTables.parse_raw(selected_tables_data) - markers_df = sample.markers.table - if selected_contigs: - contigs = {point["text"] for point in selected_contigs["points"]} - selected_contigs_count = len(contigs) - markers_df = markers_df.loc[markers_df.index.isin(contigs)] - - expected_markers_count = markers_df.shape[1] - - pfam_counts = markers_df.sum() - if pfam_counts[pfam_counts.ge(1)].empty: - total_markers = 0 - single_copy_marker_count = 0 - markers_present_count = 0 - redundant_markers_count = 0 - marker_set_count = 0 - completeness = "NA" - purity = "NA" - else: - total_markers = pfam_counts.sum() - single_copy_marker_count = pfam_counts.eq(1).sum() - markers_present_count = pfam_counts.ge(1).sum() - redundant_markers_count = pfam_counts.gt(1).sum() - completeness = markers_present_count / expected_markers_count * 100 - purity = single_copy_marker_count / markers_present_count * 100 - marker_set_count = total_markers / expected_markers_count - - marker_contig_count = markers_df.sum(axis=1).ge(1).sum() - single_marker_contig_count = markers_df.sum(axis=1).eq(1).sum() - multi_marker_contig_count = markers_df.sum(axis=1).gt(1).sum() - metrics_data = { - "Expected Markers": expected_markers_count, - "Total Markers": total_markers, - "Redundant-Markers": redundant_markers_count, - "Markers Count": markers_present_count, - "Marker Sets (Total / Expected)": marker_set_count, - "Marker-Containing Contigs": marker_contig_count, - "Multi-Marker Contigs": multi_marker_contig_count, - "Single-Marker Contigs": single_marker_contig_count, - } - if selected_contigs: - selection_metrics = { - "Contigs": selected_contigs_count, - "Completeness (%)": completeness, - "Purity (%)": purity, - } - selection_metrics.update(metrics_data) - # Adding this extra step b/c to keep selection metrics at top of the table... - metrics_data = selection_metrics - - metrics_df = pd.DataFrame([metrics_data]).T - metrics_df.rename(columns={0: "Value"}, inplace=True) - metrics_df.index.name = "MAG Metric" - metrics_df.reset_index(inplace=True) - metrics_df = metrics_df.round(2) - return DataTable( - data=metrics_df.to_dict("records"), - columns=[{"name": col, "id": col} for col in metrics_df.columns], - style_cell={ - "height": "auto", - # all three widths are needed - "minWidth": "20px", - "width": "20px", - "maxWidth": "20px", - "whiteSpace": "normal", - "textAlign": "center", - }, - # TODO: style completeness and purity cells to MIMAG standards as mentioned above - ) - - -@app.callback( - Output("scatterplot-2d", "figure"), - [ - Input("selected-tables-store", "data"), - Input("kmer-size-dropdown", "value"), - Input("norm-method-dropdown", "value"), - Input("axes-2d", "value"), - Input("scatterplot-2d-legend-toggle", "value"), - Input("color-by-column", "value"), - Input("hide-selections-toggle", "value"), - Input("mag-refinement-save-button", "n_clicks"), - ], -) -def scatterplot_2d_figure_callback( - # selected_tables_data: MetagenomeAnnotationsTables, - selected_tables_data: Json[SampleTables], - kmer_size_dropdown_value: int, - norm_method_dropdown_value: str, - axes_columns: str, - show_legend: bool, - color_by_col: str, - hide_selection_toggle: bool, - btn_clicks: int, -) -> go.Figure: - # NOTE: btn_clicks is an input so this figure is updated when new refinements are saved - # TODO: #23 refactor scatterplot callbacks - # - Add Input("scatterplot-2d", "layout") ? - # TODO: Refactor data retrieval/validation - sample = SampleTables.parse_raw(selected_tables_data) - bin_df = sample.binning.table - # TODO: Replace binning table w/coords-data - # Replace binning table w/metagenome-annotations-data[TODO] - # mag_cols=["length", "gc_content", "coverage"] - markers = sample.marker_symbols.table - if color_by_col not in bin_df.columns: - for col in ["phylum", "class", "order", "family"]: - if col in bin_df.columns: - color_by_col = col - break - if color_by_col not in bin_df.columns: - raise ValueError( - f"No columns were found in binning-main that could be used to group traces. {color_by_col} not found in table..." - ) - # Subset metagenome-annotations by selections iff selections have been made - if hide_selection_toggle: - refine_df = sample.refinements.table - refine_cols = [col for col in refine_df.columns if "refinement" in col] - if refine_cols: - latest_refine_col = refine_cols.pop() - # Retrieve only contigs that have already been refined... - refined_contigs_index = refine_df[ - refine_df[latest_refine_col].str.contains("refinement") - ].index - bin_df.drop( - refined_contigs_index, axis="index", inplace=True, errors="ignore" - ) - # TODO: Refactor figure s.t. updates are applied in - # batches for respective styling,layout,traces, etc. - # TODO: Put figure or traces in store, get/update/select - # based on current contig selections - # TODO: Should check norm_method, kmer_size prior to retrieving embeddings table... - # Add norm method and kmer_size dropdowns... - xaxis_column, yaxis_column = axes_columns.split("|") - if "_x_1" in xaxis_column or "_x_2" in yaxis_column: - # TODO: Fix retrieval of axes with embeddings... - for embeddings in sample.embeddings: - sizemers, norm_method, __ = embeddings.name.split("-") - kmer_size = int(sizemers.replace("mers", "")) - if ( - norm_method == norm_method_dropdown_value - and kmer_size == kmer_size_dropdown_value - and embeddings.exists - ): - embedding_df = embeddings.table - bin_df = bin_df.join(embedding_df, how="left") - else: - for kmer in sample.kmers: - if ( - f"{kmer.embedding.name}_x_1" == xaxis_column - and f"{kmer.embedding.name}_x_2" == yaxis_column - and kmer.size == kmer_size_dropdown_value - and kmer.norm_method == norm_method_dropdown_value - ): - bin_df = bin_df.join(kmer.embedding.table, how="left") - break - - fillnas = { - "cluster": "unclustered", - "superkingdom": "unclassified", - "phylum": "unclassified", - "class": "unclassified", - "order": "unclassified", - "family": "unclassified", - "genus": "unclassified", - "species": "unclassified", - } - fillna = fillnas.get(color_by_col, "unclustered") - fig = get_scatterplot_2d( - bin_df, - x_axis=xaxis_column, - y_axis=yaxis_column, - color_by_col=color_by_col, - fillna=fillna, - ) - - # TODO: If possible, as a separate callback do Output("scatterplot-2d", "layout") - with fig.batch_update(): - fig.layout.xaxis.title = format_axis_title(xaxis_column) - fig.layout.yaxis.title = format_axis_title(yaxis_column) - fig.layout.legend.title = color_by_col.title() - fig.layout.showlegend = show_legend - - # Update markers with symbol and size corresponding to marker count - # TODO: If possible, as a separate callback do Output("scatterplot-2d", "traces") - fig.for_each_trace( - lambda trace: trace.update( - marker_symbol=markers.symbol.loc[trace.text], - marker_size=markers.marker_size.loc[trace.text], - ) - ) - return fig - - -@app.callback( - Output("taxonomy-distribution", "figure"), - [ - Input("selected-tables-store", "data"), - Input("scatterplot-2d", "selectedData"), - Input("taxonomy-distribution-dropdown", "value"), - ], -) -def taxonomy_distribution_figure_callback( - selected_tables_data: SampleTables, - selected_contigs: Dict[str, List[Dict[str, str]]], - selected_rank: str, -) -> go.Figure: - sample = SampleTables.parse_raw(selected_tables_data) - df = sample.binning.table - if selected_contigs and selected_contigs["points"]: - contigs = {point["text"] for point in selected_contigs["points"]} - df = df.loc[df.index.isin(contigs)] - fig = taxonomy_sankey(df, selected_rank=selected_rank) - return fig - - -@app.callback( - Output("scatterplot-3d", "figure"), - [ - Input("selected-tables-store", "data"), - Input("axes-2d", "value"), - Input("scatterplot-3d-zaxis-dropdown", "value"), - Input("scatterplot-3d-legend-toggle", "value"), - Input("color-by-column", "value"), - Input("scatterplot-2d", "selectedData"), - ], -) -def scatterplot_3d_figure_callback( - selected_tables_data: SampleTables, - axes_columns: str, - z_axis: str, - show_legend: bool, - color_by_col: str, - selected_contigs: Dict[str, List[Dict[str, str]]], -) -> go.Figure: - sample = SampleTables.parse_raw(selected_tables_data) - df = sample.binning.table - color_by_col = "phylum" if color_by_col not in df.columns else color_by_col - if not selected_contigs: - contigs = set(df.index.tolist()) - else: - contigs = {point["text"] for point in selected_contigs["points"]} - # Subset DataFrame by selected contigs - df = df[df.index.isin(contigs)] - if color_by_col == "cluster": - # Categoricals for binning - df[color_by_col] = df[color_by_col].fillna("unclustered") - else: - # Other possible categorical columns all relate to taxonomy - df[color_by_col] = df[color_by_col].fillna("unclassified") - - x_axis, y_axis = axes_columns.split("|") - fig = get_scatterplot_3d( - df=df, - x_axis=x_axis, - y_axis=y_axis, - z_axis=z_axis, - color_by_col=color_by_col, - ) - fig.update_layout(showlegend=show_legend) - return fig - - -@app.callback( - Output("mag-refinement-coverage-boxplot", "figure"), - [ - Input("selected-tables-store", "data"), - Input("scatterplot-2d", "selectedData"), - ], -) -def mag_summary_coverage_boxplot_callback( - selected_tables_data: SampleTables, selected_data: Dict[str, List[Dict[str, str]]] -) -> go.Figure: - if not selected_data: - raise PreventUpdate - sample = SampleTables.parse_raw(selected_tables_data) - df = sample.binning.table - contigs = {point["text"] for point in selected_data["points"]} - df = df.loc[df.index.isin(contigs)] - fig = metric_boxplot(df, metrics=["coverage"], boxmean="sd") - return fig - - -@app.callback( - Output("mag-refinement-gc-content-boxplot", "figure"), - [ - Input("selected-tables-store", "data"), - Input("scatterplot-2d", "selectedData"), - ], -) -def mag_summary_gc_content_boxplot_callback( - selected_tables_data: SampleTables, selected_data: Dict[str, List[Dict[str, str]]] -) -> go.Figure: - if not selected_data: - raise PreventUpdate - sample = SampleTables.parse_raw(selected_tables_data) - df = sample.binning.table - contigs = {point["text"] for point in selected_data["points"]} - df = df.loc[df.index.isin(contigs)] - fig = metric_boxplot(df, metrics=["gc_content"], boxmean="sd") - fig.update_traces(name="GC Content") - return fig - - -@app.callback( - Output("mag-refinement-length-boxplot", "figure"), - [ - Input("selected-tables-store", "data"), - Input("scatterplot-2d", "selectedData"), - ], -) -def mag_summary_length_boxplot_callback( - selected_tables_data: SampleTables, selected_data: Dict[str, List[Dict[str, str]]] -) -> go.Figure: - if not selected_data: - raise PreventUpdate - sample = SampleTables.parse_raw(selected_tables_data) - df = sample.binning.table - contigs = {point["text"] for point in selected_data["points"]} - df = df.loc[df.index.isin(contigs)] - fig = metric_boxplot(df, metrics=["length"]) - return fig - - -@app.callback( - Output("refinements-table", "children"), - [ - Input("selected-tables-store", "data"), - Input("mag-refinement-save-button", "n_clicks"), - ], -) -def refinements_table_callback( - selected_tables_data: SampleTables, - btn_clicks: int, -) -> DataTable: - sample = SampleTables.parse_raw(selected_tables_data) - return DataTable( - data=sample.refinements.table.to_dict("records"), - columns=[{"name": col, "id": col} for col in sample.refinements.table.columns], - style_cell={"textAlign": "center"}, - style_cell_conditional=[{"if": {"column_id": "contig"}, "textAlign": "right"}], - virtualization=True, - ) - - -@app.callback( - Output("refinements-download", "data"), - [ - Input("refinements-download-button", "n_clicks"), - Input("selected-tables-store", "data"), - ], -) -def download_refinements( - n_clicks: int, - selected_tables_data: SampleTables, -) -> Dict[str, "str | bool"]: - if not n_clicks: - raise PreventUpdate - sample = SampleTables.parse_raw(selected_tables_data) - return dcc.send_data_frame( - sample.refinements.table.to_csv, "refinements.csv", index=False - ) - - -@app.callback( - Output("mag-refinement-save-button", "disabled"), - [Input("scatterplot-2d", "selectedData")], -) -def mag_refinement_save_button_disabled_callback( - selected_data: Dict[str, List[Dict[str, str]]] -) -> bool: - return not selected_data - - -@app.callback( - Output("mag-refinement-save-button", "n_clicks"), - [ - Input("scatterplot-2d", "selectedData"), - Input("selected-tables-store", "data"), - Input("mag-refinement-save-button", "n_clicks"), - ], -) -def store_binning_refinement_selections( - selected_data: Dict[str, List[Dict[str, str]]], - selected_tables_data: SampleTables, - n_clicks: int, -) -> int: - # Initial load... - if not n_clicks or (n_clicks and not selected_data) or not selected_data: - raise PreventUpdate - sample = SampleTables.parse_raw(selected_tables_data) - bin_df = sample.refinements.table - refinement_cols = [col for col in bin_df.columns if "refinement" in col] - refinement_num = len(refinement_cols) + 1 - refinement_name = f"refinement_{refinement_num}" - contigs = list({point["text"] for point in selected_data["points"]}) - bin_df.loc[contigs, refinement_name] = refinement_name - bin_df = bin_df.fillna(axis="columns", method="ffill") - bin_df.reset_index(inplace=True) - table_to_db(df=bin_df, name=sample.refinements.id) - return 0 diff --git a/automappa/apps/mag_summary.py b/automappa/apps/mag_summary.py deleted file mode 100644 index 5123de37..00000000 --- a/automappa/apps/mag_summary.py +++ /dev/null @@ -1,470 +0,0 @@ -# -*- coding: utf-8 -*- -from typing import Dict, List -from dash.exceptions import PreventUpdate - -from dash import dcc, html -from dash.dash_table import DataTable -from dash_extensions.enrich import Input, Output -import dash_bootstrap_components as dbc -from plotly import graph_objects as go -import plotly.io as pio - -from automappa.app import app -from automappa.utils.figures import metric_barplot, taxonomy_sankey, metric_boxplot -from automappa.utils.models import SampleTables -from automappa.tasks import get_metabin_stats_summary - -pio.templates.default = "plotly_white" - - -######################################################################## -# COMPONENTS: Figures & Tables -######################################################################## - -## Overview figures and table - -mag_overview_metrics_boxplot = dcc.Loading( - id="loading-mag-overview-metrics-boxplot", - children=[ - dcc.Graph( - id="mag-overview-metrics-boxplot", - config={"displayModeBar": False, "displaylogo": False}, - ) - ], - type="default", - color="#0479a8", -) - -mag_overview_gc_content_boxplot = dcc.Loading( - id="loading-mag-overview-gc-content-boxplot", - children=[ - dcc.Graph( - id="mag-overview-gc-content-boxplot", - config={"displayModeBar": False, "displaylogo": False}, - ) - ], - type="dot", - color="#646569", -) - -mag_overview_length_boxplot = dcc.Loading( - id="loading-mag-overview-length-boxplot", - children=[ - dcc.Graph( - id="mag-overview-length-boxplot", - config={"displayModeBar": False, "displaylogo": False}, - ) - ], - type="default", - color="#0479a8", -) - -mag_overview_coverage_boxplot = dcc.Loading( - id="loading-mag-overview-coverage-boxplot", - children=[ - dcc.Graph( - id="mag-overview-coverage-boxplot", - config={"displayModeBar": False, "displaylogo": False}, - ) - ], - type="dot", - color="#646569", -) - -mag_summary_stats_datatable = [ - html.Label("Table 1. MAGs Summary"), - dcc.Loading( - id="loading-mag-summary-stats-datatable", - children=[html.Div(id="mag-summary-stats-datatable")], - type="circle", - color="#646569", - ), -] - -### Selected MAG figures - -mag_taxonomy_sankey = dcc.Loading( - id="loading-mag-taxonomy-sankey", - children=[dcc.Graph(id="mag-taxonomy-sankey")], - type="graph", -) - -mag_metrics_boxplot = dcc.Loading( - id="loading-mag-metrics-barplot", - children=[ - dcc.Graph( - id="mag-metrics-barplot", - config={"displayModeBar": False, "displaylogo": False}, - ) - ], - type="dot", - color="#646569", -) - -mag_gc_content_boxplot = dcc.Loading( - id="loading-mag-gc-content-boxplot", - children=[ - dcc.Graph( - id="mag-gc-content-boxplot", - config={"displayModeBar": False, "displaylogo": False}, - ) - ], - type="default", - color="#0479a8", -) - -mag_length_boxplot = dcc.Loading( - id="loading-mag-length-boxplot", - children=[ - dcc.Graph( - id="mag-length-boxplot", - config={"displayModeBar": False, "displaylogo": False}, - ) - ], - type="dot", - color="#646569", -) - -mag_coverage_boxplot = dcc.Loading( - id="loading-mag-coverage-boxplot", - children=[ - dcc.Graph( - id="mag-coverage-boxplot", - config={"displayModeBar": False, "displaylogo": False}, - ) - ], - type="default", - color="#0479a8", -) - -######################################################################## -# AESTHETHIC COMPONENTS: Dropdowns -######################################################################## - - -mag_summary_cluster_col_dropdown = [ - html.Label("MAG Summary Cluster Column Dropdown"), - dcc.Dropdown( - id="mag-summary-cluster-col-dropdown", - placeholder="Select a cluster column to compute MAG summary metrics", - clearable=False, - ), -] - -mag_selection_dropdown = [ - html.Label("MAG Selection Dropdown"), - dcc.Dropdown( - id="mag-selection-dropdown", - clearable=True, - placeholder="Select a MAG from this dropdown for a MAG-specific summary", - ), -] - - -######################################################################## -# LAYOUT -# ###################################################################### - -# https://dash-bootstrap-components.opensource.faculty.ai/docs/components/layout/ -# For best results, make sure you adhere to the following two rules when constructing your layouts: -# -# 1. Only use Row and Col inside a Container. -# 2. The immediate children of any Row component should always be Col components. -# 3. Your content should go inside the Col components. - -# TODO: Markdown Summary Report - -layout = dbc.Container( - [ - dbc.Row( - [ - dbc.Col(mag_overview_metrics_boxplot, width=3), - dbc.Col(mag_overview_gc_content_boxplot, width=3), - dbc.Col(mag_overview_length_boxplot, width=3), - dbc.Col(mag_overview_coverage_boxplot, width=3), - ] - ), - dbc.Row([dbc.Col(mag_summary_cluster_col_dropdown)]), - dbc.Col(mag_summary_stats_datatable), - dbc.Col(mag_selection_dropdown), - dbc.Col(mag_taxonomy_sankey), - dbc.Row( - [ - dbc.Col(mag_metrics_boxplot, width=3), - dbc.Col(mag_gc_content_boxplot, width=3), - dbc.Col(mag_length_boxplot, width=3), - dbc.Col(mag_coverage_boxplot, width=3), - ] - ), - ], - fluid=True, -) - - -######################################################################## -# CALLBACKS -# ###################################################################### - - -@app.callback( - Output("mag-overview-metrics-boxplot", "figure"), - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), -) -def mag_overview_metrics_boxplot_callback( - selected_tables_data: SampleTables, cluster_col: str -) -> go.Figure: - sample = SampleTables.parse_raw(selected_tables_data) - mag_summary_df = sample.binning.table - if cluster_col not in mag_summary_df.columns: - raise PreventUpdate - mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) - mag_summary_df = mag_summary_df.loc[mag_summary_df[cluster_col].ne("unclustered")] - fig = metric_boxplot(df=mag_summary_df, metrics=["completeness", "purity"]) - return fig - - -@app.callback( - Output("mag-overview-gc-content-boxplot", "figure"), - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), -) -def mag_overview_gc_content_boxplot_callback( - selected_tables_data: SampleTables, cluster_col: str -) -> go.Figure: - sample = SampleTables.parse_raw(selected_tables_data) - mag_summary_df = sample.binning.table - if cluster_col not in mag_summary_df.columns: - raise PreventUpdate - mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) - mag_summary_df = mag_summary_df.loc[mag_summary_df[cluster_col].ne("unclustered")] - fig = metric_boxplot(df=mag_summary_df, metrics=["gc_content"]) - fig.update_traces(name="GC Content") - return fig - - -@app.callback( - Output("mag-overview-length-boxplot", "figure"), - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), -) -def mag_overview_length_boxplot_callback( - selected_tables_data: SampleTables, cluster_col: str -) -> go.Figure: - sample = SampleTables.parse_raw(selected_tables_data) - mag_summary_df = sample.binning.table - if cluster_col not in mag_summary_df.columns: - raise PreventUpdate - mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) - mag_summary_df = mag_summary_df.loc[mag_summary_df[cluster_col].ne("unclustered")] - fig = metric_boxplot(mag_summary_df, metrics=["length"]) - return fig - - -@app.callback( - Output("mag-overview-coverage-boxplot", "figure"), - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), -) -def mag_overview_coverage_boxplot_callback( - selected_tables_data: SampleTables, cluster_col: str -) -> go.Figure: - sample = SampleTables.parse_raw(selected_tables_data) - mag_summary_df = sample.binning.table - if cluster_col not in mag_summary_df.columns: - raise PreventUpdate - mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) - mag_summary_df = mag_summary_df.loc[mag_summary_df[cluster_col].ne("unclustered")] - fig = metric_boxplot(mag_summary_df, metrics=["coverage"]) - return fig - - -@app.callback( - Output("mag-summary-cluster-col-dropdown", "options"), - Input("selected-tables-store", "data"), -) -def mag_summary_cluster_col_dropdown_options_callback( - selected_tables_data: SampleTables, -) -> List[Dict[str, str]]: - sample = SampleTables.parse_raw(selected_tables_data) - refinement_df = sample.refinements.table - return [ - {"label": col.title(), "value": col} - for col in refinement_df.columns - if "cluster" in col or "refinement" in col - ] - - -@app.callback( - Output("mag-summary-stats-datatable", "children"), - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), -) -def mag_summary_stats_datatable_callback( - selected_tables_data: SampleTables, cluster_col: str -) -> DataTable: - if cluster_col is None: - raise PreventUpdate - sample = SampleTables.parse_raw(selected_tables_data) - stats_df = get_metabin_stats_summary( - binning_table=sample.binning.id, - refinements_table=sample.refinements.id, - markers_table=sample.markers.id, - cluster_col=cluster_col, - ) - return DataTable( - data=stats_df.to_dict("records"), - columns=[ - {"name": col.replace("_", " "), "id": col} for col in stats_df.columns - ], - style_table={"overflowX": "auto"}, - style_cell={ - "height": "auto", - # all three widths are needed - "width": "120px", - "minWidth": "120px", - "maxWidth": "120px", - "whiteSpace": "normal", - }, - fixed_rows={"headers": True}, - ) - - -## Selected MAG callbacks - - -@app.callback( - Output("mag-taxonomy-sankey", "figure"), - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - Input("mag-selection-dropdown", "value"), -) -def mag_taxonomy_sankey_callback( - selected_tables_data: SampleTables, cluster_col: str, selected_mag: str -) -> go.Figure: - sample = SampleTables.parse_raw(selected_tables_data) - bin_df = sample.binning.table - refinement_df = sample.refinements.table.drop(columns="cluster") - bin_df = bin_df.join(refinement_df, how="right") - if cluster_col not in bin_df.columns: - raise PreventUpdate - mag_df = bin_df.loc[bin_df[cluster_col].eq(selected_mag)] - fig = taxonomy_sankey(mag_df) - return fig - - -@app.callback( - Output("mag-gc-content-boxplot", "figure"), - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - Input("mag-selection-dropdown", "value"), -) -def mag_summary_gc_content_boxplot_callback( - selected_tables_data: SampleTables, cluster_col: str, selected_mag: str -) -> go.Figure: - sample = SampleTables.parse_raw(selected_tables_data) - bin_df = sample.binning.table - refinement_df = sample.refinements.table.drop(columns="cluster") - bin_df = bin_df.join(refinement_df, how="right") - if cluster_col not in bin_df.columns: - raise PreventUpdate - bin_df = bin_df.dropna(subset=[cluster_col]) - mag_df = bin_df.loc[bin_df[cluster_col].eq(selected_mag)] - mag_df = mag_df.round(2) - fig = metric_boxplot(df=mag_df, metrics=["gc_content"]) - fig.update_traces(name="GC Content") - return fig - - -@app.callback( - Output("mag-metrics-barplot", "figure"), - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - Input("mag-selection-dropdown", "value"), -) -def mag_metrics_callback( - selected_tables_data: SampleTables, cluster_col: str, selected_mag: str -) -> go.Figure: - if not selected_mag: - raise PreventUpdate - sample = SampleTables.parse_raw(selected_tables_data) - bin_df = sample.binning.table - refinement_df = sample.refinements.table.drop(columns="cluster") - mag_summary_df = bin_df.join(refinement_df, how="right") - if cluster_col not in mag_summary_df.columns: - raise PreventUpdate - mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) - mag_df = mag_summary_df.loc[mag_summary_df[cluster_col].eq(selected_mag)] - mag_df = mag_df.round(2) - fig = metric_barplot( - df=mag_df, metrics=["completeness", "purity"], name=selected_mag - ) - return fig - - -@app.callback( - Output("mag-coverage-boxplot", "figure"), - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - Input("mag-selection-dropdown", "value"), -) -def mag_summary_gc_content_boxplot_callback( - selected_tables_data: SampleTables, cluster_col: str, selected_mag: str -) -> go.Figure: - if not selected_mag: - raise PreventUpdate - sample = SampleTables.parse_raw(selected_tables_data) - bin_df = sample.binning.table - refinement_df = sample.refinements.table.drop(columns="cluster") - mag_summary_df = bin_df.join(refinement_df, how="right") - if cluster_col not in mag_summary_df.columns: - raise PreventUpdate - mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) - mag_df = mag_summary_df.loc[mag_summary_df[cluster_col].eq(selected_mag)] - mag_df = mag_df.round(2) - fig = metric_boxplot(df=mag_df, metrics=["coverage"]) - return fig - - -@app.callback( - Output("mag-length-boxplot", "figure"), - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), - Input("mag-selection-dropdown", "value"), -) -def mag_summary_gc_content_boxplot_callback( - selected_tables_data: SampleTables, cluster_col: str, selected_mag: str -) -> go.Figure: - if not selected_mag: - raise PreventUpdate - sample = SampleTables.parse_raw(selected_tables_data) - bin_df = sample.binning.table - refinement_df = sample.refinements.table.drop(columns="cluster") - mag_summary_df = bin_df.join(refinement_df, how="right") - if cluster_col not in mag_summary_df.columns: - raise PreventUpdate - mag_summary_df = mag_summary_df.dropna(subset=[cluster_col]) - mag_df = mag_summary_df.loc[mag_summary_df[cluster_col].eq(selected_mag)] - mag_df = mag_df.round(2) - fig = metric_boxplot(df=mag_df, metrics=["length"]) - return fig - - -@app.callback( - Output("mag-selection-dropdown", "options"), - Input("selected-tables-store", "data"), - Input("mag-summary-cluster-col-dropdown", "value"), -) -def mag_selection_dropdown_options_callback( - selected_tables_data: SampleTables, cluster_col: str -) -> List[Dict[str, str]]: - sample = SampleTables.parse_raw(selected_tables_data) - df = sample.refinements.table - if cluster_col not in df.columns: - options = [] - else: - options = [ - {"label": cluster, "value": cluster} - for cluster in df[cluster_col].dropna().unique() - ] - return options diff --git a/automappa/apps/__init__.py b/automappa/components/__init__.py similarity index 100% rename from automappa/apps/__init__.py rename to automappa/components/__init__.py diff --git a/automappa/components/ids.py b/automappa/components/ids.py new file mode 100644 index 00000000..3bdd1794 --- /dev/null +++ b/automappa/components/ids.py @@ -0,0 +1,130 @@ +# ROOT: stores and pages navigation +PAGES_NAVBAR = "automappa-pages-navbar" +BINNING_MAIN_UPLOAD_STORE = "binning-main-upload-store" +TASK_ID_STORE = "task-id-store" +MARKERS_UPLOAD_STORE = "markers-upload-store" +METAGENOME_UPLOAD_STORE = "metagenome-upload-store" +SAMPLES_STORE = "samples-store" +METAGENOME_ID_STORE = "metagenome-id-store" +SELECTED_TABLES_STORE = "selected-tables-store" +CYTOSCAPE_STORE = "cytoscape-upload-store" +BINNING_STORE = "binning-store" +# HOME +HOME_TAB_ID = "Home" +SELECTED_TABLES_DATATABLE = "selected-tables-datatable" +SAMPLES_DATATABLE = "samples-datatable" +LOADING_SAMPLES_DATATABLE = "loading-samples-datatable" +BINNING_SELECT = "binning-select" +MARKERS_SELECT = "markers-select" +METAGENOME_SELECT = "metagenome-select" +CYTOSCAPE_SELECT = "cytoscape-select" +BINNING_UPLOAD = "binning-upload" +MARKERS_UPLOAD = "markers-upload" +METAGENOME_UPLOAD = "metagenome-upload" +CYTOSCAPE_UPLOAD = "cytoscape-upload" +VALIDATED_UPLOADS_STORE = "validated-uploads-store" +UPLOAD_MODAL = "modal-dismiss" +OPEN_MODAL_BUTTON = "open-dismiss" +CLOSE_MODAL_BUTTON = "close-dismiss" +UPLOAD_STEPPER = "upload-stepper" +UPLOAD_STEPPER_BACK_BUTTON = "upload-stepper-back-button" +UPLOAD_STEPPER_NEXT_BUTTON = "upload-stepper-next-button" +UPLOAD_STEPPER_SUBMIT_BUTTON = "upload-stepper-submit-button" +UPLOADED_DATA_TYPE = "uploaded-data-type" +REFINE_MAGS_BUTTON = "refine-mags-button" +LOADING_SELECTED_TABLES_DATATABLE = "loading-selected-tables-datatable" +EMBEDDING_TASKS = "embedding-tasks" +SAMPLE_NAME_TEXT_INPUT = "sample-name-text-input" +SAMPLE_CARDS_CONTAINER = "sample-cards-container" +SAMPLE_CARD_INDEX = "sample-card-index" +SAMPLE_CARD_TYPE = "sample-card-type" +SAMPLE_CARD_REMOVE_BTN = "sample-card-remove-btn" +SAMPLE_CARD_TASK_ID = "sample-card-task-id" +SAMPLE_CARD_CHIP_TYPE = "sample-card-chip-type" +SAMPLE_CARD_METAGENOME_BADGE_TYPE = "sample-card-metagenome-badge-type" +SAMPLE_CARD_METAGENOME_BADGE_LABEL = "metagenome" +SAMPLE_CARD_BINNING_BADGE_TYPE = "sample-card-binning-badge-type" +SAMPLE_CARD_BINNING_BADGE_LABEL = "contigs" +SAMPLE_CARD_MARKERS_BADGE_TYPE = "sample-card-markers-badge-type" +SAMPLE_CARD_MARKERS_BADGE_LABEL = "markers" +SAMPLE_CARD_CONNECTIONS_BADGE_TYPE = "sample-card-connections-badge-type" +SAMPLE_CARD_CONNECTIONS_BADGE_LABEL = "connections" +BACKGROUND_TASK_BADGE = "background-task-badge" +BACKGROUND_TASK_DIV = "background-task-div" +BACKGROUND_TASK_INTERVAL = "background-task-interval" +NOTIFICATION_TASK_ID = "notification-task-id" +BACKGROUND_TASK_BUTTON = "background-task-button" +BADGE_TASK_STORE = "badge-task-store" +BADGE_STATUS_INTERVAL = "badge-status-interval" +# MAG_REFINEMENT +MAG_REFINEMENT_TAB_ID = "MAG-refinement" +COLOR_BY_COLUMN_DROPDOWN = "color-by-column" +COLOR_BY_COLUMN_DROPDOWN_VALUE_DEFAULT = "cluster" +SCATTERPLOT_2D_LEGEND_TOGGLE = "scatterplot-2d-legend-toggle" +SCATTERPLOT_2D_LEGEND_TOGGLE_VALUE_DEFAULT = True +KMER_SIZE_DROPDOWN = "kmer-size-dropdown" +KMER_SIZE_DROPDOWN_VALUE_DEFAULT = 5 +NORM_METHOD_DROPDOWN = "norm-method-dropdown" +NORM_METHOD_DROPDOWN_VALUE_DEFAULT = "am_clr" +AXES_2D_DROPDOWN = "axes-2d-dropdown" +AXES_2D_DROPDOWN_VALUE_DEFAULT = "coverage|gc_content" +COVERAGE_RANGE_SLIDER = "coverage-range-slider" +SCATTERPLOT_3D_LEGEND_TOGGLE = "scatterplot-3d-legend-toggle" +SCATTERPLOT_3D_LEGEND_TOGGLE_VALUE_DEFAULT = True +SCATTERPLOT_3D_ZAXIS_DROPDOWN = "scatterplot-3d-zaxis-dropdown" +SCATTERPLOT_3D_ZAXIS_DROPDOWN_VALUE_DEFAULT = "length" +TAXONOMY_DISTRIBUTION_DROPDOWN = "taxonomy-distribution-dropdown" +TAXONOMY_DISTRIBUTION_DROPDOWN_VALUE_DEFAULT = "species" +REFINEMENTS_DOWNLOAD_BUTTON = "refinements-download-button" +REFINEMENTS_CLEAR_BUTTON = "refinements-clear-button" +REFINEMENTS_CLEARED_NOTIFICATION = "refinements-cleared-notification" +REFINEMENTS_NOTIFICATION = "refinements-notification" +REFINEMENTS_DOWNLOAD = "refinements-download" +REFINEMENTS_SUMMARY_BUTTON = "refinements-summary-button" +SETTINGS_OFFCANVAS = "settings-offcanvas" +MAG_REFINEMENTS_SAVE_BUTTON = "mag-refinement-save-button" +HIDE_SELECTIONS_TOGGLE = "hide-selections-toggle" +HIDE_SELECTIONS_TOGGLE_VALUE_DEFAULT = False +LOADING_MAG_METRICS_DATATABLE = "loading-mag-metrics-datatable" +MAG_METRICS_DATATABLE = "mag-metrics-datatable" +LOADING_SCATTERPLOT_2D = "loading-scatterplot-2d" +SCATTERPLOT_2D_FIGURE = "scatterplot-2d-figure" +LOADING_SCATTERPLOT_3D = "loading-scatterplot-3d" +SCATTERPLOT_3D = "scatterplot-3d" +LOADING_TAXONOMY_DISTRIBUTION = "loading-taxonomy-distribution" +TAXONOMY_DISTRIBUTION = "taxonomy-distribution" +LOADING_MAG_REFINEMENT_COVERAGE_BOXPLOT = "loading-mag-refinement-coverage-boxplot" +MAG_REFINEMENT_COVERAGE_BOXPLOT = "mag-refinement-coverage-boxplot" +LOADING_MAG_REFINEMENT_GC_CONTENT_BOXPLOT = "loading-mag-refinement-gc-content-boxplot" +MAG_REFINEMENT_GC_CONTENT_BOXPLOT = "mag-refinement-gc-content-boxplot" +LOADING_MAG_REFINEMENT_LENGTH_BOXPLOT = "loading-mag-refinement-length-boxplot" +MAG_REFINEMENT_LENGTH_BOXPLOT = "mag-refinement-length-boxplot" +LOADING_REFINEMENTS_TABLE = "loading-refinements-table" +REFINEMENTS_TABLE = "refinements-table" +SETTINGS_BUTTON = "settings-button" +LOADING_CONTIG_CYTOSCAPE = "loading-contig-cytoscape" +CONTIG_CYTOSCAPE = "contig-cytoscape" +# MAG SUMMARY +MAG_SUMMARY_TAB_ID = "MAG-summary" +MAG_OVERVIEW_METRICS_BOXPLOT = "mag-overview-metrics-boxplot" +LOADING_MAG_OVERVIEW_METRICS_BOXPLOT = "loading-mag-overview-metrics-boxplot" +LOADING_MAG_OVERVIEW_GC_CONTENT_BOXPLOT = "loading-mag-overview-gc-content-boxplot" +MAG_OVERVIEW_GC_CONTENT_BOXPLOT = "mag-overview-gc-content-boxplot" +LOADING_MAG_OVERVIEW_LENGTH_BOXPLOT = "loading-mag-overview-length-boxplot" +MAG_OVERVIEW_LENGTH_BOXPLOT = "mag-overview-length-boxplot" +LOADING_MAG_OVERVIEW_COVERAGE_BOXPLOT = "loading-mag-overview-coverage-boxplot" +MAG_OVERVIEW_COVERAGE_BOXPLOT = "mag-overview-coverage-boxplot" +LOADING_MAG_SUMMARY_STATS_DATATABLE = "loading-mag-summary-stats-datatable" +MAG_SUMMARY_STATS_DATATABLE = "mag-summary-stats-datatable" +LOADING_MAG_TAXONOMY_SANKEY = "loading-mag-taxonomy-sankey" +MAG_TAXONOMY_SANKEY = "mag-taxonomy-sankey" +LOADING_MAG_METRICS_BARPLOT = "loading-mag-metrics-barplot" +MAG_METRICS_BARPLOT = "mag-metrics-barplot" +LOADING_MAG_GC_CONTENT_BOXPLOT = "loading-mag-gc-content-boxplot" +MAG_GC_CONTENT_BOXPLOT = "mag-gc-content-boxplot" +LOADING_MAG_LENGTH_BOXPLOT = "loading-mag-length-boxplot" +MAG_LENGTH_BOXPLOT = "mag-length-boxplot" +LOADING_MAG_COVERAGE_BOXPLOT = "loading-mag-coverage-boxplot" +MAG_COVERAGE_BOXPLOT = "mag-coverage-boxplot" +MAG_SUMMARY_CLUSTER_COL_DROPDOWN = "mag-summary-cluster-col-dropdown" +MAG_SELECTION_DROPDOWN = "mag-selection-dropdown" diff --git a/automappa/components/layout.py b/automappa/components/layout.py new file mode 100644 index 00000000..98bc9e6b --- /dev/null +++ b/automappa/components/layout.py @@ -0,0 +1,94 @@ +import logging +import dash + +from typing import Literal +from dash_extensions.enrich import DashProxy, html +import dash_mantine_components as dmc + +from automappa.components import ( + metagenome_id_store, + tasks_store, + pages_navbar, +) + +from automappa.pages.home.source import HomeDataSource +from automappa.pages.mag_refinement.source import RefinementDataSource +from automappa.pages.mag_summary.source import SummaryDataSource + +from automappa.pages.home.layout import render as render_home_layout +from automappa.pages.mag_refinement.layout import render as render_mag_refinement_layout +from automappa.pages.mag_summary.layout import render as render_mag_summary_layout +from automappa.pages.not_found_404 import render as render_not_found_404 + +logger = logging.getLogger(__name__) + + +def render( + app: DashProxy, + storage_type: Literal["memory", "session", "local"] = "session", + clear_data: bool = False, +) -> html.Div: + home_data_source = HomeDataSource() + home_page = render_home_layout(source=home_data_source) + home_page.register( + app=app, + module=home_page.name, + **dict( + name=home_page.name, + description=home_page.description, + title=home_page.title, + icon=home_page.icon, + top_nav=True, + order=0, + path="/", + redirect_from=["/home"], + ) + ) + refinement_source = RefinementDataSource() + mag_refinement_page = render_mag_refinement_layout(source=refinement_source) + mag_refinement_page.register( + app=app, + module=mag_refinement_page.name, + **dict( + name=mag_refinement_page.name, + description=mag_refinement_page.description, + title=mag_refinement_page.title, + icon=mag_refinement_page.icon, + top_nav=False, + order=1, + ) + ) + summary_source = SummaryDataSource() + mag_summary_page = render_mag_summary_layout(source=summary_source) + mag_summary_page.register( + app=app, + module=mag_summary_page.name, + **dict( + name=mag_summary_page.name, + description=mag_summary_page.description, + title=mag_summary_page.title, + icon=mag_summary_page.icon, + top_nav=False, + order=2, + ) + ) + not_found_404_page = render_not_found_404() + not_found_404_page.register( + app=app, + module=not_found_404_page.name, + ) + + # Setup main app layout. + return dmc.MantineProvider( + dmc.NotificationsProvider( + dmc.Container( + [ + metagenome_id_store.render(app, storage_type, clear_data), + tasks_store.render(app, storage_type, clear_data), + pages_navbar.render(), + dash.page_container, + ], + fluid=True, + ) + ) + ) diff --git a/automappa/components/metagenome_id_store.py b/automappa/components/metagenome_id_store.py new file mode 100644 index 00000000..12277b79 --- /dev/null +++ b/automappa/components/metagenome_id_store.py @@ -0,0 +1,45 @@ +from typing import Dict, List, Literal +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import ( + dcc, + DashProxy, + State, + Input, + Serverside, + Output, + ALL, +) +from automappa.components import ids +from automappa.data.database import redis_backend + + +def render( + app: DashProxy, + storage_type: Literal["memory", "session", "local"] = "session", + clear_data: bool = False, +) -> dcc.Store: + @app.callback( + Output(ids.METAGENOME_ID_STORE, "data"), + Input( + {"type": ids.SAMPLE_CARD_TYPE, ids.SAMPLE_CARD_INDEX: ALL}, + "withBorder", + ), + State({"type": ids.SAMPLE_CARD_TYPE, ids.SAMPLE_CARD_INDEX: ALL}, "id"), + prevent_initial_call=True, + ) + def update_metagenome_id( + sample_cards_borders: List[str], sample_cards_ids: List[Dict[str, str]] + ) -> int: + if not any(sample_cards_borders): + raise PreventUpdate + sample_card_index = [ + i for i, border in enumerate(sample_cards_borders) if border + ][0] + metagenome_id = sample_cards_ids[sample_card_index].get(ids.SAMPLE_CARD_INDEX) + return Serverside(metagenome_id, backend=redis_backend) + + return dcc.Store( + id=ids.METAGENOME_ID_STORE, + storage_type=storage_type, + clear_data=clear_data, + ) diff --git a/automappa/components/pages_navbar.py b/automappa/components/pages_navbar.py new file mode 100644 index 00000000..4c000345 --- /dev/null +++ b/automappa/components/pages_navbar.py @@ -0,0 +1,57 @@ +import dash_mantine_components as dmc +from dash_iconify import DashIconify +import dash +from dash_extensions.enrich import html + + +def render() -> html.Div: + # @app.callback( + # Output(ids.PAGES_NAVBAR, "children"), + # ) + # def update_disabled_navlinks(): + # ... + + logo = html.Img(src=dash.get_asset_url("favicon.ico"), height="30px") + brand = dmc.Anchor( + [logo, " Automappa"], + href="https://github.com/WiscEvan/Automappa", + target="_blank", + underline=False, + color="dark", + size="md", + transform="capitalize", + weight=550, + ) + link_group = dmc.Group( + [ + dmc.NavLink( + label=dmc.Text(page["name"], align="center", weight=500), + href=page["path"], + icon=DashIconify(icon=page["icon"], height=25), + variant="subtle", + color="gray", + id=page["name"], + ) + for page in dash.page_registry.values() + if page["module"] != "not_found_404" + ], + position="apart", + grow=True, + spacing="xs", + ) + + header = dmc.Header( + dmc.Grid( + children=[ + dmc.Col(brand, span=2, style={"textAlign": "center"}), + dmc.Col(link_group, span=10), + ], + justify="space-around", + align="center", + gutter="xl", + ), + height=55, + style={"backgroundColor": "#FFFFFF"}, + zIndex=99999999, + ) + return html.Div(header) diff --git a/automappa/components/samples_store.py b/automappa/components/samples_store.py new file mode 100644 index 00000000..ae571d9f --- /dev/null +++ b/automappa/components/samples_store.py @@ -0,0 +1,84 @@ +# -*- coding: utf-8 -*- + +import logging +from typing import Literal +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import Input, Output, State, DashProxy, dcc, Serverside +import pandas as pd + +from automappa.data.loader import ( + get_uploaded_files_table, +) +from automappa.components import ids +from automappa.data.database import redis_backend + +logger = logging.getLogger(__name__) + + +def render( + app: DashProxy, + storage_type: Literal["memory", "session", "local"] = "session", + clear_data: bool = False, +) -> dcc.Store: + @app.callback( + Output(ids.SAMPLES_STORE, "data"), + [ + Input(ids.BINNING_MAIN_UPLOAD_STORE, "modified_timestamp"), + Input(ids.MARKERS_UPLOAD_STORE, "modified_timestamp"), + Input(ids.METAGENOME_UPLOAD_STORE, "modified_timestamp"), + Input(ids.CYTOSCAPE_STORE, "modified_timestamp"), + ], + [ + State(ids.BINNING_MAIN_UPLOAD_STORE, "data"), + State(ids.MARKERS_UPLOAD_STORE, "data"), + State(ids.METAGENOME_UPLOAD_STORE, "data"), + State(ids.CYTOSCAPE_STORE, "data"), + State(ids.SAMPLES_STORE, "data"), + ], + ) + def on_upload_stores_data( + binning_uploads_timestamp: str, + markers_uploads_timestamp: str, + metagenome_uploads_timestamp: str, + cytoscape_uploads_timestamp: str, + binning_uploads_df: pd.DataFrame, + markers_uploads_df: pd.DataFrame, + metagenome_uploads_df: pd.DataFrame, + cytoscape_uploads_df: pd.DataFrame, + samples_store_data_df: pd.DataFrame, + ): + if ( + binning_uploads_df is None + and markers_uploads_df is None + and metagenome_uploads_df is None + and cytoscape_uploads_df is None + ) or ( + binning_uploads_timestamp is None + and markers_uploads_timestamp is None + and metagenome_uploads_timestamp is None + and cytoscape_uploads_timestamp is None + ): + # Check if db has any samples in table + uploaded_files_df = get_uploaded_files_table() + if not uploaded_files_df.empty: + return Serverside(uploaded_files_df, backend=redis_backend) + raise PreventUpdate + samples_df = pd.concat( + [ + binning_uploads_df, + markers_uploads_df, + metagenome_uploads_df, + cytoscape_uploads_df, + samples_store_data_df, + ] + ).drop_duplicates(subset=["table_id"]) + logger.debug( + f"{samples_df.shape[0]:,} samples retrieved from data upload stores" + ) + return Serverside(samples_df, backend=redis_backend) + + return dcc.Store( + id=ids.SAMPLES_STORE, + storage_type=storage_type, + clear_data=clear_data, + ) diff --git a/automappa/components/selected_tables_store.py b/automappa/components/selected_tables_store.py new file mode 100644 index 00000000..2e83e52f --- /dev/null +++ b/automappa/components/selected_tables_store.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- + +import itertools +import logging +from typing import Literal +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import Input, Output, DashProxy, Serverside, dcc +from automappa.data.source import SampleTables +from automappa.components import ids +from automappa.tasks import ( + preprocess_clusters_geom_medians, + preprocess_embeddings, + preprocess_marker_symbols, +) +from automappa.data.database import redis_backend + +logger = logging.getLogger(__name__) + + +def render( + app: DashProxy, + storage_type: Literal["memory", "session", "local"] = "session", + clear_data: bool = False, +) -> dcc.Store: + @app.callback( + Output(ids.SELECTED_TABLES_STORE, "data"), + [ + Input(ids.REFINE_MAGS_BUTTON, "n_clicks"), + Input(ids.BINNING_SELECT, "value"), + Input(ids.MARKERS_SELECT, "value"), + Input(ids.METAGENOME_SELECT, "value"), + Input(ids.CYTOSCAPE_SELECT, "value"), + ], + ) + def on_refine_mags_button_click( + n: int, + binning_select_value: str, + markers_select_value: str, + metagenome_select_value: str, + cytoscape_select_value: str, + ): + if n is None: + raise PreventUpdate + tables_dict = {} + if metagenome_select_value is not None: + tables_dict["metagenome"] = {"id": metagenome_select_value} + if binning_select_value is not None: + tables_dict.update( + { + "binning": {"id": binning_select_value}, + "refinements": { + "id": binning_select_value.replace("-binning", "-refinement") + }, + } + ) + if markers_select_value is not None: + tables_dict["markers"] = {"id": markers_select_value} + if cytoscape_select_value is not None: + tables_dict["cytoscape"] = {"id": cytoscape_select_value} + + sample = SampleTables(**tables_dict) + + # BEGIN task-queue submissions + # TODO Refactor to separate bg-task submission (Tasks should be methods for specific components datasources) + # TODO Show table of running tasks for user to monitor... + # TODO Monitor tasks progress with dcc.Interval in another callback... + + # TASK: compute marker symbols + # if sample.binning and sample.markers: + # marker_symbols_task = preprocess_marker_symbols.delay( + # sample.binning.id, sample.markers.id + # ) + + # TASK: compute k-mer freq. embeddings + # NOTE: Possibly use transfer list component to allow user to select which embeddings they want to compute + # https://www.dash-mantine-components.com/components/transferlist + # if sample.metagenome: + # embedding_tasks = [] + # # kmer_sizes = set([kmer_table.size for kmer_table in sample.kmers]) + # kmer_sizes = set([5]) + # # norm_methods = set([kmer_table.norm_method for kmer_table in sample.kmers]) + # norm_methods = set(["am_clr"]) + # embed_methods = set( + # [kmer_table.embed_method for kmer_table in sample.kmers] + # ) + # embed_methods = ["umap", "densmap", "bhsne"] + # for kmer_size, norm_method in itertools.product(kmer_sizes, norm_methods): + # embeddings_task = preprocess_embeddings( + # metagenome_table=sample.metagenome.id, + # kmer_size=kmer_size, + # norm_method=norm_method, + # embed_methods=embed_methods, + # ) + # embedding_tasks.append(embeddings_task) + # TASK: compute geometric medians from cluster assignments + # if sample.binning: + # clusters_geom_medians_task = preprocess_clusters_geom_medians.delay( + # sample.binning.id, "cluster" + # ) + # END task-queue submissions + return Serverside(sample, backend=redis_backend) + + return dcc.Store( + id=ids.SELECTED_TABLES_STORE, + storage_type=storage_type, + clear_data=clear_data, + ) diff --git a/automappa/components/tasks_store.py b/automappa/components/tasks_store.py new file mode 100644 index 00000000..d478aa49 --- /dev/null +++ b/automappa/components/tasks_store.py @@ -0,0 +1,15 @@ +from typing import Literal +from dash_extensions.enrich import dcc, DashProxy +from automappa.components import ids + + +def render( + app: DashProxy, + storage_type: Literal["memory", "session", "local"] = "session", + clear_data: bool = False, +) -> dcc.Store: + return dcc.Store( + id=ids.TASK_ID_STORE, + storage_type=storage_type, + clear_data=clear_data, + ) diff --git a/automappa/conf/celeryconfig.py b/automappa/conf/celeryconfig.py index c6d63de8..a6779639 100644 --- a/automappa/conf/celeryconfig.py +++ b/automappa/conf/celeryconfig.py @@ -1,5 +1,7 @@ +broker_connection_retry_on_startup = True worker_send_task_events = True task_send_sent_event = True worker_prefetch_multiplier = 1 worker_concurrency = 2 task_track_started = True +imports = ("automappa.pages.home.tasks", "automappa.tasks") diff --git a/automappa/data/__init__.py b/automappa/data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/automappa/data/database.py b/automappa/data/database.py new file mode 100644 index 00000000..afd66f86 --- /dev/null +++ b/automappa/data/database.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python +from typing import List +from sqlmodel import SQLModel, create_engine + +from dash_extensions.enrich import RedisBackend, FileSystemBackend + +from automappa import settings + +# SQL DATABASE file +# sqlite_url = f"sqlite:///database.db" +# engine = create_engine(sqlite_url, echo=False) + +# SQL in-memory +# sqlite_url = f"sqlite:///:memory:" +# engine = create_engine(sqlite_url, echo=False) + +# POSTGRES DATABASE +engine = create_engine( + url=settings.db.url, + pool_size=settings.db.pool_size, + pool_pre_ping=settings.db.pool_pre_ping, +) + +redis_backend = RedisBackend( + **dict(host=settings.redis.host, port=settings.redis.port, db=settings.redis.db), + # password=settings.redis.password, +) +file_system_backend = FileSystemBackend(cache_dir=settings.server.root_upload_folder) + + +def create_db_and_tables() -> None: + SQLModel.metadata.create_all(engine) + + +def get_table_names() -> List[str]: + return SQLModel.metadata.tables.keys() + + +def main(): + create_db_and_tables() + + +if __name__ == "__main__": + main() diff --git a/automappa/data/loader.py b/automappa/data/loader.py new file mode 100644 index 00000000..824956f4 --- /dev/null +++ b/automappa/data/loader.py @@ -0,0 +1,513 @@ +#!/usr/bin/env python +# DataLoader for Autometa results ingestion +import logging +from pathlib import Path +import uuid +import pandas as pd + +from functools import partial, reduce +from typing import Callable, List, Optional, Union + +from Bio import SeqIO +from sqlmodel import Session, select, SQLModel + +from automappa.data.schemas import ContigSchema, CytoscapeConnectionSchema, MarkerSchema + +from automappa.settings import server +from automappa.data.database import ( + create_db_and_tables, + engine, + get_table_names, +) +from automappa.data.models import ( + Contig, + Marker, + Metagenome, + CytoscapeConnection, + Refinement, +) + +logging.basicConfig(level=logging.DEBUG) +logger = logging.getLogger(__name__) +numba_logger = logging.getLogger("numba") +numba_logger.setLevel(logging.WARNING) +numba_logger.propagate = False +h5py_logger = logging.getLogger("h5py") +h5py_logger.setLevel(logging.WARNING) +h5py_logger.propagate = False + + +Preprocessor = Callable[[pd.DataFrame], pd.DataFrame] + + +def compose(*functions: Preprocessor) -> Preprocessor: + return reduce(lambda f, g: lambda x: g(f(x)), functions) + + +def sqlmodel_to_df(objects: List[SQLModel], set_index: bool = True) -> pd.DataFrame: + """Converts SQLModel objects into a Pandas DataFrame. + + From https://github.com/tiangolo/sqlmodel/issues/215#issuecomment-1092348993 + + Usage + ---------- + df = sqlmodel_to_df(list_of_sqlmodels) + Parameters + ---------- + :param objects: List[SQLModel]: List of SQLModel objects to be converted. + :param set_index: bool: Sets the first column, usually the primary key, to dataframe index. + """ + + records = [obj.dict() for obj in objects] + columns = list(objects[0].schema()["properties"].keys()) + df = pd.DataFrame.from_records(records, columns=columns) + return df.set_index(columns[0]) if set_index else df + + +def validate_uploader( + is_completed: bool, filenames: List[str], upload_id: uuid.UUID +) -> Union[Path, None]: + """Ensure only one file was uploaded and create Path to uploaded file + + Parameters + ---------- + is_completed : bool + Whether or not the upload has finished + filenames : list + list of filenames + upload_id : uuid + unique user id associated with upload + + Returns + ------- + Path + Server-side path to uploaded file + + Raises + ------ + ValueError + You may only upload one file at a time! + """ + if not is_completed: + return + if filenames is None: + return + if upload_id: + root_folder = server.root_upload_folder / upload_id + else: + root_folder = server.root_upload_folder + + uploaded_files = [] + for filename in filenames: + file = root_folder / filename + uploaded_files.append(file) + if len(uploaded_files) > 1: + raise ValueError("You may only upload one file at a time!") + return uploaded_files[0] + + +def create_metagenome( + name: str, fpath: Optional[str], contigs: Optional[List[Contig]] +) -> Metagenome: + logger.info(f"Adding metagenome from {fpath} to db") + if not contigs: + contigs = [ + Contig(header=record.id, seq=str(record.seq)) + for record in SeqIO.parse(fpath, "fasta") + ] + else: + pass + # Need to ensure Seq column is in contigs otherwise add them + # contig_seq_df = pd.DataFrame( + # [ + # dict(header=record.id, seq=str(record.seq)) + # for record in SeqIO.parse(metagenome_fpath, "fasta") + # ] + # ) + # merge_seq_column = partial(add_seq_column, seqrecord_df=contig_seq_df) + metagenome = Metagenome(name=name, contigs=contigs) + with Session(engine) as session: + session.add(metagenome) + session.commit() + session.refresh(metagenome) + return metagenome + + +def read_metagenome(metagenome_id: int) -> Metagenome: + with Session(engine) as session: + metagenomes = session.exec( + select(Metagenome).where(Metagenome.id == metagenome_id) + ).first() + return metagenomes + + +def read_metagenomes() -> List[Metagenome]: + with Session(engine) as session: + metagenomes = session.exec(select(Metagenome)).all() + return metagenomes + + +def update_metagenomes() -> None: + raise NotImplemented + + +def delete_metagenomes() -> None: + raise NotImplemented + + +def rename_class_column_to_klass(df: pd.DataFrame) -> pd.DataFrame: + return df.rename(columns={ContigSchema.CLASS: ContigSchema.KLASS}) + + +def rename_contig_column_to_header(df: pd.DataFrame) -> pd.DataFrame: + return df.rename(columns={ContigSchema.CONTIG: ContigSchema.HEADER}) + + +def replace_cluster_na_values_with_unclustered(df: pd.DataFrame) -> pd.DataFrame: + return df.fillna(value={ContigSchema.CLUSTER: "unclustered"}) + + +def add_seq_column(df: pd.DataFrame, seqrecord_df: pd.DataFrame) -> pd.DataFrame: + return pd.merge(df, seqrecord_df, on=ContigSchema.HEADER, how="left") + + +def add_markers_column(df: pd.DataFrame, markers_list_df: pd.DataFrame) -> pd.DataFrame: + return pd.merge( + df, + markers_list_df, + left_on=ContigSchema.HEADER, + right_on=MarkerSchema.CONTIG, + how="left", + ) + + +def load_contigs(fpath: str) -> pd.DataFrame: + logger.info(f"Loading contigs: {fpath}") + return pd.read_table( + fpath, + dtype={ + ContigSchema.CONTIG: str, + ContigSchema.CLUSTER: str, + ContigSchema.COMPLETENESS: float, + ContigSchema.PURITY: float, + ContigSchema.COVERAGE_STDDEV: float, + ContigSchema.GC_CONTENT_STDDEV: float, + ContigSchema.COVERAGE: float, + ContigSchema.GC_CONTENT: float, + ContigSchema.LENGTH: int, + ContigSchema.SUPERKINGDOM: str, + ContigSchema.PHYLUM: str, + ContigSchema.CLASS: str, + ContigSchema.ORDER: str, + ContigSchema.FAMILY: str, + ContigSchema.GENUS: str, + ContigSchema.SPECIES: str, + ContigSchema.TAXID: int, + ContigSchema.X_1: float, + ContigSchema.X_2: float, + }, + ) + + +async def create_contigs(fpath: str, markers: Optional[List[Marker]]) -> None: + logger.info(f"Adding binned contigs from {fpath} to db") + raw_data = load_contigs(fpath) + if markers: + merge_markers_column = partial(add_markers_column, markers_list_df=markers) + preprocessor = compose( + rename_class_column_to_klass, + rename_contig_column_to_header, + replace_cluster_na_values_with_unclustered, + merge_markers_column, + ) + data = preprocessor(raw_data) + contigs = df_to_sqlmodel(data, Contig) + contig_df = preprocessor(raw_data) + nonmarker_contigs_mask = contig_df.markers.isna() + nonmarker_contigs = df_to_sqlmodel( + contig_df.loc[nonmarker_contigs_mask].drop(columns=["markers"]), Contig + ) + marker_contigs = df_to_sqlmodel(contig_df.loc[~nonmarker_contigs_mask], Contig) + contigs = nonmarker_contigs + marker_contigs + + with Session(engine) as session: + session.add_all(contigs) + session.commit(contigs) + session.refresh(contigs) + await contigs + + +def read_contigs(headers: List[str] = []) -> List[Contig]: + statement = select(Contig) + if headers: + statement = statement.where(Contig.header.in_(headers)) + with Session(engine) as session: + contigs = session.exec(statement).all() + return contigs + + +def update_contigs(): + raise NotImplemented + + +def delete_contig(contig: Contig) -> None: + with Session(engine) as session: + session.delete(contig) + session.commit() + + +def rename_qname_column_to_orf(df: pd.DataFrame) -> pd.DataFrame: + return df.rename(columns={MarkerSchema.QNAME: MarkerSchema.ORF}) + + +def drop_contig_column(df: pd.DataFrame) -> pd.DataFrame: + return df.drop(columns=[MarkerSchema.CONTIG]) + + +def agg_to_markers_list_column(df: pd.DataFrame) -> pd.DataFrame: + return ( + df.set_index(MarkerSchema.CONTIG) + .apply(func=lambda row: Marker(**row), axis=1, result_type="reduce", raw=False) + .to_frame(name="markers") + .reset_index() + .groupby(MarkerSchema.CONTIG) + .agg({"markers": lambda x: x.tolist()}) + ) + + +def load_markers(fpath: str) -> pd.DataFrame: + logger.info(f"Loading markers: {fpath}") + return pd.read_table( + fpath, + usecols=[ + MarkerSchema.CONTIG, + MarkerSchema.QNAME, + MarkerSchema.SNAME, + MarkerSchema.SACC, + MarkerSchema.FULL_SEQ_SCORE, + MarkerSchema.CUTOFF, + ], + dtype={ + MarkerSchema.CONTIG: str, + MarkerSchema.QNAME: str, + MarkerSchema.SNAME: str, + MarkerSchema.SACC: str, + MarkerSchema.FULL_SEQ_SCORE: float, + MarkerSchema.CUTOFF: float, + }, + ) + + +async def create_markers(fpath: str) -> List[Marker]: + logger.info(f"Adding markers from {fpath} to db") + raw_data = load_markers(fpath) + preprocessor = compose(rename_qname_column_to_orf, drop_contig_column) + data = preprocessor(raw_data) + markers = [Marker(**row) for row in data.to_dict("records")] + with Session(engine) as session: + session.add_all(markers) + session.commit() + session.refresh(markers) + return markers + + +def read_markers(contig_headers: List[str] = None) -> List[Marker]: + statement = select(Marker) + if contig_headers: + statement = statement.where(Marker.contig.header.in_(contig_headers)) + with Session(engine) as session: + markers = session.exec(statement).all() + return markers + + +def update_markers() -> None: + raise NotImplemented + + +def delete_markers() -> None: + raise NotImplemented + + +def load_cytoscape_connections(fpath: str) -> pd.DataFrame: + logger.info(f"Loading cytoscape connections: {fpath}") + return pd.read_table( + fpath, + low_memory=False, + usecols=[ + CytoscapeConnectionSchema.NODE1, + CytoscapeConnectionSchema.INTERACTION, + CytoscapeConnectionSchema.NODE2, + CytoscapeConnectionSchema.CONNECTIONS, + CytoscapeConnectionSchema.MAPPINGTYPE, + CytoscapeConnectionSchema.NAME, + CytoscapeConnectionSchema.CONTIGLENGTH, + ], + dtype={ + CytoscapeConnectionSchema.NODE1: str, + CytoscapeConnectionSchema.INTERACTION: int, + CytoscapeConnectionSchema.NODE2: str, + CytoscapeConnectionSchema.CONNECTIONS: int, + CytoscapeConnectionSchema.MAPPINGTYPE: str, # Literal['intra', 'ss', 'se', 'ee'] + # Below are commented as these are missing when mapping type != intra + # causing pd.read_table(...) to fail... + # CytoscapeConnectionSchema.NAME: str, + # CytoscapeConnectionSchema.CONTIGLENGTH: int, + }, + ) + + +def create_cytoscape_connections(fpath: str) -> None: + logger.info(f"Adding cytoscape connections from {fpath} to db") + cyto_df = load_cytoscape_connections(fpath) + cytoscape_connections = [ + CytoscapeConnection(**record) for record in cyto_df.to_dict("records") + ] + with Session(engine) as session: + session.add_all(cytoscape_connections) + session.commit() + + +def read_cytoscape_connections() -> List[CytoscapeConnection]: + logger.info("Reading cytoscape connections...") + with Session(engine) as session: + results = session.exec(select(CytoscapeConnection)).all() + return results + + +def update_cytoscape_connection() -> None: + raise NotImplemented + + +def delete_cytoscape_connection(connection: CytoscapeConnection) -> None: + with Session(engine) as session: + session.delete(connection) + session.commit() + + +def create_sample_metagenome( + name: str, + metagenome_fpath: str, + binning_fpath: str, + markers_fpath: str, + connections_fpath: Optional[str] = None, +) -> Metagenome: + logger.info(f"Creating Metagenome {name=}") + raw_markers = load_markers(markers_fpath) + marker_preprocessor = compose( + rename_qname_column_to_orf, agg_to_markers_list_column + ) + contig_markers_df = marker_preprocessor(raw_markers) + + contig_seq_df = pd.DataFrame( + [ + dict(header=record.id, seq=str(record.seq)) + for record in SeqIO.parse(metagenome_fpath, "fasta") + ] + ) + merge_seq_column = partial(add_seq_column, seqrecord_df=contig_seq_df) + merge_markers_column = partial( + add_markers_column, markers_list_df=contig_markers_df + ) + contig_preprocessor = compose( + rename_class_column_to_klass, + rename_contig_column_to_header, + replace_cluster_na_values_with_unclustered, + merge_seq_column, + merge_markers_column, + ) + raw_binning = load_contigs(binning_fpath) + contig_df = contig_preprocessor(raw_binning) + nonmarker_contigs_mask = contig_df.markers.isna() + nonmarker_contigs = [ + Contig(**record) + for record in contig_df.loc[nonmarker_contigs_mask] + .drop(columns=["markers"]) + .to_dict("records") + ] + marker_contigs = [ + Contig(**record) + for record in contig_df.loc[~nonmarker_contigs_mask].to_dict("records") + ] + contigs = nonmarker_contigs + marker_contigs + # Add cytoscape connection mapping if available + if connections_fpath: + connections_df = load_cytoscape_connections(connections_fpath) + connections = [ + CytoscapeConnection(**record) + for record in connections_df.to_dict("records") + ] + else: + connections = [] + + metagenome = Metagenome( + name=name, contigs=contigs, connections=connections, refinements=[] + ) + with Session(engine) as session: + session.add(metagenome) + session.commit() + session.refresh(metagenome) + return metagenome + + +def create_initial_refinements(metagenome_id: int) -> None: + """Initialize Contig.refinements for contigs with Contig.cluster values + + Parameters + ---------- + metagenome_id : int + Metagenome.id value corresponding to Contigs + """ + clusters_stmt = ( + select([Contig.cluster]) + .where( + Contig.metagenome_id == metagenome_id, + Contig.cluster != None, + Contig.cluster != "nan", + Contig.cluster != "unclustered", + ) + .distinct() + ) + with Session(engine) as session: + clusters = session.exec(clusters_stmt).all() + + for cluster in clusters: + contigs_stmt = select(Contig).where(Contig.cluster == cluster) + contigs = session.exec(contigs_stmt).all() + + refinement = Refinement( + contigs=contigs, + outdated=False, + initial_refinement=True, + metagenome_id=metagenome_id, + ) + session.add(refinement) + session.commit() + + +def main(): + # init database and tables + create_db_and_tables() + table_names = get_table_names() + print(f"db table names: {', '.join(table_names)}") + + # CRUD sample (this will take some time with the connection mapping...) + # NOTE: Create two samples for testing... + sponge_mg = create_sample_metagenome( + name="lasonolide", + metagenome_fpath="data/lasonolide/metagenome.filtered.fna", + binning_fpath="data/lasonolide/binning.tsv", + markers_fpath="data/lasonolide/bacteria.markers.tsv", + # connections_fpath="data/lasonolide/cytoscape.connections.tab", + ) + create_initial_refinements(sponge_mg.id) + nubbins_mg = create_sample_metagenome( + name="nubbins", + metagenome_fpath="data/nubbins/scaffolds.fasta", + binning_fpath="data/nubbins/nubbins.tsv", + markers_fpath="data/nubbins/bacteria.markers.tsv", + ) + create_initial_refinements(nubbins_mg.id) + + +if __name__ == "__main__": + main() diff --git a/automappa/data/models.py b/automappa/data/models.py new file mode 100644 index 00000000..75df5cd6 --- /dev/null +++ b/automappa/data/models.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +from typing import List, Optional +from sqlmodel import Field, Relationship, SQLModel +from datetime import datetime, timezone + + +def utc_now() -> datetime: + return datetime.now(tz=timezone.utc) + + +class Metagenome(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + name: str + contigs: List["Contig"] = Relationship(back_populates="metagenome") + refinements: List["Refinement"] = Relationship(back_populates="metagenome") + connections: List["CytoscapeConnection"] = Relationship(back_populates="metagenome") + + +class ContigRefinementLink(SQLModel, table=True): + refinement_id: Optional[int] = Field( + default=None, foreign_key="refinement.id", primary_key=True + ) + contig_id: Optional[int] = Field( + default=None, foreign_key="contig.id", primary_key=True + ) + + +class Refinement(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + timestamp: datetime = Field(default=utc_now(), index=True) + outdated: bool = False + initial_refinement: bool = False + contigs: List["Contig"] = Relationship( + back_populates="refinements", link_model=ContigRefinementLink + ) + metagenome_id: Optional[int] = Field(default=None, foreign_key="metagenome.id") + metagenome: Optional[Metagenome] = Relationship(back_populates="refinements") + + +class Contig(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + header: str = Field(index=True) + seq: Optional[str] + cluster: Optional[str] = Field(index=True) + completeness: Optional[float] + purity: Optional[float] + coverage_stddev: Optional[float] + gc_content_stddev: Optional[float] + coverage: Optional[float] = Field(index=True) + gc_content: Optional[float] + length: Optional[int] + superkingdom: Optional[str] + phylum: Optional[str] + klass: Optional[str] + order: Optional[str] + family: Optional[str] + genus: Optional[str] + species: Optional[str] + taxid: Optional[int] + x_1: Optional[float] + x_2: Optional[float] + marker_symbol: Optional[str] + marker_size: Optional[int] + refinements: Optional[List[Refinement]] = Relationship( + back_populates="contigs", link_model=ContigRefinementLink + ) + metagenome_id: Optional[int] = Field(default=None, foreign_key="metagenome.id") + metagenome: Optional[Metagenome] = Relationship(back_populates="contigs") + markers: Optional[List["Marker"]] = Relationship(back_populates="contig") + + +class Marker(SQLModel, table=True): + id: Optional[int] = Field(default=None, primary_key=True) + orf: str # qname + sacc: str + sname: str + full_seq_score: float = Field(index=True) + cutoff: float = Field(index=True) + contig_id: Optional[int] = Field(default=None, foreign_key="contig.id") + contig: Contig = Relationship(back_populates="markers") + + +class CytoscapeConnection(SQLModel, table=True): + __tablename__ = "cytoscape_connection" + id: Optional[int] = Field(default=None, primary_key=True) + node1: str + interaction: int + node2: str + connections: int + mappingtype: str # Literal["intra", "ss", "se", "ee"] + name: Optional[str] + contiglength: Optional[int] + metagenome_id: Optional[int] = Field(default=None, foreign_key="metagenome.id") + metagenome: Optional[Metagenome] = Relationship(back_populates="connections") diff --git a/automappa/data/schemas.py b/automappa/data/schemas.py new file mode 100644 index 00000000..670ecb41 --- /dev/null +++ b/automappa/data/schemas.py @@ -0,0 +1,47 @@ +class MetagenomeSchema: + pass + + +class ContigSchema: + CONTIG = "contig" + HEADER = "header" + CLUSTER = "cluster" + COMPLETENESS = "completeness" + PURITY = "purity" + COVERAGE_STDDEV = "coverage_stddev" + GC_CONTENT_STDDEV = "gc_content_stddev" + COVERAGE = "coverage" + GC_CONTENT = "gc_content" + LENGTH = "length" + SUPERKINGDOM = "superkingdom" + DOMAIN = "domain" + PHYLUM = "phylum" + CLASS = "class" + KLASS = "klass" + ORDER = "order" + FAMILY = "family" + GENUS = "genus" + SPECIES = "species" + TAXID = "taxid" + X_1 = "x_1" + X_2 = "x_2" + + +class MarkerSchema: + QNAME = "qname" + ORF = "orf" + SACC = "sacc" + SNAME = "sname" + FULL_SEQ_SCORE = "full_seq_score" + CUTOFF = "cutoff" + CONTIG = "contig" + + +class CytoscapeConnectionSchema: + NODE1 = "node1" + INTERACTION = "interaction" + NODE2 = "node2" + CONNECTIONS = "connections" + MAPPINGTYPE = "mappingtype" + NAME = "name" + CONTIGLENGTH = "contiglength" diff --git a/automappa/db.py b/automappa/db.py deleted file mode 100644 index 26f8d9bf..00000000 --- a/automappa/db.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python - -from sqlalchemy import create_engine, MetaData - -# from sqlalchemy import create_engine, MetaData, Table -# from sqlalchemy import inspect - -# User inspector for retrieving db info: -# Inspector: https://docs.sqlalchemy.org/en/14/core/reflection.html?highlight=inspector%20has_table#sqlalchemy.engine.reflection.Inspector -# has_table: https://docs.sqlalchemy.org/en/14/core/reflection.html?highlight=has_table#sqlalchemy.engine.reflection.Inspector.has_table - -from automappa.settings import db - -engine = create_engine( - url=db.url, - pool_size=db.pool_size, - pool_pre_ping=db.pool_pre_ping, -) - -metadata = MetaData(bind=engine) - -# inspector = inspect(engine) -# user_table = Table('user', metadata) -# insp.reflect_table(user_table, None) diff --git a/automappa/index.py b/automappa/index.py deleted file mode 100755 index 6261d321..00000000 --- a/automappa/index.py +++ /dev/null @@ -1,146 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import argparse -import logging - -from dash_extensions.enrich import Input, Output -from dash import dcc, html -import dash_bootstrap_components as dbc - -from automappa import settings -from automappa.apps import home, mag_refinement, mag_summary -from automappa.app import app -from automappa.utils.models import SampleTables - -logging.basicConfig( - format="[%(levelname)s] %(name)s: %(message)s", - level=logging.DEBUG, -) - -logger = logging.getLogger(__name__) - - -@app.callback( - Output("tab-content", "children"), - Input("tabs", "active_tab"), - Input("selected-tables-store", "data"), -) -def render_content( - active_tab: str, - selected_tables_data: SampleTables, -) -> dbc.Container: - # Only alow user to navigate to mag refinement or summary if data is already uploaded - if selected_tables_data is None: - return home.layout - sample = SampleTables.parse_raw(selected_tables_data) - if not sample.binning or not sample.markers: - return home.layout - layouts = { - "home": home.layout, - "mag_refinement": mag_refinement.layout, - "mag_summary": mag_summary.layout, - } - return layouts.get(active_tab, "home") - - -def main(): - parser = argparse.ArgumentParser( - description="Automappa: An interactive interface for exploration of metagenomes", - formatter_class=argparse.RawTextHelpFormatter, - ) - parser.add_argument( - "--storage-type", - help=( - "The type of the web storage. (default: %(default)s)\n" - "- memory: only kept in memory, reset on page refresh.\n" - "- session: data is cleared once the browser quit.\n" - "- local: data is kept after the browser quit.\n" - ), - choices=["memory", "session", "local"], - default="session", - ) - parser.add_argument( - "--clear-store-data", - help=( - "Clear storage data (default: %(default)s)\n" - "(only required if using 'session' or 'local' for `--storage-type`)" - ), - action="store_true", - default=False, - ) - args = parser.parse_args() - - # contig_marker_symbols_store = dcc.Store( - # id="contig-marker-symbols-store", - # storage_type=args.storage_type, - # data=contig_marker_symbols.to_json(orient="split"), - # clear_data=args.clear_store_data, - # ) - binning_main_upload_store = dcc.Store( - id="binning-main-upload-store", - storage_type=args.storage_type, - clear_data=args.clear_store_data, - ) - markers_upload_store = dcc.Store( - id="markers-upload-store", - storage_type=args.storage_type, - clear_data=args.clear_store_data, - ) - metagenome_upload_store = dcc.Store( - id="metagenome-upload-store", - storage_type=args.storage_type, - clear_data=args.clear_store_data, - ) - samples_store = dcc.Store( - id="samples-store", - storage_type=args.storage_type, - clear_data=args.clear_store_data, - ) - selected_samples_store = dcc.Store( - id="selected-tables-store", - storage_type=args.storage_type, - clear_data=args.clear_store_data, - ) - - if args.clear_store_data: - logger.info( - f"Store data cleared. Now re-run automappa *without* --clear-store-data" - ) - exit() - - home_tab = dbc.Tab(label="Home", tab_id="home") - refinement_tab = dbc.Tab(label="MAG Refinement", tab_id="mag_refinement") - summary_tab = dbc.Tab(label="MAG Summary", tab_id="mag_summary") - - app.layout = dbc.Container( - [ - # dbc.Col(contig_marker_symbols_store), - dbc.Col(binning_main_upload_store), - dbc.Col(markers_upload_store), - dbc.Col(metagenome_upload_store), - dbc.Col(samples_store), - dbc.Col(selected_samples_store), - # Navbar - dbc.Tabs( - id="tabs", - children=[home_tab, refinement_tab, summary_tab], - className="nav-fill", - ), - html.Div(id="tab-content"), - ], - fluid=True, - ) - - # TODO: Replace cli inputs (as well as updating title once file is uploaded...) - # app.title = f"Automappa: {sample_name}" - app.title = "Automappa" - app.run_server( - host=settings.server.host, - port=settings.server.port, - debug=settings.server.debug, - ) - - -if __name__ == "__main__": - main() diff --git a/automappa/pages/__init__.py b/automappa/pages/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/automappa/pages/home/__init__.py b/automappa/pages/home/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/automappa/pages/home/components/__init__.py b/automappa/pages/home/components/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/automappa/pages/home/components/binning_select.py b/automappa/pages/home/components/binning_select.py new file mode 100644 index 00000000..1a761906 --- /dev/null +++ b/automappa/pages/home/components/binning_select.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +import logging +from typing import Dict, List +import pandas as pd + +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import Input, Output, State, DashProxy, html +import dash_mantine_components as dmc +from dash_iconify import DashIconify + +from automappa.components import ids + + +logger = logging.getLogger(__name__) + + +def render(app: DashProxy) -> html.Div: + @app.callback( + Output(ids.BINNING_SELECT, "data"), + [Input(ids.SAMPLES_STORE, "data")], + State(ids.SAMPLES_STORE, "data"), + ) + def binning_select_options( + samples_df: pd.DataFrame, new_samples_df: pd.DataFrame + ) -> List[Dict[str, str]]: + if samples_df is None or samples_df.empty: + raise PreventUpdate + if new_samples_df is not None: + samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( + subset=["table_id"] + ) + df = samples_df.loc[samples_df.filetype.eq("binning")] + logger.debug(f"{df.shape[0]:,} binning available for mag_refinement") + return [ + { + "label": filename, + "value": table_id, + } + for filename, table_id in zip(df.filename.tolist(), df.table_id.tolist()) + ] + + return html.Div( + dmc.Select( + id=ids.BINNING_SELECT, + label="Binning", + placeholder="Select binning annotations", + icon=[DashIconify(icon="ph:chart-scatter-bold")], + rightSection=[DashIconify(icon="radix-icons:chevron-down")], + persistence=True, + persistence_type="session", + ) + ) diff --git a/automappa/pages/home/components/binning_upload.py b/automappa/pages/home/components/binning_upload.py new file mode 100644 index 00000000..4ea86365 --- /dev/null +++ b/automappa/pages/home/components/binning_upload.py @@ -0,0 +1,61 @@ +import logging +from typing import List, Protocol +from uuid import UUID +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import DashProxy, Output, Input, State, dcc, html +import dash_uploader as du +from automappa.components import ids + + +logger = logging.getLogger(__name__) + + +class UploadDataSource(Protocol): + def validate_uploader_path( + self, is_completed: bool, filenames: List[str], upload_id: UUID + ) -> str: + ... + + +def render(app: DashProxy, source: UploadDataSource) -> html.Div: + @app.callback( + Output(ids.BINNING_MAIN_UPLOAD_STORE, "data"), + Input(ids.BINNING_UPLOAD, "isCompleted"), + State(ids.BINNING_UPLOAD, "fileNames"), + State(ids.BINNING_UPLOAD, "upload_id"), + prevent_initial_call=True, + ) + def on_binning_main_upload( + is_completed: bool, filenames: List[str], upload_id: UUID + ): + try: + filepath = source.validate_uploader_path(is_completed, filenames, upload_id) + except ValueError as err: + logger.warn(err) + raise PreventUpdate + if not filepath: + raise PreventUpdate + return filepath + + return html.Div( + [ + dcc.Store( + id=ids.BINNING_MAIN_UPLOAD_STORE, + storage_type="session", + clear_data=False, + ), + du.Upload( + id=ids.BINNING_UPLOAD, + text="Drag and Drop or Select binning-main file", + default_style={ + "width": "100%", + "borderWidth": "1px", + "borderStyle": "dashed", + "borderRadius": "5px", + "margin": "10px", + }, + max_files=1, + max_file_size=10240, + ), + ] + ) diff --git a/automappa/pages/home/components/cytoscape_connections_select.py b/automappa/pages/home/components/cytoscape_connections_select.py new file mode 100644 index 00000000..6f8fcaef --- /dev/null +++ b/automappa/pages/home/components/cytoscape_connections_select.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +import logging +from typing import Dict, List +import pandas as pd + +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import Input, Output, State, DashProxy, html +import dash_mantine_components as dmc +from dash_iconify import DashIconify +from automappa.components import ids + + +logger = logging.getLogger(__name__) + + +def render(app: DashProxy) -> html.Div: + @app.callback( + Output(ids.CYTOSCAPE_SELECT, "data"), + [Input(ids.SAMPLES_STORE, "data")], + State(ids.SAMPLES_STORE, "data"), + ) + def cytoscape_select_options( + samples_df: pd.DataFrame, new_samples_df: pd.DataFrame + ) -> List[Dict[str, str]]: + if samples_df is None or samples_df.empty: + raise PreventUpdate + if new_samples_df is not None: + samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( + subset=["table_id"] + ) + + if samples_df.empty: + raise PreventUpdate + + df = samples_df.loc[samples_df.filetype.eq("cytoscape")] + logger.debug(f"{df.shape[0]:,} cytoscapes available for mag_refinement") + return [ + { + "label": filename, + "value": table_id, + } + for filename, table_id in zip(df.filename.tolist(), df.table_id.tolist()) + ] + + return html.Div( + dmc.Select( + id=ids.CYTOSCAPE_SELECT, + label="Cytoscape connections", + placeholder="Select cytoscape connections annotations", + icon=[DashIconify(icon="bx:network-chart")], + rightSection=[DashIconify(icon="radix-icons:chevron-down")], + persistence=True, + persistence_type="session", + ) + ) diff --git a/automappa/pages/home/components/cytoscape_connections_upload.py b/automappa/pages/home/components/cytoscape_connections_upload.py new file mode 100644 index 00000000..bee1232b --- /dev/null +++ b/automappa/pages/home/components/cytoscape_connections_upload.py @@ -0,0 +1,60 @@ +import logging +from typing import List, Protocol +from uuid import UUID +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import DashProxy, Input, Output, State, dcc, html +import dash_uploader as du +from automappa.components import ids + +logger = logging.getLogger(__name__) + + +class UploadDataSource(Protocol): + def validate_uploader_path( + self, is_completed: bool, filenames: List[str], upload_id: UUID + ) -> str: + ... + + +def render(app: DashProxy, source: UploadDataSource) -> html.Div: + @app.callback( + Output(ids.CYTOSCAPE_STORE, "data"), + Input(ids.CYTOSCAPE_UPLOAD, "isCompleted"), + State(ids.CYTOSCAPE_UPLOAD, "fileNames"), + State(ids.CYTOSCAPE_UPLOAD, "upload_id"), + prevent_initial_call=True, + ) + def cytoscape_uploader_callback( + is_completed: bool, filenames: List[str], upload_id: UUID + ): + try: + filepath = source.validate_uploader_path(is_completed, filenames, upload_id) + except ValueError as err: + logger.warn(err) + raise PreventUpdate + if not filepath: + raise PreventUpdate + return filepath + + return html.Div( + [ + dcc.Store( + id=ids.CYTOSCAPE_STORE, + storage_type="session", + clear_data=False, + ), + du.Upload( + id=ids.CYTOSCAPE_UPLOAD, + text="Drag and Drop or Select cytoscape contig connections file", + default_style={ + "width": "100%", + "borderWidth": "1px", + "borderStyle": "dashed", + "borderRadius": "5px", + "margin": "10px", + }, + max_files=1, + max_file_size=10240, + ), + ] + ) diff --git a/automappa/pages/home/components/markers_select.py b/automappa/pages/home/components/markers_select.py new file mode 100644 index 00000000..7fd0e31f --- /dev/null +++ b/automappa/pages/home/components/markers_select.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +import logging +from typing import Dict, List +import pandas as pd + +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import Input, Output, State, DashProxy, html +import dash_mantine_components as dmc +from dash_iconify import DashIconify + +from automappa.components import ids + + +logger = logging.getLogger(__name__) + + +def render(app: DashProxy) -> html.Div: + @app.callback( + Output(ids.MARKERS_SELECT, "data"), + [Input(ids.SAMPLES_STORE, "data")], + State(ids.SAMPLES_STORE, "data"), + ) + def markers_select_options( + samples_df: pd.DataFrame, new_samples_df: pd.DataFrame + ) -> List[Dict[str, str]]: + if samples_df is None or samples_df.empty: + raise PreventUpdate + if new_samples_df is not None: + samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( + subset=["table_id"] + ) + + markers_samples = samples_df.loc[samples_df.filetype.eq("markers")] + logger.debug( + f"{markers_samples.shape[0]:,} markers available for mag_refinement" + ) + return [ + { + "label": filename, + "value": table_id, + } + for filename, table_id in zip( + markers_samples.filename.tolist(), markers_samples.table_id.tolist() + ) + ] + + return html.Div( + dmc.Select( + id=ids.MARKERS_SELECT, + label="Markers", + placeholder="Select marker annotations", + icon=[DashIconify(icon="line-md:document-report")], + rightSection=[DashIconify(icon="radix-icons:chevron-down")], + persistence=True, + persistence_type="session", + ) + ) diff --git a/automappa/pages/home/components/markers_upload.py b/automappa/pages/home/components/markers_upload.py new file mode 100644 index 00000000..c6fdd0d3 --- /dev/null +++ b/automappa/pages/home/components/markers_upload.py @@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- + +import logging +from typing import List, Protocol +from uuid import UUID +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import Input, Output, State, DashProxy, dcc, html +from automappa.components import ids + +import dash_uploader as du + +logger = logging.getLogger(__name__) + + +class UploadDataSource(Protocol): + def validate_uploader_path( + self, is_completed: bool, filenames: List[str], upload_id: UUID + ) -> str: + ... + + +def render(app: DashProxy, source: UploadDataSource) -> html.Div: + @app.callback( + Output(ids.MARKERS_UPLOAD_STORE, "data"), + Input(ids.MARKERS_UPLOAD, "isCompleted"), + State(ids.MARKERS_UPLOAD, "fileNames"), + State(ids.MARKERS_UPLOAD, "upload_id"), + prevent_initial_call=True, + ) + def on_markers_upload(is_completed: bool, filenames: List[str], upload_id: UUID): + try: + filepath = source.validate_uploader_path(is_completed, filenames, upload_id) + except ValueError as err: + logger.warn(err) + raise PreventUpdate + if not filepath: + raise PreventUpdate + return filepath + + return html.Div( + [ + dcc.Store( + id=ids.MARKERS_UPLOAD_STORE, + storage_type="session", + clear_data=False, + ), + du.Upload( + id=ids.MARKERS_UPLOAD, + text="Drag and Drop or Select marker annotations file", + default_style={ + "width": "100%", + "borderWidth": "1px", + "borderStyle": "dashed", + "borderRadius": "5px", + "margin": "10px", + }, + max_files=1, + # 10240 MB = 10GB + max_file_size=10240, + ), + ] + ) diff --git a/automappa/pages/home/components/metagenome_select.py b/automappa/pages/home/components/metagenome_select.py new file mode 100644 index 00000000..c65590c4 --- /dev/null +++ b/automappa/pages/home/components/metagenome_select.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +import logging +from typing import Dict, List +import pandas as pd + +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import Input, Output, State, DashProxy, html +import dash_mantine_components as dmc +from dash_iconify import DashIconify +from automappa.components import ids + + +logger = logging.getLogger(__name__) + + +def render(app: DashProxy) -> html.Div: + @app.callback( + Output(ids.METAGENOME_SELECT, "data"), + [Input(ids.SAMPLES_STORE, "data")], + State(ids.SAMPLES_STORE, "data"), + ) + def metagenome_select_options( + samples_df: pd.DataFrame, new_samples_df: pd.DataFrame + ) -> List[Dict[str, str]]: + if samples_df is None or samples_df.empty: + raise PreventUpdate + if new_samples_df is not None: + samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( + subset=["table_id"] + ) + + df = samples_df.loc[samples_df.filetype.eq("metagenome")] + logger.debug(f"{df.shape[0]:,} metagenomes available for mag_refinement") + return [ + { + "label": filename, + "value": table_id, + } + for filename, table_id in zip(df.filename.tolist(), df.table_id.tolist()) + ] + + return html.Div( + dmc.Select( + id=ids.METAGENOME_SELECT, + label="Metagenome", + placeholder="Select metagenome annotations", + icon=[DashIconify(icon="ph:dna-bold")], + rightSection=[DashIconify(icon="radix-icons:chevron-down")], + persistence=True, + persistence_type="session", + ) + ) diff --git a/automappa/pages/home/components/metagenome_upload.py b/automappa/pages/home/components/metagenome_upload.py new file mode 100644 index 00000000..e26e5623 --- /dev/null +++ b/automappa/pages/home/components/metagenome_upload.py @@ -0,0 +1,63 @@ +# -*- coding: utf-8 -*- + +import logging +from typing import List, Protocol +from uuid import UUID +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import Input, Output, State, DashProxy, dcc, html +import dash_uploader as du + +from automappa.components import ids + +logger = logging.getLogger(__name__) + + +class UploadDataSource(Protocol): + def validate_uploader_path( + self, is_completed: bool, filenames: List[str], upload_id: UUID + ) -> str: + ... + + +def render(app: DashProxy, source: UploadDataSource) -> html.Div: + @app.callback( + Output(ids.METAGENOME_UPLOAD_STORE, "data"), + Input(ids.METAGENOME_UPLOAD, "isCompleted"), + State(ids.METAGENOME_UPLOAD, "fileNames"), + State(ids.METAGENOME_UPLOAD, "upload_id"), + prevent_initial_call=True, + ) + def on_metagenome_upload(is_completed: bool, filenames: List[str], upload_id: UUID): + if not is_completed: + raise PreventUpdate + try: + filepath = source.validate_uploader_path(is_completed, filenames, upload_id) + except ValueError as err: + logger.warn(err) + raise PreventUpdate + if not filepath: + raise PreventUpdate + return filepath + + return html.Div( + [ + dcc.Store( + id=ids.METAGENOME_UPLOAD_STORE, + storage_type="session", + clear_data=False, + ), + du.Upload( + id=ids.METAGENOME_UPLOAD, + text="Drag and Drop or Select metagenome assembly", + default_style={ + "width": "100%", + "borderWidth": "1px", + "borderStyle": "dashed", + "borderRadius": "5px", + "margin": "10px", + }, + max_files=1, + max_file_size=10240, + ), + ] + ) diff --git a/automappa/pages/home/components/refine_mags_button.py b/automappa/pages/home/components/refine_mags_button.py new file mode 100644 index 00000000..e00f52ea --- /dev/null +++ b/automappa/pages/home/components/refine_mags_button.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +import logging +from dash_extensions.enrich import DashProxy, Input, Output, html +import dash_mantine_components as dmc +from dash_iconify import DashIconify + +from automappa.components import ids + + +logger = logging.getLogger(__name__) + + +def render(app: DashProxy) -> html.Div: + @app.callback( + Output(ids.REFINE_MAGS_BUTTON, "disabled"), + [ + Input(ids.BINNING_SELECT, "value"), + Input(ids.MARKERS_SELECT, "value"), + Input(ids.METAGENOME_SELECT, "value"), + ], + ) + def disable_button_callback( + binning_value: str, markers_value: str, metagenome_value: str + ) -> bool: + return ( + binning_value is None or markers_value is None or metagenome_value is None + ) + + return html.Div( + dmc.Button( + "Refine MAGs", + id=ids.REFINE_MAGS_BUTTON, + leftIcon=[DashIconify(icon="mingcute:broom-line", width=25)], + variant="gradient", + gradient={"from": "#642E8D", "to": "#1f58a6", "deg": 150}, + fullWidth=True, + ) + ) diff --git a/automappa/pages/home/components/sample_card.py b/automappa/pages/home/components/sample_card.py new file mode 100644 index 00000000..0ae43956 --- /dev/null +++ b/automappa/pages/home/components/sample_card.py @@ -0,0 +1,260 @@ +from dash_iconify import DashIconify +import dash_mantine_components as dmc + +from automappa.components import ids +from typing import Optional, Protocol, Tuple, Union + + +class SampleCardDataSource(Protocol): + def get_metagenome_name(self, metagenome_id: int) -> str: + """Get Metagenome.name where Metagenome.id == metagenome_id""" + ... + + def contig_count(self, metagenome_id: int) -> int: + ... + + def marker_count(self, metagenome_id: int) -> int: + ... + + def connections_count(self, metagenome_id: int) -> int: + ... + + def get_approximate_marker_sets(self, metagenome_id: int) -> int: + ... + + def get_mimag_counts(self, metagenome_id: int) -> Tuple[int, int, int]: + """Retrieve counts of clusters following MIMAG standards. + + standards: + + - High-quality >90% complete > 95% pure + - Medium-quality >=50% complete > 90% pure + - Low-quality <50% complete < 90% pure + + """ + ... + + def get_refinements_count( + self, metagenome_id: int, initial: Optional[bool], outdated: Optional[bool] + ) -> int: + """Get Refinement count where Refinement.metagenome_id == metagenome_id + + Providing `initial` will add where(Refinement.initial_refinement == True) + otherwise will omit this filter + + Providing `outdated` will add where(Refinement.outdated == outdated) + otherwise will omit this filter + """ + ... + + def get_refined_contig_count(self, metagenome_id: int) -> int: + ... + + +def get_badge(label: str, id: dict[str, Union[str, int]], color: str) -> dmc.Badge: + return dmc.Badge(label, id=id, color=color, variant="dot", size="xs") + + +def render(source: SampleCardDataSource, metagenome_id: int) -> dmc.Card: + metagenome_badge = get_badge( + label=ids.SAMPLE_CARD_METAGENOME_BADGE_LABEL, + id={ + ids.SAMPLE_CARD_INDEX: metagenome_id, + "type": ids.SAMPLE_CARD_METAGENOME_BADGE_TYPE, + }, + color="lime", + ) + contig_count = source.contig_count(metagenome_id) + binning_badge = get_badge( + label=f"{ids.SAMPLE_CARD_BINNING_BADGE_LABEL}: {contig_count:,}", + id={ + ids.SAMPLE_CARD_INDEX: metagenome_id, + "type": ids.SAMPLE_CARD_BINNING_BADGE_TYPE, + }, + color="lime", + ) + marker_count = source.marker_count(metagenome_id) + marker_badge = get_badge( + label=f"{ids.SAMPLE_CARD_MARKERS_BADGE_LABEL}: {marker_count:,}", + id={ + ids.SAMPLE_CARD_INDEX: metagenome_id, + "type": ids.SAMPLE_CARD_MARKERS_BADGE_TYPE, + }, + color="lime", + ) + connections_count = source.connections_count(metagenome_id) + connections_badge = get_badge( + label=ids.SAMPLE_CARD_CONNECTIONS_BADGE_LABEL, + id={ + ids.SAMPLE_CARD_INDEX: metagenome_id, + "type": ids.SAMPLE_CARD_CONNECTIONS_BADGE_TYPE, + }, + color="lime" if connections_count > 0 else "red", + ) + badges = [ + metagenome_badge, + binning_badge, + marker_badge, + connections_badge, + ] + chip = dmc.Chip( + "Select", + id={ + ids.SAMPLE_CARD_INDEX: metagenome_id, + "type": ids.SAMPLE_CARD_CHIP_TYPE, + }, + size="sm", + variant="outline", + radius="xl", + checked=False, + ) + high_quality, medium_quality, low_quality = source.get_mimag_counts(metagenome_id) + hiqh_quality_badge = dmc.Tooltip( + label=">95% complete & >90% pure", + position="top-end", + offset=3, + children=dmc.Badge(high_quality, color="lime", variant="filled"), + ) + medium_quality_badge = dmc.Tooltip( + label=(">=50% complete & >90% pure"), + position="top", + offset=3, + children=dmc.Badge(medium_quality, color="yellow", variant="filled"), + ) + low_quality_badge = dmc.Tooltip( + label="<50% complete & <90% pure", + position="top-start", + offset=3, + children=dmc.Badge(low_quality, color="orange", variant="filled"), + ) + mimag_section = dmc.Center( + dmc.Group( + [ + hiqh_quality_badge, + dmc.Divider(orientation="vertical", style={"height": 20}), + medium_quality_badge, + dmc.Divider(orientation="vertical", style={"height": 20}), + low_quality_badge, + ], + ), + ) + approx_markers = dmc.Group( + [ + dmc.Text(f"Approx. Markers Sets:", size="xs"), + dmc.Badge( + source.get_approximate_marker_sets(metagenome_id), + size="xs", + variant="outline", + color="gray", + ), + ], + position="apart", + ) + uploaded_clusters = dmc.Group( + [ + dmc.Text(f"Uploaded Clusters:", size="xs"), + dmc.Badge( + source.get_refinements_count(metagenome_id, initial=True), + size="xs", + variant="outline", + color="gray", + ), + ], + position="apart", + ) + user_refinements = dmc.Group( + [ + dmc.Text(f"User Refinements:", size="xs"), + dmc.Badge( + source.get_refinements_count( + metagenome_id, initial=False, outdated=False + ), + size="xs", + variant="outline", + color="gray", + ), + ], + position="apart", + ) + current_refinements = dmc.Group( + [ + dmc.Text(f"Current Refinements:", size="xs"), + dmc.Badge( + source.get_refinements_count(metagenome_id, outdated=False), + size="xs", + variant="outline", + ), + ], + position="apart", + ) + refined_contig_count = source.get_refined_contig_count(metagenome_id) + percent_clustered = round(refined_contig_count / contig_count * 100, 2) + percent_clustered_text = dmc.Group( + [ + dmc.Text(f"Contigs Clustered (%):", size="xs"), + dmc.Badge(percent_clustered, size="xs", variant="outline"), + ], + position="apart", + ) + return dmc.Card( + id={ids.SAMPLE_CARD_INDEX: metagenome_id, "type": ids.SAMPLE_CARD_TYPE}, + children=[ + dmc.CardSection( + dmc.Group( + [ + dmc.Text( + source.get_metagenome_name(metagenome_id) + .replace("_", " ") + .title(), + weight=500, + ), + dmc.Tooltip( + dmc.ActionIcon( + DashIconify(icon="tabler:trash-x", width=18), + id={ + ids.SAMPLE_CARD_INDEX: metagenome_id, + "type": ids.SAMPLE_CARD_REMOVE_BTN, + }, + variant="filled", + n_clicks=0, + size="md", + radius="md", + color="red", + ), + label="Delete Sample", + position="top", + color="red", + radius="xl", + offset=3, + ), + ], + position="apart", + ), + withBorder=True, + inheritPadding=True, + py="xs", + ), + dmc.Space(h=10), + dmc.SimpleGrid(cols=2, children=badges), + dmc.Space(h=10), + dmc.Text("MIMAG cluster counts", size="sm"), + dmc.Space(h=5), + mimag_section, + dmc.Space(h=10), + dmc.Divider(variant="dotted"), + dmc.Space(h=10), + approx_markers, + uploaded_clusters, + user_refinements, + current_refinements, + percent_clustered_text, + dmc.Space(h=10), + dmc.CardSection(dmc.Divider(variant="dashed"), withBorder=False), + dmc.Space(h=10), + dmc.Group(chip), + ], + withBorder=False, + shadow="sm", + radius="md", + styles=dict(color="lime"), + ) diff --git a/automappa/pages/home/components/sample_cards.py b/automappa/pages/home/components/sample_cards.py new file mode 100644 index 00000000..2f60f740 --- /dev/null +++ b/automappa/pages/home/components/sample_cards.py @@ -0,0 +1,239 @@ +from typing import Dict, List, Literal, Optional, Protocol, Tuple, Union +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import ( + DashProxy, + html, + Input, + Output, + State, + dcc, + ctx, + MATCH, + ALL, +) +from dash_iconify import DashIconify +import dash_mantine_components as dmc + +from celery.result import GroupResult, AsyncResult + +from automappa.components import ids +from automappa.pages.home.components import sample_card + + +class SampleCardsDataSource(Protocol): + def preprocess_metagenome( + self, + name: str, + metagenome_fpath: str, + binning_fpath: str, + markers_fpath: str, + connections_fpath: Optional[str] = None, + ) -> GroupResult: + """Create Metagenome object in db + + Returns + ------- + AsyncResult + Celery AsyncResult from task canvas preprocess metagenome pipeline + """ + ... + + def get_preprocess_metagenome_tasks( + self, task_ids: List[str] + ) -> List[Tuple[str, AsyncResult]]: + ... + + def remove_metagenome(self, metagenome_id: int) -> None: + ... + + def get_metagenome_ids(self) -> List[int]: + ... + + +def render(app: DashProxy, source: SampleCardsDataSource) -> html.Div: + @app.callback( + Output( + {ids.SAMPLE_CARD_INDEX: MATCH, "type": ids.SAMPLE_CARD_TYPE}, + "withBorder", + ), + Input( + {ids.SAMPLE_CARD_INDEX: MATCH, "type": ids.SAMPLE_CARD_CHIP_TYPE}, "checked" + ), + prevent_initial_call=True, + ) + def sample_card_selected(sample_chip_checked: bool) -> bool: + return sample_chip_checked + + @app.callback( + Output(ids.TASK_ID_STORE, "data", allow_duplicate=True), + Input(ids.METAGENOME_UPLOAD_STORE, "data"), + Input(ids.BINNING_MAIN_UPLOAD_STORE, "data"), + Input(ids.MARKERS_UPLOAD_STORE, "data"), + Input(ids.CYTOSCAPE_STORE, "data"), + Input(ids.UPLOAD_STEPPER_SUBMIT_BUTTON, "n_clicks"), + State(ids.SAMPLE_NAME_TEXT_INPUT, "value"), + prevent_initial_call="initial_duplicate", + ) + def submit_sample_ingestion_task( + metagenome_fpath: str, + binning_fpath: str, + marker_fpath: str, + connection_fpath: Union[str, None], + submit_btn: int, + metagenome_name: str, + ) -> List[str]: + if not ctx.triggered_id == ids.UPLOAD_STEPPER_SUBMIT_BUTTON: + raise PreventUpdate + group_result = source.preprocess_metagenome( + metagenome_name, + metagenome_fpath, + binning_fpath, + marker_fpath, + connection_fpath, + ) + task_ids = [group_result.parent.id] + [task.id for task in group_result.results] + return task_ids + + @app.callback( + Output(ids.BACKGROUND_TASK_DIV, "children"), + Output(ids.TASK_ID_STORE, "data", allow_duplicate=True), + Input(ids.BACKGROUND_TASK_INTERVAL, "n_intervals"), + State(ids.TASK_ID_STORE, "data"), + prevent_initial_call=True, + ) + def notify_task_progress( + n_intervals: int, task_ids: List[str] + ) -> Tuple[List[dmc.Notification], List[str]]: + notifications = [] + if not task_ids: + raise PreventUpdate + tasks = source.get_preprocess_metagenome_tasks(task_ids) + n_tasks = len(tasks) + tasks_completed = 0 + for task_name, task in tasks: + if task.status in { + "PENDING", + "RETRY", + }: + loading = True + color = "orange" + autoclose = False + action = "show" + icon = DashIconify(icon="la:running") + elif task.status == "RECEIVED": + loading = True + color = "blue" + autoclose = False + action = "update" + icon = DashIconify(icon="la:running") + elif task.status == "STARTED": + loading = True + color = "green" + autoclose = False + action = "update" + icon = DashIconify(icon="ooui:error", color="red") + elif task.status == "FAILURE" or task.status == "REVOKED": + loading = False + color = "red" + autoclose = 15000 + action = "update" + icon = DashIconify(icon="ooui:error", color="red") + else: + # task.status == "SUCCESS" + loading = False + color = "green" + autoclose = 15000 + action = "update" + icon = DashIconify(icon="akar-icons:circle-check") + # Forget task upon success... + # otherwise keep in tasks list + tasks_completed += 1 + + notification = dmc.Notification( + id={ids.NOTIFICATION_TASK_ID: task.id}, + title=f"Task: {task_name}", + message=f"pre-processing status: {task.status}", + loading=loading, + color=color, + action=action, + autoClose=autoclose, + disallowClose=False, + icon=icon, + ) + notifications.append(notification) + + if tasks_completed == n_tasks: + for name, task in tasks: + task.forget() + task_ids.pop(task_ids.index(task.id)) + for notification in notifications: + notification.action = "update" + return notifications, task_ids + + @app.callback( + Output(ids.SAMPLE_CARDS_CONTAINER, "children", allow_duplicate=True), + Input(ids.TASK_ID_STORE, "data"), + prevent_initial_call=True, + ) + def get_sample_cards(task_ids: List[str]) -> List[dmc.Card]: + if task_ids: + raise PreventUpdate + return [ + sample_card.render(source, metagenome_id=mg_id) + for mg_id in source.get_metagenome_ids() + ] + + @app.callback( + Output(ids.SAMPLE_CARDS_CONTAINER, "children", allow_duplicate=True), + Input( + {ids.SAMPLE_CARD_INDEX: ALL, "type": ids.SAMPLE_CARD_REMOVE_BTN}, + "n_clicks", + ), + State({ids.SAMPLE_CARD_INDEX: ALL, "type": ids.SAMPLE_CARD_REMOVE_BTN}, "id"), + prevent_initial_call=True, + ) + def remove_button_clicked( + remove_btns_clicks: List[int], remove_btn_ids: Dict[str, str] + ) -> List[dmc.Card]: + if not any(remove_btns_clicks): + raise PreventUpdate + sample_card_index = [ + i for i, n_clicks in enumerate(remove_btns_clicks) if n_clicks > 0 + ][0] + metagenome_id = remove_btn_ids[sample_card_index].get(ids.SAMPLE_CARD_INDEX) + source.remove_metagenome(metagenome_id) + return [ + sample_card.render(source, metagenome_id=mg_id) + for mg_id in source.get_metagenome_ids() + ] + + @app.callback( + Output(ids.SAMPLE_CARDS_CONTAINER, "children", allow_duplicate=True), + Input(ids.UPLOAD_STEPPER_SUBMIT_BUTTON, "n_clicks"), + prevent_initial_call="initial_duplicate", + ) + def get_sample_cards(submit_btn: int) -> List[dmc.Card]: + return [ + sample_card.render(source, metagenome_id=mg_id) + for mg_id in source.get_metagenome_ids() + ] + + # TODO Callback to delete sample card + return html.Div( + [ + dcc.Interval(id=ids.BACKGROUND_TASK_INTERVAL, interval=3000), + html.Div(id=ids.BACKGROUND_TASK_DIV), + dmc.SimpleGrid( + id=ids.SAMPLE_CARDS_CONTAINER, + spacing="xs", + cols=6, + breakpoints=[ + dict(maxWidth=1500, cols=5), + dict(maxWidth=1200, cols=4), + dict(maxWidth=980, cols=3), + dict(maxWidth=755, cols=2), + dict(maxWidth=600, cols=1), + ], + ), + ] + ) diff --git a/automappa/pages/home/components/sample_name_text_input.py b/automappa/pages/home/components/sample_name_text_input.py new file mode 100644 index 00000000..e679d406 --- /dev/null +++ b/automappa/pages/home/components/sample_name_text_input.py @@ -0,0 +1,61 @@ +import dash_mantine_components as dmc +from dash.exceptions import PreventUpdate +import string +from typing import Protocol +from dash_extensions.enrich import html, DashProxy, Output, Input + +from automappa.components import ids + +MAX_CHARS = 24 + + +class SampleNameTextInputDataSource(Protocol): + def name_is_unique(self, name: str) -> bool: + ... + + +def has_symbols_or_whitespace(text: str) -> bool: + # Define the set of allowed characters (letters, digits, and underscores) + allowed_chars = string.ascii_letters + string.digits + "_" + # Check if any character in the string is not in the set of allowed characters + return any(char not in allowed_chars for char in text) + + +def exceeds_max_char_length(text: str) -> bool: + return len(text) >= MAX_CHARS + + +def render(app: DashProxy, source: SampleNameTextInputDataSource) -> html.Div: + @app.callback( + Output(ids.SAMPLE_NAME_TEXT_INPUT, "error"), + Input(ids.SAMPLE_NAME_TEXT_INPUT, "value"), + prevent_initial_call=True, + ) + def update_is_valid_sample_name(input_text: str) -> str: + if input_text is None: + raise PreventUpdate + if not source.name_is_unique(input_text): + return "Sample name must be unique!" + if has_symbols_or_whitespace(input_text): + return "Sample may not contain any symbols or whitespace!" + if exceeds_max_char_length(input_text): + return f"Name must be less than {MAX_CHARS}" + return "" + + @app.callback( + Output(ids.SAMPLE_NAME_TEXT_INPUT, "value"), + Input(ids.UPLOAD_STEPPER_SUBMIT_BUTTON, "n_clicks"), + prevent_initial_call=True, + ) + def reset_text_input_value_on_submit(submit_btn: int) -> str: + return "" + + return html.Div( + dmc.TextInput( + id=ids.SAMPLE_NAME_TEXT_INPUT, + label="Sample Name", + placeholder="i.e. forcepia_sponge", + description="Provide a name to identify this sample", + required=True, + ), + ) diff --git a/automappa/pages/home/components/samples_datatable.py b/automappa/pages/home/components/samples_datatable.py new file mode 100644 index 00000000..87de9e74 --- /dev/null +++ b/automappa/pages/home/components/samples_datatable.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +import logging +from dash.dash_table import DataTable +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import DashProxy, Input, Output, State, html, dcc +import pandas as pd + +from automappa.components import ids + + +logger = logging.getLogger(__name__) + + +def render(app: DashProxy) -> html.Div: + @app.callback( + Output(ids.SAMPLES_DATATABLE, "children"), + [Input(ids.SAMPLES_STORE, "data")], + State(ids.SAMPLES_STORE, "data"), + ) + def on_samples_store_data(samples_df: pd.DataFrame, new_samples_df: pd.DataFrame): + if samples_df is None or samples_df.empty: + raise PreventUpdate + if new_samples_df is not None: + samples_df = pd.concat([samples_df, new_samples_df]).drop_duplicates( + subset=["table_id"] + ) + + logger.debug(f"retrieved {samples_df.shape[0]:,} samples from samples store") + + if samples_df.empty: + raise PreventUpdate + + return DataTable( + data=samples_df.to_dict("records"), + columns=[ + {"id": col, "name": col, "editable": False} + for col in samples_df.columns + ], + persistence=True, + persistence_type="session", + ) + + return html.Div( + children=[ + dcc.Loading( + id=ids.LOADING_SAMPLES_DATATABLE, + children=[ + html.Label("Uploaded Datasets"), + html.Div(id=ids.SAMPLES_DATATABLE), + ], + type="dot", + color="#646569", + ), + ] + ) diff --git a/automappa/pages/home/components/selected_tables_datatable.py b/automappa/pages/home/components/selected_tables_datatable.py new file mode 100644 index 00000000..6c489acb --- /dev/null +++ b/automappa/pages/home/components/selected_tables_datatable.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +from dash.dash_table import DataTable +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import DashProxy, Input, Output, State, html, dcc +from automappa.data.source import SampleTables + +from automappa.components import ids + + +def render(app: DashProxy) -> html.Div: + @app.callback( + Output(ids.SELECTED_TABLES_DATATABLE, "children"), + [ + Input(ids.SELECTED_TABLES_STORE, "data"), + ], + State(ids.SELECTED_TABLES_STORE, "data"), + ) + def selected_tables_datatable_children( + samples: SampleTables, + new_tables: SampleTables, + ): + if samples is None: + raise PreventUpdate + if new_tables is not None: + if new_tables != samples: + tables_dict = samples.dict() + tables_dict.update(new_tables.dict()) + samples = SampleTables.parse_obj(tables_dict) + + has_table = False + for __, table_id in samples: + if table_id: + has_table = True + break + + if not has_table: + raise PreventUpdate + + return DataTable( + data=[ + {"filetype": sample, "table_id": table.id} + for sample, table in samples + if sample not in {"kmers"} + ], + columns=[ + {"id": "filetype", "name": "filetype", "editable": False}, + {"id": "table_id", "name": "table_id", "editable": False}, + ], + persistence=True, + persistence_type="session", + ) + + return html.Div( + children=[ + dcc.Loading( + id=ids.LOADING_SELECTED_TABLES_DATATABLE, + children=[ + html.Label("Selected Datasets for Refinement & Summary:"), + html.Div(id=ids.SELECTED_TABLES_DATATABLE), + ], + type="dot", + color="#646569", + ), + ] + ) diff --git a/automappa/pages/home/components/task_status_badge.py b/automappa/pages/home/components/task_status_badge.py new file mode 100644 index 00000000..c77e419a --- /dev/null +++ b/automappa/pages/home/components/task_status_badge.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python + +import random +from dash.exceptions import PreventUpdate +from dash import Patch +from dash_extensions.enrich import DashProxy, Output, Input, State, html, dcc, ctx +import dash_mantine_components as dmc +from typing import List + +from automappa.components import ids +from automappa.pages.home.tasks.task_status_badge import set_badge_color + + +PENDING = "PENDING" +STARTED = "STARTED" +RETRY = "RETRY" +FAILURE = "FAILURE" +SUCCESS = "SUCCESS" + + +def render(app: DashProxy) -> html.Div: + @app.callback( + Output(ids.BADGE_TASK_STORE, "data", allow_duplicate=True), + Input(ids.BACKGROUND_TASK_BUTTON, "n_clicks"), + prevent_initial_call="initial_duplicate", + ) + def create_task(btn: int): + if not ctx.triggered_id == ids.BACKGROUND_TASK_BUTTON: + raise PreventUpdate + task_ids = Patch() + color = random.choice(["green", "lime"]) + task = set_badge_color.delay((color,)) + task_ids.append(task.id) + return task_ids + + @app.callback( + Output(ids.BACKGROUND_TASK_BADGE, "color"), + Output(ids.BADGE_TASK_STORE, "data", allow_duplicate=True), + Input(ids.BADGE_STATUS_INTERVAL, "n_intervals"), + State(ids.BADGE_TASK_STORE, "data"), + prevent_initial_call=True, + ) + def read_tasks(n_intervals: int, task_ids: List[str]) -> str: + if not task_ids: + raise PreventUpdate + for task_id in task_ids: + task = set_badge_color.AsyncResult(task_id) + if task.status == PENDING: + color = "orange" + elif task.status == STARTED: + color = "blue" + elif task.status == RETRY: + color = "yellow" + elif task.status == FAILURE: + color = "red" + else: + # i.e. task.status == SUCCESS + color = task.get() + task_ids.pop(task_ids.index(task_id)) + return color, task_ids + + @app.callback( + Output(ids.BACKGROUND_TASK_BADGE, "children"), + Input(ids.BADGE_STATUS_INTERVAL, "n_intervals"), + Input(ids.BADGE_TASK_STORE, "data"), + ) + def read_tasks_count(n_intervals: int, task_ids: List[str]) -> str: + if not task_ids: + text = f"0 tasks!" + else: + n_tasks = len(task_ids) + text = f"{n_tasks} task" if n_tasks == 1 else f"{n_tasks} tasks" + return text + + # NOTE: You can uncomment this callback + # for disabling submit button when task is submitted + @app.callback( + Output(ids.BACKGROUND_TASK_BUTTON, "disabled"), + Input(ids.BADGE_TASK_STORE, "data"), + ) + def disable_task_button(task_ids: List[str]) -> bool: + return True if task_ids else False + + return html.Div( + [ + dcc.Store(ids.BADGE_TASK_STORE, data=[]), + dcc.Interval(ids.BADGE_STATUS_INTERVAL, interval=500), + dmc.Button( + children=[dmc.Text("task button")], id=ids.BACKGROUND_TASK_BUTTON + ), + dmc.Badge(id=ids.BACKGROUND_TASK_BADGE), + ] + ) diff --git a/automappa/pages/home/components/tasks_table.py b/automappa/pages/home/components/tasks_table.py new file mode 100644 index 00000000..69b4d733 --- /dev/null +++ b/automappa/pages/home/components/tasks_table.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- + +from dash_extensions.enrich import DashProxy, Input, Output, html +import dash_bootstrap_components as dbc + +from automappa.components import ids + + +def render(app: DashProxy) -> html.Div: + return html.Div(id=ids.EMBEDDING_TASKS) diff --git a/automappa/pages/home/components/upload_modal.py b/automappa/pages/home/components/upload_modal.py new file mode 100644 index 00000000..f2a44349 --- /dev/null +++ b/automappa/pages/home/components/upload_modal.py @@ -0,0 +1,66 @@ +# -*- coding: utf-8 -*- + +from typing import Protocol +from dash_extensions.enrich import DashProxy, Input, Output, State, html + +import dash_bootstrap_components as dbc +import dash_mantine_components as dmc +from dash_iconify import DashIconify + +from automappa.components import ids + +from automappa.pages.home.components import upload_stepper + + +class UploadModalDataSource(Protocol): + def name_is_unique(self, name: str) -> bool: + ... + + +def render(app: DashProxy, source: UploadModalDataSource) -> html.Div: + @app.callback( + Output(ids.UPLOAD_MODAL, "is_open"), + [ + Input(ids.OPEN_MODAL_BUTTON, "n_clicks"), + Input(ids.CLOSE_MODAL_BUTTON, "n_clicks"), + Input(ids.UPLOAD_STEPPER_SUBMIT_BUTTON, "n_clicks"), + ], + [State(ids.UPLOAD_MODAL, "is_open")], + ) + def toggle_modal( + open_btn: int, + close_btn: int, + submit_btn: int, + is_open: bool, + ) -> bool: + if open_btn or close_btn or submit_btn: + return not is_open + return is_open + + # TODO Add text to modal with max_file_size info... + return html.Div( + dbc.Modal( + [ + dbc.ModalHeader( + dbc.ModalTitle("Upload sample annotations"), + close_button=False, + ), + dbc.ModalBody(upload_stepper.render(app, source)), + dbc.ModalFooter( + dmc.Button( + "Close", + id=ids.CLOSE_MODAL_BUTTON, + leftIcon=[DashIconify(icon="line-md:close-small")], + style={"textAlign": "center"}, + color="dark", + ) + ), + ], + id=ids.UPLOAD_MODAL, + keyboard=False, + backdrop="static", + size="lg", + fullscreen=False, + centered=True, + ), + ) diff --git a/automappa/pages/home/components/upload_modal_button.py b/automappa/pages/home/components/upload_modal_button.py new file mode 100644 index 00000000..92c83b63 --- /dev/null +++ b/automappa/pages/home/components/upload_modal_button.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +from typing import List, Protocol +from dash_iconify import DashIconify +from dash_extensions.enrich import DashProxy, html, Output, Input + +import dash_mantine_components as dmc + +from automappa.components import ids +from automappa.pages.home.components import upload_modal + + +class UploadModalButtonDataSource(Protocol): + def name_is_unique(self, name: str) -> bool: + ... + + +def render(app: DashProxy, source: UploadModalButtonDataSource) -> html.Div: + @app.callback( + Output(ids.OPEN_MODAL_BUTTON, "disabled"), + Input(ids.TASK_ID_STORE, "data"), + ) + def disable_task_button(task_ids: List[str]) -> bool: + return True if task_ids and task_ids is not None else False + + return html.Div( + [ + dmc.Button( + "New Sample", + id=ids.OPEN_MODAL_BUTTON, + leftIcon=[DashIconify(icon="line-md:upload-outline", width=25)], + variant="gradient", + gradient={"from": "#CA2270", "to": "#F36E2D"}, + fullWidth=False, + ), + upload_modal.render(app, source), + ] + ) diff --git a/automappa/pages/home/components/upload_stepper.py b/automappa/pages/home/components/upload_stepper.py new file mode 100644 index 00000000..049cb365 --- /dev/null +++ b/automappa/pages/home/components/upload_stepper.py @@ -0,0 +1,383 @@ +from typing import Protocol +from dash_extensions.enrich import ( + DashProxy, + Input, + Output, + html, + State, + ctx, +) +from dash.exceptions import PreventUpdate +import dash_mantine_components as dmc +from dash_iconify import DashIconify + +from automappa.components import ids +from automappa.pages.home.components import ( + binning_upload, + cytoscape_connections_upload, + markers_upload, + metagenome_upload, + sample_name_text_input, +) + +# LOGIC: +# Steps: +# 1. Upload metagenome +# 2. Upload binning (will eventually link to metagenome) +# 3. Upload markers (will eventually link to binning contigs) +# 4. Upload cytoscape connections (optional will eventually link to metagenome) +# 5. Finish upload (unique sample name required as text input to act as link b/w uploaded data) +# On completion (adds metagenome card to home page) + + +class UploadStepperDataSource(Protocol): + def name_is_unique(self, name: str) -> bool: + ... + + +def render(app: DashProxy, source: UploadStepperDataSource) -> html.Div: + @app.callback( + Output(ids.UPLOAD_STEPPER, "active", allow_duplicate=True), + Input(ids.UPLOAD_STEPPER_BACK_BUTTON, "n_clicks"), + Input(ids.UPLOAD_STEPPER_NEXT_BUTTON, "n_clicks"), + State(ids.UPLOAD_STEPPER, "active"), + prevent_initial_call=True, + ) + def update_active_step(back_btn: int, next_btn: int, current_step: int) -> int: + button_id = ctx.triggered_id + step = current_step if current_step is not None else ACTIVE + if button_id == ids.UPLOAD_STEPPER_BACK_BUTTON: + step = step - 1 if step > MIN_STEP else step + else: + step = step + 1 if step < MAX_STEP else step + return step + + @app.callback( + Output(ids.UPLOAD_STEPPER_BACK_BUTTON, "disabled"), + Input(ids.UPLOAD_STEPPER, "active"), + prevent_initial_call=True, + ) + def disable_back_button_on_upload_metagenome_step( + current_step: int, + ) -> bool: + is_disabled = True + if current_step == MIN_STEP: + return is_disabled + return not is_disabled + + @app.callback( + Output(ids.UPLOAD_STEPPER_NEXT_BUTTON, "disabled", allow_duplicate=True), + Input(ids.METAGENOME_UPLOAD, "isCompleted"), + Input(ids.UPLOAD_STEPPER, "active"), + prevent_initial_call=True, + ) + def toggle_next_button_on_metagenome_upload( + is_completed: bool, + current_step: int, + ) -> bool: + if current_step != METAGENOME_UPLOAD_STEP: + raise PreventUpdate + return not is_completed + + @app.callback( + Output(ids.UPLOAD_STEPPER_NEXT_BUTTON, "disabled", allow_duplicate=True), + Input(ids.BINNING_UPLOAD, "isCompleted"), + Input(ids.UPLOAD_STEPPER, "active"), + prevent_initial_call=True, + ) + def toggle_next_button_on_binning_upload( + is_completed: bool, current_step: int + ) -> bool: + if current_step != BINNING_UPLOAD_STEP: + raise PreventUpdate + return not is_completed + + @app.callback( + Output(ids.UPLOAD_STEPPER_NEXT_BUTTON, "disabled", allow_duplicate=True), + Input(ids.MARKERS_UPLOAD, "isCompleted"), + Input(ids.UPLOAD_STEPPER, "active"), + prevent_initial_call=True, + ) + def toggle_next_button_on_marker_upload( + is_completed: bool, current_step: int + ) -> bool: + if current_step != MARKERS_UPLOAD_STEP: + raise PreventUpdate + return not is_completed + + @app.callback( + Output(ids.UPLOAD_STEPPER_NEXT_BUTTON, "disabled", allow_duplicate=True), + Input(ids.SAMPLE_NAME_TEXT_INPUT, "error"), + Input(ids.SAMPLE_NAME_TEXT_INPUT, "value"), + Input(ids.UPLOAD_STEPPER, "active"), + prevent_initial_call=True, + ) + def toggle_next_button_on_sample_name_input( + text_input_error: str, text_input_value: str, current_step: int + ) -> bool: + if current_step != SAMPLE_NAME_STEP: + raise PreventUpdate + return not text_input_value or text_input_error != "" + + @app.callback( + Output(ids.UPLOAD_STEPPER_NEXT_BUTTON, "disabled", allow_duplicate=True), + Input(ids.UPLOAD_STEPPER, "active"), + prevent_initial_call=True, + ) + def disable_next_button_on_completed_step(current_step: int) -> bool: + if current_step != MAX_STEP: + raise PreventUpdate + return True + + @app.callback( + Output(ids.UPLOAD_STEPPER_SUBMIT_BUTTON, "disabled"), + Input(ids.SAMPLE_NAME_TEXT_INPUT, "value"), + ) + def toggle_submit_button(text_input: str) -> bool: + return not text_input + + @app.callback( + Output(ids.UPLOAD_STEPPER, "active", allow_duplicate=True), + Input(ids.UPLOAD_STEPPER_SUBMIT_BUTTON, "n_clicks"), + prevent_initial_call=True, + ) + def on_submit_button(submit_btn: int) -> int: + return ACTIVE + + @app.callback( + Output(ids.UPLOAD_STEPPER, "active", allow_duplicate=True), + Input(ids.CLOSE_MODAL_BUTTON, "n_clicks"), + prevent_initial_call=True, + ) + def on_close_modal_button(close_modal_btn: int) -> int: + return ACTIVE + + def get_icon(icon: str, height: int = 20) -> DashIconify: + return DashIconify(icon=icon, height=height) + + MIN_STEP = 0 + METAGENOME_UPLOAD_STEP = 0 + BINNING_UPLOAD_STEP = 1 + MARKERS_UPLOAD_STEP = 2 + CONNECTIONS_UPLOAD_STEP = 3 + SAMPLE_NAME_STEP = 4 + MAX_STEP = 5 + ACTIVE = 0 + + upload_metagenome_step = dmc.StepperStep( + label="First step", + description="Upload metagenome", + icon=get_icon("tabler:dna-2-off"), + iconSize=30, + progressIcon=get_icon("tabler:dna-2-off"), + completedIcon=get_icon("tabler:dna-2"), + children=[ + dmc.Text( + "Upload your sample's metagenome assembly fasta file", + align="center", + ), + metagenome_upload.render(app, source), + ], + ) + upload_binning_step = dmc.StepperStep( + label="Second step", + description="Upload binning", + icon=get_icon("ph:chart-scatter"), + iconSize=30, + progressIcon=get_icon("ph:chart-scatter"), + completedIcon=get_icon("ph:chart-scatter-bold"), + children=[ + dmc.Group( + children=[ + dmc.Text( + "Upload the corresponding main binning results", + align="center", + ), + dmc.Anchor( + dmc.Tooltip( + get_icon( + "material-symbols:info-outline", + height=15, + ), + label=dmc.Code( + "autometa-binning --output-main " + ), + color="gray", + withArrow=True, + position="right", + radius="md", + ), + href="https://autometa.readthedocs.io/en/latest/step-by-step-tutorial.html#binning", + target="_blank", + underline=False, + color="dark", + ), + ], + spacing="xs", + ), + binning_upload.render(app, source), + ], + ) + + upload_markers_step = dmc.StepperStep( + label="Third step", + description="Upload markers", + icon=get_icon("fluent:document-dismiss-24-regular"), + iconSize=30, + progressIcon=get_icon("fluent:document-dismiss-24-regular"), + completedIcon=get_icon("fluent:document-checkmark-24-regular"), + children=[ + dmc.Group( + children=[ + dmc.Text( + "Upload the corresponding marker annotation results", + align="center", + ), + dmc.Anchor( + dmc.Tooltip( + get_icon( + "material-symbols:info-outline", + height=15, + ), + label=dmc.Code( + "autometa-markers --out " + ), + color="gray", + withArrow=True, + position="right", + radius="md", + ), + href="https://autometa.readthedocs.io/en/latest/step-by-step-tutorial.html#single-copy-markers", + target="_blank", + color="dark", + underline=False, + ), + ], + ), + markers_upload.render(app, source), + ], + ) + + connections_upload_step = dmc.StepperStep( + label="Fourth step", + description="Upload connections", + icon=get_icon("bx:network-chart"), + iconSize=30, + progressIcon=get_icon("bx:network-chart"), + completedIcon=get_icon("bx:network-chart"), + children=[ + dmc.Group( + children=[ + dmc.Text("Upload corresponding cytoscape connections"), + dmc.Text("(optional)", weight=500, color="orange"), + dmc.Anchor( + dmc.Tooltip( + get_icon( + "material-symbols:info-outline", + height=15, + ), + label="Mads Albertsen tutorial on generating paired-end connections", + color="gray", + withArrow=True, + position="right", + radius="md", + ), + href="https://madsalbertsen.github.io/multi-metagenome/docs/step10.html", + target="_blank", + underline=False, + color="dark", + ), + dmc.Anchor( + dmc.Tooltip( + get_icon("openmoji:github", height=15), + label="https://github.com/MadsAlbertsen/multi-metagenome", + color="gray", + withArrow=True, + position="right", + radius="md", + ), + href="https://github.com/MadsAlbertsen/multi-metagenome", + target="_blank", + underline=False, + color="dark", + ), + ], + spacing="xs", + ), + cytoscape_connections_upload.render(app, source), + ], + ) + + name_sample_step = dmc.StepperStep( + label="Fifth step", + description="Name Sample", + icon=get_icon("mdi:rename-outline"), + iconSize=30, + progressIcon=get_icon("mdi:rename-outline"), + completedIcon=get_icon("mdi:rename"), + children=[ + dmc.Text( + "Supply a unique sample name to group your metagenome annotations" + ), + sample_name_text_input.render(app, source), + ], + ) + + completed_step = dmc.StepperCompleted( + children=[ + dmc.Text( + "That's it! Click the back button to go to a previous step or submit to save this dataset", + align="center", + ), + dmc.Center( + dmc.Button( + "Submit", + id=ids.UPLOAD_STEPPER_SUBMIT_BUTTON, + leftIcon=[get_icon("line-md:upload-outline")], + variant="gradient", + gradient={"from": "#CA2270", "to": "#F36E2D"}, + ) + ), + ] + ) + + return html.Div( + dmc.Container( + [ + dmc.Stepper( + id=ids.UPLOAD_STEPPER, + active=ACTIVE, + breakpoint="md", + color="dark", + children=[ + upload_metagenome_step, + upload_binning_step, + upload_markers_step, + connections_upload_step, + name_sample_step, + completed_step, + ], + ), + dmc.Group( + position="center", + mt="xl", + children=[ + dmc.Button( + "Back", + id=ids.UPLOAD_STEPPER_BACK_BUTTON, + variant="outline", + color="dark", + disabled=True, + ), + dmc.Button( + "Next step", + id=ids.UPLOAD_STEPPER_NEXT_BUTTON, + disabled=True, + variant="outline", + color="dark", + ), + ], + ), + ] + ) + ) diff --git a/automappa/pages/home/layout.py b/automappa/pages/home/layout.py new file mode 100644 index 00000000..701d525d --- /dev/null +++ b/automappa/pages/home/layout.py @@ -0,0 +1,35 @@ +from dash_extensions.enrich import DashBlueprint, LogTransform +import dash_mantine_components as dmc +from automappa.components import ids +from automappa.pages.home.components import ( + sample_cards, + upload_modal_button, +) +from automappa.pages.home.source import HomeDataSource + + +HEIGHT_MARGIN = 10 +WIDTH_MARGIN = 10 + + +def render(source: HomeDataSource) -> DashBlueprint: + app = DashBlueprint(transforms=[LogTransform()]) + app.name = ids.HOME_TAB_ID + app.icon = "line-md:home" + app.description = "Automappa home page to upload genome binning results." + app.title = "Automappa home" + app.layout = dmc.NotificationsProvider( + dmc.Container( + [ + dmc.Space(h=HEIGHT_MARGIN, w=WIDTH_MARGIN), + sample_cards.render(app, source), + dmc.Space(h=HEIGHT_MARGIN, w=WIDTH_MARGIN), + dmc.Affix( + upload_modal_button.render(app, source), + position={"bottom": HEIGHT_MARGIN, "left": WIDTH_MARGIN}, + ), + ], + fluid=True, + ) + ) + return app diff --git a/automappa/pages/home/source.py b/automappa/pages/home/source.py new file mode 100644 index 00000000..349a6a00 --- /dev/null +++ b/automappa/pages/home/source.py @@ -0,0 +1,312 @@ +#!/usr/bin/env python + +from functools import partial +import uuid +import logging +from pydantic import BaseModel +from typing import List, Optional, Tuple, Union + +from sqlmodel import Session, and_, select, func + +from sqlalchemy.exc import NoResultFound, MultipleResultsFound + +from celery import group +from celery.result import GroupResult, AsyncResult +from automappa.data import loader + + +from automappa.data.database import engine +from automappa.data.models import ( + Metagenome, + Contig, + Marker, + CytoscapeConnection, + Refinement, +) +from automappa.pages.home.tasks.sample_cards import ( + assign_contigs_marker_size, + assign_contigs_marker_symbol, + create_metagenome_model, + initialize_refinement, +) + +logger = logging.getLogger(__name__) + +MARKER_SET_SIZE = 139 +HIGH_QUALITY_COMPLETENESS = 90 # gt +HIGH_QUALITY_PURITY = 95 # gt +MEDIUM_QUALITY_COMPLETENESS = 50 # gte +MEDIUM_QUALITY_PURITY = 90 # gt +# lt +LOW_QUALITY_COMPLETENESS = 50 +LOW_QUALITY_PURITY = 90 + + +class HomeDataSource(BaseModel): + def name_is_unique(self, name: str) -> bool: + """Determine whether metagenome name is unique in the database + + Parameters + ---------- + name : str + Name of the metagenome + + Returns + ------- + bool + Whether Metagenome.name occurs exactly once in the database + """ + with Session(engine) as session: + try: + session.exec(select(Metagenome).where(Metagenome.name == name)).one() + is_unique = True + except NoResultFound: + is_unique = True + except MultipleResultsFound: + is_unique = False + return is_unique + + def get_metagenome_name(self, metagenome_id: int) -> str: + with Session(engine) as session: + name = session.exec( + select(Metagenome.name).where(Metagenome.id == metagenome_id) + ).one() + return name + + def get_metagenome_ids(self) -> List[int]: + """Get all unique Metagenome names in database + + Returns + ------- + List[int] + Metagenome id available in database + """ + with Session(engine) as session: + metagenome_ids = session.exec(select(Metagenome.id)).all() + return metagenome_ids + + def validate_uploader_path( + self, + is_completed: bool, + filenames: List[str], + upload_id: uuid.UUID, + ) -> str: + """Given uploader inputs return filepaths in order: + + Tuple[metagenome_fpath, binning_fpath, markers_fpath, connections_fpath] + """ + fpath = loader.validate_uploader(is_completed, filenames, upload_id) + if fpath: + fpath = str(fpath) + return fpath + + def preprocess_metagenome( + self, + name: str, + metagenome_fpath: str, + binning_fpath: str, + markers_fpath: str, + connections_fpath: Union[str, None] = None, + ) -> GroupResult: + task_chain = create_metagenome_model.s() | group( + [ + assign_contigs_marker_size.s(), + assign_contigs_marker_symbol.s(), + initialize_refinement.s(), + ] + ) + # NOTE: task ids may be retrieved using .results method + # (will correspond to order in group) + # group_result.results: List[AsyncResult] + result: GroupResult = task_chain.set(countdown=2).delay( + name=name, + metagenome_fpath=metagenome_fpath, + binning_fpath=binning_fpath, + markers_fpath=markers_fpath, + connections_fpath=connections_fpath, + ) + return result + + def get_preprocess_metagenome_tasks( + self, task_ids: Tuple[str, str, str, str] + ) -> List[Tuple[str, AsyncResult]]: + ( + mg_model_task_id, + marker_size_task_id, + marker_symbol_task_id, + refinement_task_id, + ) = task_ids + mg_model_task = create_metagenome_model.AsyncResult(mg_model_task_id) + marker_size_task = assign_contigs_marker_size.AsyncResult(marker_size_task_id) + marker_symbol_task = assign_contigs_marker_symbol.AsyncResult( + marker_symbol_task_id + ) + refinement_task = initialize_refinement.AsyncResult(refinement_task_id) + return ( + ("ingesting metagenome data", mg_model_task), + ("pre-computing marker sizes", marker_size_task), + ("pre-computing marker symbols", marker_symbol_task), + ("initializing user refinements", refinement_task), + ) + + def remove_metagenome(self, metagenome_id: int) -> None: + with Session(engine) as session: + metagenome = session.exec( + select(Metagenome).where(Metagenome.id == metagenome_id) + ).one() + session.delete(metagenome) + session.commit() + + def marker_count(self, metagenome_id: int) -> int: + with Session(engine) as session: + statement = ( + select([func.count(Marker.id)]) + .join(Contig) + .join(Metagenome) + .where(Metagenome.id == metagenome_id) + ) + marker_count = session.exec(statement).one() + return marker_count + + def get_approximate_marker_sets(self, metagenome_id: int) -> int: + marker_count_stmt = ( + select(func.count(Marker.id)) + .join(Contig) + .where(Contig.metagenome_id == metagenome_id) + ) + with Session(engine) as session: + total_markers = session.exec(marker_count_stmt).first() + + return total_markers // MARKER_SET_SIZE + + def contig_count(self, metagenome_id: int) -> int: + with Session(engine) as session: + statement = ( + select([func.count(Metagenome.contigs)]) + .join(Contig) + .where(Metagenome.id == metagenome_id) + ) + contig_count = session.exec(statement).one() + return contig_count + + def connections_count(self, metagenome_id: int) -> int: + with Session(engine) as session: + statement = ( + select([func.count(Metagenome.connections)]) + .join(CytoscapeConnection) + .where(Metagenome.id == metagenome_id) + ) + connection_count = session.exec(statement).first() + return connection_count + + def get_refined_contig_count(self, metagenome_id: int) -> int: + stmt = ( + select(func.count(Contig.id)) + .where(Contig.metagenome_id == metagenome_id) + .where(Contig.refinements.any(Refinement.outdated == False)) + ) + with Session(engine) as session: + count = session.exec(stmt).first() or 0 + return count + + def get_refinements_count( + self, + metagenome_id: int, + initial: Optional[bool] = None, + outdated: Optional[bool] = None, + ) -> int: + """Get Refinement count where Refinement.metagenome_id == metagenome_id + + Providing `initial` will add where(Refinement.initial_refinement == True) + otherwise will omit this filter and retrieve all. + """ + stmt = select(func.count(Refinement.id)).where( + Refinement.metagenome_id == metagenome_id + ) + if isinstance(initial, bool): + stmt = stmt.where(Refinement.initial_refinement == initial) + if isinstance(outdated, bool): + stmt = stmt.where(Refinement.outdated == outdated) + with Session(engine) as session: + count = session.exec(stmt).first() or 0 + return count + + def compute_completeness_purity_metrics( + self, metagenome_id: int, refinement_id: int + ) -> Tuple[float, float]: + marker_count_stmt = ( + select(func.count(Marker.id)) + .join(Contig) + .where( + Contig.metagenome_id == metagenome_id, + Contig.refinements.any( + and_( + Refinement.outdated == False, + Refinement.id == refinement_id, + ) + ), + ) + ) + unique_marker_stmt = ( + select(Marker.sacc) + .join(Contig) + .distinct() + .where( + Contig.metagenome_id == metagenome_id, + Contig.refinements.any( + and_( + Refinement.outdated == False, + Refinement.id == refinement_id, + ) + ), + ) + ) + with Session(engine) as session: + markers_count = session.exec(marker_count_stmt).first() or 0 + unique_marker_count = session.exec( + select(func.count()).select_from(unique_marker_stmt) + ).first() + + completeness = round(unique_marker_count / MARKER_SET_SIZE * 100, 2) + purity = ( + round(unique_marker_count / markers_count * 100, 2) if markers_count else 0 + ) + return completeness, purity + + def get_mimag_counts(self, metagenome_id: int) -> Tuple[int, int, int]: + """Retrieve counts of clusters following MIMAG standards. + + standards: + + - High-quality >90% complete > 95% pure + - Medium-quality >=50% complete > 90% pure + - Low-quality <50% complete < 90% pure + + """ + stmt = select(Refinement.id).where( + Refinement.metagenome_id == metagenome_id, + Refinement.outdated == False, + ) + high_quality_count = 0 + medium_quality_count = 0 + low_quality_count = 0 + with Session(engine) as session: + refinement_ids = session.exec(stmt).all() + for refinement_id in refinement_ids: + completeness, purity = self.compute_completeness_purity_metrics( + metagenome_id, refinement_id + ) + if ( + completeness > HIGH_QUALITY_COMPLETENESS + and purity > HIGH_QUALITY_PURITY + ): + high_quality_count += 1 + elif ( + completeness >= MEDIUM_QUALITY_COMPLETENESS + and purity > MEDIUM_QUALITY_PURITY + ): + medium_quality_count += 1 + else: + # completeness < LOW_QUALITY_COMPLETENESS and purity < LOW_QUALITY_PURITY: + low_quality_count += 1 + return high_quality_count, medium_quality_count, low_quality_count diff --git a/automappa/pages/home/tasks/README.md b/automappa/pages/home/tasks/README.md new file mode 100644 index 00000000..b1a6f813 --- /dev/null +++ b/automappa/pages/home/tasks/README.md @@ -0,0 +1,100 @@ +# Tasks + +## Adding a new task module discovery path + +To have the Celery task-queue register a page's tasks, they must be +discovered based on the relative task module's path. Here's a simple example +(It may be easier to show here than explain...) + +We have our page's tasks module: + +```console +automappa/pages/home/tasks +├── README.md +├── __init__.py +└── task_status_badge.py +``` + +We configure celery for this module in the `celeryconfig.py` file. + +```python +# contents of celeryconfig.py +imports = ("automappa.pages.home.tasks", "automappa.tasks") +``` + +This ***almost*** takes care of celery checking for tasks in the module + +Unfortunately, this is not all, we also need to update the +`automappa/pages/home/tasks/__init__.py` with all of our implemented tasks in the +module for celery to recognize and register the tasks under the `automappa/pages/home/tasks` module. + +For example we have a task `set_badge_color` defined in `task_status_badge.py`. + +We would need to explicitly add this task to `__init__.py` like so: + +```python +# contents of `automappa/pages/home/tasks/__init__.py` +from .task_status_badge import set_badge_color + +__all__ = ["set_badge_color"] +``` + +Voilá, now celery will recognize the task on startup. It should look like this: + +
+ +Celery Startup with `set_badge_color` registered + +```console + + -------------- celery@4bbb963e90ec v5.3.1 (emerald-rush) +--- ***** ----- +-- ******* ---- Linux-5.15.49-linuxkit-pr-x86_64-with-glibc2.31 2023-07-13 18:24:15 +- *** --- * --- +- ** ---------- [config] +- ** ---------- .> app: automappa.tasks:0x7f23e6d778e0 +- ** ---------- .> transport: amqp://user:**@rabbitmq:5672// +- ** ---------- .> results: redis://redis:6379/0 +- *** --- * --- .> concurrency: 2 (prefork) +-- ******* ---- .> task events: ON +--- ***** ----- + -------------- [queues] + .> celery exchange=celery(direct) key=celery + +[tasks] + . automappa.pages.home.tasks.task_status_badge.set_badge_color + . automappa.tasks.aggregate_embeddings + . automappa.tasks.count_kmer + . automappa.tasks.embed_kmer + . automappa.tasks.get_embedding_traces_df + . automappa.tasks.normalize_kmer + . automappa.tasks.preprocess_clusters_geom_medians + . automappa.tasks.preprocess_marker_symbols +[2023-07-13 18:24:16,413: WARNING/MainProcess] /opt/conda/lib/python3.9/site-packages/celery/worker/consumer/consumer.py:498: CPendingDeprecationWarning: The broker_connection_retry configuration setting will no longer determine +whether broker connection retries are made during startup in Celery 6.0 and above. +If you wish to retain the existing behavior for retrying connections on startup, +you should set broker_connection_retry_on_startup to True. + warnings.warn( +[2023-07-13 18:24:16,437: INFO/MainProcess] Connected to amqp://user:**@rabbitmq:5672// +[2023-07-13 18:24:16,439: WARNING/MainProcess] /opt/conda/lib/python3.9/site-packages/celery/worker/consumer/consumer.py:498: CPendingDeprecationWarning: The broker_connection_retry configuration setting will no longer determine +whether broker connection retries are made during startup in Celery 6.0 and above. +If you wish to retain the existing behavior for retrying connections on startup, +you should set broker_connection_retry_on_startup to True. + warnings.warn( +[2023-07-13 18:24:16,455: INFO/MainProcess] mingle: searching for neighbors +[2023-07-13 18:24:17,569: INFO/MainProcess] mingle: all alone +[2023-07-13 18:24:17,627: INFO/MainProcess] celery@4bbb963e90ec ready. +``` + +
+ +## Adding a new task to an existing page + +Celery has trouble discovering newly implemented tasks + +Unfortunately I have not found a convenient workaround for this +but each page has its own `tasks` module with an `__init__.py` +where any tasks implemented under this module must be imported into +`__init__.py` and specified in the `__all__` dunder variable. + +You can avoid alot of headache by recalling this... \ No newline at end of file diff --git a/automappa/pages/home/tasks/__init__.py b/automappa/pages/home/tasks/__init__.py new file mode 100644 index 00000000..0aefe56d --- /dev/null +++ b/automappa/pages/home/tasks/__init__.py @@ -0,0 +1,17 @@ +from .task_status_badge import set_badge_color +from .sample_cards import ( + create_metagenome_model, + initialize_refinement, + assign_contigs_marker_size, + assign_contigs_marker_symbol, + create_metagenome, +) + +__all__ = [ + "set_badge_color", + "create_metagenome", + "create_metagenome_model", + "initialize_refinement", + "assign_contigs_marker_symbol", + "assign_contigs_marker_size", +] diff --git a/automappa/pages/home/tasks/sample_cards.py b/automappa/pages/home/tasks/sample_cards.py new file mode 100644 index 00000000..53521dd6 --- /dev/null +++ b/automappa/pages/home/tasks/sample_cards.py @@ -0,0 +1,129 @@ +#!/usr/bin/env python + +from typing import List, Optional, Tuple, Union +from sqlmodel import Session, case, func, select +from automappa.data import loader +from automappa.data.database import engine +from automappa.data.models import Contig, Marker +from automappa.tasks import queue + + +@queue.task(bind=True) +def create_metagenome( + self, + name: str, + metagenome_fpath: str, + binning_fpath: str, + markers_fpath: str, + connections_fpath: Union[List[str], None] = None, +) -> Tuple[str, int]: + # TODO Create sample should be async so sample_card with loader + # is displayed and user can continue with navigation + # TODO Disable "new sample" button while create sample is in progress + metagenome = loader.create_sample_metagenome( + name, metagenome_fpath, binning_fpath, markers_fpath, connections_fpath + ) + loader.create_initial_refinements(metagenome.id) + return metagenome.name, metagenome.id + + +@queue.task(bind=True) +def create_metagenome_model( + self, + name: str, + metagenome_fpath: str, + binning_fpath: str, + markers_fpath: str, + connections_fpath: Optional[str] = None, +) -> int: + metagenome = loader.create_sample_metagenome( + name, metagenome_fpath, binning_fpath, markers_fpath, connections_fpath + ) + return metagenome.id + + +@queue.task(bind=True) +def initialize_refinement(self, metagenome_id: int) -> None: + loader.create_initial_refinements(metagenome_id) + + +@queue.task(bind=True) +def assign_contigs_marker_symbol(self, metagenome_id: int) -> None: + subquery = ( + select( + [ + Contig.id, + case( + [ + (func.count(Marker.id) == 0, "circle"), + (func.count(Marker.id) == 1, "square"), + (func.count(Marker.id) == 2, "diamond"), + (func.count(Marker.id) == 3, "triangle-up"), + (func.count(Marker.id) == 4, "x"), + (func.count(Marker.id) == 5, "pentagon"), + (func.count(Marker.id) == 6, "hexagon2"), + (func.count(Marker.id) >= 7, "hexagram"), + ], + else_="circle", + ).label("symbol"), + ] + ) + .select_from(Contig) + .join(Marker, isouter=True) + .group_by(Contig.id) + .subquery() + ) + stmt = ( + select(Contig, subquery.c.symbol) + .select_from(Contig) + .join(subquery, subquery.c.id == Contig.id) + ) + stmt = stmt.where(Contig.metagenome_id == metagenome_id) + with Session(engine) as session: + results = session.exec(stmt).all() + for contig, symbol in results: + contig.marker_symbol = symbol + session.add(contig) + + session.commit() + + +@queue.task(bind=True) +def assign_contigs_marker_size(self, metagenome_id: int) -> None: + subquery = ( + select( + [ + Contig.id, + case( + [ + (func.count(Marker.id) == 0, 7), + (func.count(Marker.id) == 1, 8), + (func.count(Marker.id) == 2, 9), + (func.count(Marker.id) == 3, 10), + (func.count(Marker.id) == 4, 11), + (func.count(Marker.id) == 5, 12), + (func.count(Marker.id) == 6, 13), + (func.count(Marker.id) >= 7, 14), + ], + else_=7, + ).label("size"), + ] + ) + .select_from(Contig) + .join(Marker, isouter=True) + .group_by(Contig.id) + .subquery() + ) + stmt = ( + select(Contig, subquery.c.size) + .select_from(Contig) + .join(subquery, subquery.c.id == Contig.id) + ) + stmt = stmt.where(Contig.metagenome_id == metagenome_id) + with Session(engine) as session: + results = session.exec(stmt).all() + for contig, size in results: + contig.marker_size = size + session.add(contig) + + session.commit() diff --git a/automappa/pages/home/tasks/task_status_badge.py b/automappa/pages/home/tasks/task_status_badge.py new file mode 100644 index 00000000..37d2d915 --- /dev/null +++ b/automappa/pages/home/tasks/task_status_badge.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +import time + +from automappa.tasks import queue + + +@queue.task(bind=True) +def set_badge_color(self, color: str) -> str: + time.sleep(15) + return color + + +if __name__ == "__main__": + pass diff --git a/automappa/pages/mag_refinement/__init__.py b/automappa/pages/mag_refinement/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/automappa/pages/mag_refinement/components/__init__.py b/automappa/pages/mag_refinement/components/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/automappa/pages/mag_refinement/components/binning_refinements_clear_button.py b/automappa/pages/mag_refinement/components/binning_refinements_clear_button.py new file mode 100644 index 00000000..39cb5c4d --- /dev/null +++ b/automappa/pages/mag_refinement/components/binning_refinements_clear_button.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +from typing import List, Protocol +from dash_extensions.enrich import DashProxy, Output, Input, html, ctx +import dash_mantine_components as dmc +from dash_iconify import DashIconify + +from automappa.components import ids + + +class RefinementsClearButtonDataSource(Protocol): + def clear_refinements(self, metagenome_id: int) -> int: + ... + + def has_user_refinements(self, metagenome_id: int) -> bool: + ... + + +def render(app: DashProxy, source: RefinementsClearButtonDataSource) -> html.Div: + @app.callback( + Output(ids.REFINEMENTS_CLEARED_NOTIFICATION, "children"), + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.REFINEMENTS_CLEAR_BUTTON, "n_clicks"), + prevent_initial_call=True, + ) + def clear_refinements_callback(metagenome_id: int, btn: int) -> dmc.Notification: + deleted_refinements_count = source.clear_refinements(metagenome_id) + message = f"Successfully cleared {deleted_refinements_count:,}" + title = ( + "Refinement cleared!" + if deleted_refinements_count == 1 + else "Refinements cleared!" + ) + message += " refinement" if deleted_refinements_count == 1 else " refinements" + return dmc.Notification( + id=ids.REFINEMENTS_NOTIFICATION, + action="show", + message=message, + title=title, + icon=DashIconify(icon="icomoon-free:fire", color="#f78f1f"), + color="dark", + autoClose=60000, + ) + + @app.callback( + Output(ids.REFINEMENTS_CLEAR_BUTTON, "disabled"), + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.REFINEMENTS_CLEARED_NOTIFICATION, "children"), + Input(ids.MAG_REFINEMENTS_SAVE_BUTTON, "n_clicks"), + ) + def disable_clear_button( + metagenome_id: int, cleared_notification: List[dmc.Notification], save_btn: int + ) -> bool: + return not source.has_user_refinements(metagenome_id) + + return html.Div( + [ + dmc.Button( + children=[dmc.Text("Clear Refinements")], + id=ids.REFINEMENTS_CLEAR_BUTTON, + variant="filled", + color="red", + leftIcon=DashIconify(icon="icomoon-free:fire", color="white"), + ), + html.Div(id=ids.REFINEMENTS_CLEARED_NOTIFICATION), + ] + ) diff --git a/automappa/pages/mag_refinement/components/binning_refinements_download_button.py b/automappa/pages/mag_refinement/components/binning_refinements_download_button.py new file mode 100644 index 00000000..b5cf4c34 --- /dev/null +++ b/automappa/pages/mag_refinement/components/binning_refinements_download_button.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from typing import Dict, Protocol + +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html + +import dash_mantine_components as dmc +from dash_iconify import DashIconify +import pandas as pd + +from automappa.components import ids + + +class RefinementsDownloadButtonDataSource(Protocol): + def get_refinements_dataframe(self, metagenome_id: int) -> pd.DataFrame: + ... + + def has_user_refinements(self, metagenome_id: int) -> bool: + ... + + +def render(app: DashProxy, source: RefinementsDownloadButtonDataSource) -> html.Div: + @app.callback( + Output(ids.REFINEMENTS_DOWNLOAD, "data"), + [ + Input(ids.REFINEMENTS_DOWNLOAD_BUTTON, "n_clicks"), + Input(ids.METAGENOME_ID_STORE, "data"), + ], + ) + def download_refinements( + n_clicks: int, + metagenome_id: int, + ) -> Dict[str, "str | bool"]: + if not n_clicks: + raise PreventUpdate + df = source.get_refinements_dataframe(metagenome_id) + return dcc.send_data_frame(df.to_csv, "refinements.csv", index=False) + + @app.callback( + Output(ids.REFINEMENTS_DOWNLOAD_BUTTON, "disabled"), + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.MAG_REFINEMENTS_SAVE_BUTTON, "n_clicks"), + Input(ids.REFINEMENTS_CLEARED_NOTIFICATION, "children"), + ) + def disable_download_button( + metagenome_id: int, save_btn: int, clear_btn_notification + ) -> bool: + return not source.has_user_refinements(metagenome_id) + + # Download Refinements Button + return html.Div( + [ + dmc.Button( + "Download Refinements", + id=ids.REFINEMENTS_DOWNLOAD_BUTTON, + leftIcon=[DashIconify(icon="line-md:download-loop", height=25)], + n_clicks=0, + color="dark", + variant="outline", + fullWidth=False, + ), + dcc.Download(id=ids.REFINEMENTS_DOWNLOAD), + ] + ) diff --git a/automappa/pages/mag_refinement/components/binning_refinements_summary_button.py b/automappa/pages/mag_refinement/components/binning_refinements_summary_button.py new file mode 100644 index 00000000..0d65edc5 --- /dev/null +++ b/automappa/pages/mag_refinement/components/binning_refinements_summary_button.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from dash_extensions.enrich import html +import dash_mantine_components as dmc +from automappa.components import ids + + +# Summarize Refinements Button +def render() -> html.Div: + return html.Div( + dmc.Button( + "Summarize Refinements", + id=ids.REFINEMENTS_SUMMARY_BUTTON, + n_clicks=0, + color="dark", + fullWidth=True, + ), + # TODO: Create background task to compute binning summary metrics + # TODO: Create downloader task to download file of computed summary metrics + ) diff --git a/automappa/pages/mag_refinement/components/color_by_col_dropdown.py b/automappa/pages/mag_refinement/components/color_by_col_dropdown.py new file mode 100644 index 00000000..fd88fadf --- /dev/null +++ b/automappa/pages/mag_refinement/components/color_by_col_dropdown.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- + +from typing import Dict, List, Literal, Protocol +from dash_extensions.enrich import DashProxy, html +import dash_mantine_components as dmc + +from automappa.components import ids + + +class ColorByColDropdownDataSource(Protocol): + def get_color_by_column_options(self) -> List[Dict[Literal["label", "value"], str]]: + ... + + +def render(app: DashProxy, source: ColorByColDropdownDataSource) -> html.Div: + options = source.get_color_by_column_options() + radios = [ + dmc.Radio(option["label"], value=option["value"], color="orange") + for option in options + ] + return html.Div( + [ + html.Label("Color contigs by:"), + dmc.RadioGroup( + radios, + id=ids.COLOR_BY_COLUMN_DROPDOWN, + value=ids.COLOR_BY_COLUMN_DROPDOWN_VALUE_DEFAULT, + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/contig_cytoscape.py b/automappa/pages/mag_refinement/components/contig_cytoscape.py new file mode 100644 index 00000000..d2c4b9c6 --- /dev/null +++ b/automappa/pages/mag_refinement/components/contig_cytoscape.py @@ -0,0 +1,116 @@ +from typing import Dict, List, Literal, Optional, Protocol, Union +import dash_cytoscape as cyto +from dash_extensions.enrich import DashProxy, html, Output, Input, dcc + +from automappa.components import ids + + +class ContigCytoscapeDataSource(Protocol): + def get_cytoscape_elements( + self, metagenome_id: int, headers: Optional[List[str]] + ) -> List[ + Dict[ + Literal["data"], + Dict[ + Literal["id", "label", "source", "target", "connections"], + Union[str, int], + ], + ] + ]: + ... + + def get_cytoscape_stylesheet( + self, metagenome_id: int, headers: Optional[List[str]] + ) -> List[ + Dict[ + Literal["selector", "style"], + Union[Literal["node", "edge"], Dict[str, Union[str, int, float]]], + ] + ]: + ... + + +def render(app: DashProxy, source: ContigCytoscapeDataSource) -> html.Div: + @app.callback( + Output(ids.CONTIG_CYTOSCAPE, "stylesheet"), + [ + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.SCATTERPLOT_2D_FIGURE, "selectedData"), + ], + prevent_initial_call=True, + ) + def highlight_selected_contigs( + metagenome_id: int, + selected_contigs: Dict[str, List[Dict[str, str]]], + ) -> List[ + Dict[ + Literal["selector", "style"], + Union[Literal["node", "edge"], Dict[str, Union[str, int, float]]], + ] + ]: + headers = {point["text"] for point in selected_contigs["points"]} + stylesheet = source.get_cytoscape_stylesheet(metagenome_id, headers) + + SELECTED_COLOR = "#B10DC9" + stylesheet += [ + { + "selector": "node", + "style": {"label": "data(label)", "opacity": 0.7}, + }, + { + "selector": "edge", + "style": { + "opacity": 0.4, + "curve-style": "bezier", + "label": "data(connections)", + }, + }, + ] + # TODO + # 1. Style connections using mappingtype + # (i.e. differentiate between start and end connections) + # - https://dash.plotly.com/cytoscape/styling#edge-arrows + # TODO + # 2. Add selector based on number of contig connections + # - https://dash.plotly.com/cytoscape/styling#comparing-data-items-using-selectors + # It looks like this could be done using the 'weight' key for the edge + # and then selecting using stylesheet = [{'selector': '[weight > 3]'}] + # where the '3' could be dynamically updated by a slider component (or other component) + return stylesheet + + @app.callback( + Output(ids.CONTIG_CYTOSCAPE, "elements"), + [ + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.SCATTERPLOT_2D_FIGURE, "selectedData"), + ], + prevent_initial_call=True, + ) + def update_cytoscape_elements( + metagenome_id: int, + selected_contigs: Dict[str, List[Dict[str, str]]], + ) -> List[ + Dict[ + Literal["data"], + Dict[ + Literal["id", "label", "source", "target", "connections"], + Union[str, int], + ], + ] + ]: + headers = {point["text"] for point in selected_contigs["points"]} + records = source.get_cytoscape_elements(metagenome_id, headers) + return records + + return html.Div( + dcc.Loading( + cyto.Cytoscape( + id=ids.CONTIG_CYTOSCAPE, + layout=dict(name="cose"), + style=dict(width="100%", height="600px"), + responsive=True, + ), + id=ids.LOADING_CONTIG_CYTOSCAPE, + type="graph", + ), + ) diff --git a/automappa/pages/mag_refinement/components/coverage_range_slider.py b/automappa/pages/mag_refinement/components/coverage_range_slider.py new file mode 100644 index 00000000..7f662479 --- /dev/null +++ b/automappa/pages/mag_refinement/components/coverage_range_slider.py @@ -0,0 +1,53 @@ +from typing import Protocol, Tuple +from dash_extensions.enrich import DashProxy, Output, Input, html +import dash_mantine_components as dmc +from dash_iconify import DashIconify + +from automappa.components import ids + + +class CoverageRangeSliderDataSource(Protocol): + def get_coverage_min_max_values(self, metagenome_id: int) -> Tuple[float, float]: + ... + + +def render(app: DashProxy, source: CoverageRangeSliderDataSource) -> html.Div: + @app.callback( + Output(ids.COVERAGE_RANGE_SLIDER, "max"), + Output(ids.COVERAGE_RANGE_SLIDER, "value"), + Input(ids.METAGENOME_ID_STORE, "data"), + ) + def update_slider_range( + metagenome_id: int, + ) -> Tuple[float, float, Tuple[float, float]]: + min_cov, max_cov = source.get_coverage_min_max_values(metagenome_id) + min_cov = round(min_cov, 2) + max_cov = round(max_cov, 2) + return max_cov, (min_cov, max_cov) + + return html.Div( + [ + dmc.Text("Coverage range slider"), + dmc.Space(h=30), + dmc.LoadingOverlay( + dmc.RangeSlider( + min=0, + showLabelOnHover=True, + labelTransition="fade", + labelTransitionDuration=1000, # in ms + color="gray", + size="lg", + thumbFromLabel="cov", + thumbSize=35, + thumbChildren=DashIconify(icon="iconamoon:sign-x-light", width=25), + id=ids.COVERAGE_RANGE_SLIDER, + ), + loaderProps=dict( + variant="oval", + color="dark", + size="sm", + ), + ), + dmc.Space(h=30), + ] + ) diff --git a/automappa/pages/mag_refinement/components/hide_selections_switch.py b/automappa/pages/mag_refinement/components/hide_selections_switch.py new file mode 100644 index 00000000..4dcad0e9 --- /dev/null +++ b/automappa/pages/mag_refinement/components/hide_selections_switch.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from typing import List, Protocol +from dash_extensions.enrich import html, Input, Output, DashProxy +import dash_mantine_components as dmc + +from automappa.components import ids + + +class HideRefinementsSwitchDataSource(Protocol): + def has_user_refinements(self, metagenome_id: int) -> bool: + ... + + +def render(app: DashProxy, source: HideRefinementsSwitchDataSource) -> html.Div: + @app.callback( + Output(ids.HIDE_SELECTIONS_TOGGLE, "disabled"), + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.MAG_REFINEMENTS_SAVE_BUTTON, "n_clicks"), + Input(ids.REFINEMENTS_CLEARED_NOTIFICATION, "children"), + ) + def disable_switch( + metagenome_id: int, save_btn: int, cleared_notification: List[dmc.Notification] + ) -> bool: + return not source.has_user_refinements(metagenome_id) + + return html.Div( + dmc.Tooltip( + dmc.Switch( + id=ids.HIDE_SELECTIONS_TOGGLE, + checked=ids.HIDE_SELECTIONS_TOGGLE_VALUE_DEFAULT, + size="lg", + radius="md", + color="indigo", + label="Hide MAG Refinements", + offLabel="Off", + onLabel="On", + ), + label='Toggling this to "On" will hide your manually-curated MAG refinement groups', + position="bottom-start", + openDelay=1000, # milliseconds + transition="pop-bottom-left", + transitionDuration=500, + multiline=True, + width=300, + withArrow=True, + ) + ) diff --git a/automappa/pages/mag_refinement/components/kmer_size_dropdown.py b/automappa/pages/mag_refinement/components/kmer_size_dropdown.py new file mode 100644 index 00000000..bacf0b34 --- /dev/null +++ b/automappa/pages/mag_refinement/components/kmer_size_dropdown.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from dash_extensions.enrich import dcc, html +from automappa.components import ids + + +def render() -> html.Div: + return html.Div( + [ + html.Label("K-mer size:"), + dcc.Dropdown( + id=ids.KMER_SIZE_DROPDOWN, + options=[3, 4, 5], + value=ids.KMER_SIZE_DROPDOWN_VALUE_DEFAULT, + clearable=False, + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/mag_metrics_table.py b/automappa/pages/mag_refinement/components/mag_metrics_table.py new file mode 100644 index 00000000..ffea7436 --- /dev/null +++ b/automappa/pages/mag_refinement/components/mag_metrics_table.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from typing import Dict, List, Literal, Optional, Protocol, Union +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html +import dash_ag_grid as dag + +from automappa.components import ids + + +class MagMetricsTableDataSource(Protocol): + def get_marker_overview( + self, metagenome_id: int + ) -> List[Dict[Literal["metric", "metric_value"], Union[str, int, float]]]: + ... + + def get_mag_metrics_row_data( + self, metagenome_id: int, headers: Optional[List[str]] + ) -> List[Dict[Literal["metric", "metric_value"], Union[str, int, float]]]: + ... + + +def render(app: DashProxy, source: MagMetricsTableDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_METRICS_DATATABLE, "rowData", allow_duplicate=True), + [ + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.SCATTERPLOT_2D_FIGURE, "selectedData"), + ], + prevent_initial_call=True, + ) + def compute_mag_metrics( + metagenome_id: int, + selected_contigs: Optional[Dict[str, List[Dict[str, str]]]], + ) -> List[Dict[Literal["metric", "metric_value"], Union[str, int, float]]]: + headers = ( + {point["text"] for point in selected_contigs["points"]} + if selected_contigs + else None + ) + row_data = source.get_mag_metrics_row_data( + metagenome_id=metagenome_id, headers=headers + ) + return row_data + + @app.callback( + Output(ids.MAG_METRICS_DATATABLE, "rowData", allow_duplicate=True), + Input(ids.METAGENOME_ID_STORE, "data"), + prevent_initial_call="initial_duplicate", + ) + def compute_markers_overview( + metagenome_id: int, + ) -> List[Dict[Literal["metric", "metric_value"], Union[str, int, float]]]: + row_data = source.get_marker_overview(metagenome_id) + return row_data + + GREEN = "#2FCC90" + YELLOW = "#f2e530" + ORANGE = "#f57600" + MIMAG_STYLE_CONDITIONS = { + "styleConditions": [ + # High-quality >90% complete > 95% pure + { + "condition": "params.data.metric == 'Completeness (%)' && params.value > 90", + "style": {"backgroundColor": GREEN}, + }, + { + "condition": "params.data.metric == 'Purity (%)' && params.value > 95", + "style": {"backgroundColor": GREEN}, + }, + # Medium-quality >=50% complete > 90% pure + { + "condition": "params.data.metric == 'Completeness (%)' && params.value >= 50", + "style": {"backgroundColor": YELLOW}, + }, + { + "condition": "params.data.metric == 'Purity (%)' && params.value > 90", + "style": {"backgroundColor": YELLOW}, + }, + # Low-quality <50% complete < 90% pure + { + "condition": "params.data.metric == 'Completeness (%)' && params.value < 50", + "style": {"backgroundColor": ORANGE, "color": "white"}, + }, + { + "condition": "params.data.metric == 'Purity (%)' && params.value < 90", + "style": {"backgroundColor": ORANGE, "color": "white"}, + }, + ] + } + + column_defs = [ + {"field": "metric", "headerName": "MAG Metric", "resizable": True}, + { + "field": "metric_value", + "headerName": "Value", + "cellStyle": MIMAG_STYLE_CONDITIONS, + }, + ] + return html.Div( + [ + html.Label("Table 1. MAG Marker Metrics"), + dcc.Loading( + dag.AgGrid( + id=ids.MAG_METRICS_DATATABLE, + className="ag-theme-material", + columnSize="responsiveSizeToFit", + style={"height": 600, "width": "100%"}, + columnDefs=column_defs, + ), + id=ids.LOADING_MAG_METRICS_DATATABLE, + type="dot", + color="#646569", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/mag_refinement_coverage_boxplot.py b/automappa/pages/mag_refinement/components/mag_refinement_coverage_boxplot.py new file mode 100644 index 00000000..4676241b --- /dev/null +++ b/automappa/pages/mag_refinement/components/mag_refinement_coverage_boxplot.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from typing import Dict, List, Optional, Protocol, Tuple +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html +from plotly import graph_objects as go + +from automappa.utils.figures import metric_boxplot + +from automappa.components import ids + + +class RefinementCoverageBoxplotDataSource(Protocol): + def get_coverage_boxplot_records( + self, metagenome_id: int, headers: Optional[List[str]] + ) -> List[Tuple[str, List[float]]]: + ... + + +def render(app: DashProxy, source: RefinementCoverageBoxplotDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_REFINEMENT_COVERAGE_BOXPLOT, "figure"), + [ + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.SCATTERPLOT_2D_FIGURE, "selectedData"), + ], + ) + def subset_coverage_boxplot_by_scatterplot_selection( + metagenome_id: int, + selected_data: Dict[str, List[Dict[str, str]]], + ) -> go.Figure: + headers = ( + {point["text"] for point in selected_data["points"]} + if selected_data + else None + ) + data = source.get_coverage_boxplot_records(metagenome_id, headers=headers) + fig = metric_boxplot(data, boxmean="sd") + return fig + + return html.Div( + [ + html.Label("Figure 4: MAG Refinement Coverage Boxplot"), + dcc.Loading( + id=ids.LOADING_MAG_REFINEMENT_COVERAGE_BOXPLOT, + children=[ + dcc.Graph( + id=ids.MAG_REFINEMENT_COVERAGE_BOXPLOT, + config={"displayModeBar": False, "displaylogo": False}, + ) + ], + type="dot", + color="#646569", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/mag_refinement_gc_content_boxplot.py b/automappa/pages/mag_refinement/components/mag_refinement_gc_content_boxplot.py new file mode 100644 index 00000000..ca573586 --- /dev/null +++ b/automappa/pages/mag_refinement/components/mag_refinement_gc_content_boxplot.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from typing import Dict, List, Optional, Protocol, Tuple +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html +from plotly import graph_objects as go + +from automappa.utils.figures import metric_boxplot + +from automappa.components import ids + + +class RefinementGcContentBoxplotDataSource(Protocol): + def get_gc_content_boxplot_records( + self, metagenome_id: int, headers: Optional[List[str]] + ) -> List[Tuple[str, List[float]]]: + ... + + +def render(app: DashProxy, source: RefinementGcContentBoxplotDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_REFINEMENT_GC_CONTENT_BOXPLOT, "figure"), + [ + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.SCATTERPLOT_2D_FIGURE, "selectedData"), + ], + ) + def subset_gc_content_boxplot_by_scatterplot_selection( + metagenome_id: int, + selected_data: Dict[str, List[Dict[str, str]]], + ) -> go.Figure: + headers = ( + {point["text"] for point in selected_data["points"]} + if selected_data + else None + ) + data = source.get_gc_content_boxplot_records( + metagenome_id=metagenome_id, headers=headers + ) + fig = metric_boxplot(data, boxmean="sd") + return fig + + return html.Div( + [ + html.Label("Figure 5: MAG Refinement GC Content Boxplot"), + dcc.Loading( + id=ids.LOADING_MAG_REFINEMENT_GC_CONTENT_BOXPLOT, + children=[ + dcc.Graph( + id=ids.MAG_REFINEMENT_GC_CONTENT_BOXPLOT, + config={"displayModeBar": False, "displaylogo": False}, + ) + ], + type="dot", + color="#0479a8", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/mag_refinement_length_boxplot.py b/automappa/pages/mag_refinement/components/mag_refinement_length_boxplot.py new file mode 100644 index 00000000..12a5cec0 --- /dev/null +++ b/automappa/pages/mag_refinement/components/mag_refinement_length_boxplot.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from typing import Dict, List, Optional, Protocol, Tuple +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html +from plotly import graph_objects as go + +from automappa.utils.figures import ( + metric_boxplot, +) + +from automappa.components import ids + + +class RefinementLengthBoxplotDataSource(Protocol): + def get_length_boxplot_records( + self, metagenome_id: int, headers: Optional[List[str]] + ) -> List[Tuple[str, List[int]]]: + ... + + +def render(app: DashProxy, source: RefinementLengthBoxplotDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_REFINEMENT_LENGTH_BOXPLOT, "figure"), + [ + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.SCATTERPLOT_2D_FIGURE, "selectedData"), + ], + ) + def subset_length_boxplot_by_scatterplot_selection( + metagenome_id: int, + selected_data: Dict[str, List[Dict[str, str]]], + ) -> go.Figure: + headers = ( + {point["text"] for point in selected_data["points"]} + if selected_data + else None + ) + data = source.get_length_boxplot_records( + metagenome_id=metagenome_id, headers=headers + ) + fig = metric_boxplot(data=data) + return fig + + return html.Div( + [ + html.Label("Figure 6: MAG Refinement Length Boxplot"), + dcc.Loading( + id=ids.LOADING_MAG_REFINEMENT_LENGTH_BOXPLOT, + children=[ + dcc.Graph( + id=ids.MAG_REFINEMENT_LENGTH_BOXPLOT, + config={"displayModeBar": False, "displaylogo": False}, + ) + ], + type="dot", + color="#0479a8", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/marker_symbols_legend.py b/automappa/pages/mag_refinement/components/marker_symbols_legend.py new file mode 100644 index 00000000..af60606d --- /dev/null +++ b/automappa/pages/mag_refinement/components/marker_symbols_legend.py @@ -0,0 +1,109 @@ +from dash_extensions.enrich import html +import dash_bootstrap_components as dbc +import dash_mantine_components as dmc +from dash_iconify import DashIconify + + +# TODO: Refactor to update scatterplot legend with update marker symbol traces... +def render() -> html.Div: + return html.Div( + [ + dbc.Row( + dbc.Col(dmc.Title("Marker Symbol Count Legend", order=6)), + justify="start", + ), + dbc.Row( + [ + dbc.Col( + dmc.Badge( + ": 0", + leftSection=[DashIconify(icon="ph:circle-bold")], + size="lg", + radius="xl", + color="dark", + variant="outline", + fullWidth=True, + ) + ), + dbc.Col( + dmc.Badge( + ": 1", + leftSection=[DashIconify(icon="uil:square")], + size="lg", + radius="xl", + color="dark", + variant="outline", + fullWidth=True, + ) + ), + dbc.Col( + dmc.Badge( + ": 2", + leftSection=[DashIconify(icon="ph:diamond-bold")], + size="lg", + radius="xl", + color="dark", + variant="outline", + fullWidth=True, + ) + ), + dbc.Col( + dmc.Badge( + ": 3", + leftSection=[DashIconify(icon="tabler:triangle")], + size="lg", + radius="xl", + color="dark", + variant="outline", + fullWidth=True, + ) + ), + dbc.Col( + dmc.Badge( + ": 4", + leftSection=[DashIconify(icon="tabler:x")], + size="lg", + radius="xl", + color="dark", + variant="outline", + fullWidth=True, + ) + ), + dbc.Col( + dmc.Badge( + ": 5", + leftSection=[DashIconify(icon="tabler:pentagon")], + size="lg", + radius="xl", + color="dark", + variant="outline", + fullWidth=True, + ) + ), + dbc.Col( + dmc.Badge( + ": 6", + leftSection=[DashIconify(icon="tabler:hexagon")], + size="lg", + radius="xl", + color="dark", + variant="outline", + fullWidth=True, + ) + ), + dbc.Col( + dmc.Badge( + ": 7+", + leftSection=[DashIconify(icon="mdi:hexagram-outline")], + size="lg", + radius="xl", + color="dark", + variant="outline", + fullWidth=True, + ) + ), + ], + justify="evenly", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/norm_method_dropdown.py b/automappa/pages/mag_refinement/components/norm_method_dropdown.py new file mode 100644 index 00000000..f3ed1b4c --- /dev/null +++ b/automappa/pages/mag_refinement/components/norm_method_dropdown.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from dash_extensions.enrich import dcc, html +from automappa.components import ids + + +def render() -> html.Div: + return html.Div( + [ + html.Label("norm. method:"), + dcc.Dropdown( + id=ids.NORM_METHOD_DROPDOWN, + options=[ + dict(label="CLR", value="am_clr"), + dict(label="ILR", value="ilr"), + ], + value=ids.NORM_METHOD_DROPDOWN_VALUE_DEFAULT, + clearable=False, + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/refinements_table.py b/automappa/pages/mag_refinement/components/refinements_table.py new file mode 100644 index 00000000..06d1abc5 --- /dev/null +++ b/automappa/pages/mag_refinement/components/refinements_table.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + + +import dash_ag_grid as dag +from typing import Dict, List, Literal, Protocol, Union +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html +from datetime import datetime + +from automappa.components import ids + + +class RefinementsTableDataSource(Protocol): + def get_refinements_row_data( + self, metagenome_id: int + ) -> List[ + Dict[ + Literal["refinement_id", "timestamp", "contigs"], + Union[str, int, datetime], + ] + ]: + ... + + +def render(app: DashProxy, source: RefinementsTableDataSource) -> html.Div: + @app.callback( + Output(ids.REFINEMENTS_TABLE, "rowData"), + [ + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.MAG_REFINEMENTS_SAVE_BUTTON, "n_clicks"), + Input(ids.REFINEMENTS_CLEARED_NOTIFICATION, "children"), + ], + ) + def refinements_table_callback( + metagenome_id: int, save_btn: int, notification + ) -> List[ + Dict[ + Literal["refinement_id", "timestamp", "contigs"], + Union[str, int, datetime], + ] + ]: + row_data = source.get_refinements_row_data(metagenome_id) + return row_data + + column_defs = [ + {"field": "refinement_id", "headerName": "ID", "resizable": False}, + {"field": "timestamp", "headerName": "Timestamp"}, + {"field": "contigs", "headerName": "Contigs"}, + ] + + return html.Div( + [ + html.Label("Table 2. MAG Refinements"), + dcc.Loading( + dag.AgGrid( + id=ids.REFINEMENTS_TABLE, + className="ag-theme-material", + columnSize="responsiveSizeToFit", + style=dict(height=600, width="100%"), + columnDefs=column_defs, + ), + id=ids.LOADING_REFINEMENTS_TABLE, + type="circle", + color="#646569", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/save_selection_button.py b/automappa/pages/mag_refinement/components/save_selection_button.py new file mode 100644 index 00000000..789db640 --- /dev/null +++ b/automappa/pages/mag_refinement/components/save_selection_button.py @@ -0,0 +1,73 @@ +from typing import Dict, List, Protocol +import dash_mantine_components as dmc +from dash.exceptions import PreventUpdate +from dash_iconify import DashIconify + +from dash_extensions.enrich import DashProxy, html, Output, Input +from automappa.components import ids + + +class SaveSelectionButtonDataSource(Protocol): + def save_selections_to_refinement( + self, metagenome_id: int, headers: List[str] + ) -> None: + ... + + +def render(app: DashProxy, source: SaveSelectionButtonDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_REFINEMENTS_SAVE_BUTTON, "disabled"), + Input(ids.SCATTERPLOT_2D_FIGURE, "selectedData"), + ) + def disable_save_button( + selected_data: Dict[str, List[Dict[str, str]]], + ) -> bool: + if ( + selected_data + and len({point["text"] for point in selected_data["points"]}) > 0 + ): + return False + return True + + @app.callback( + Output(ids.MAG_REFINEMENTS_SAVE_BUTTON, "n_clicks"), + [ + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.SCATTERPLOT_2D_FIGURE, "selectedData"), + Input(ids.MAG_REFINEMENTS_SAVE_BUTTON, "n_clicks"), + ], + prevent_initial_call=True, + ) + def store_binning_refinement_selections( + metagenome_id: int, + selected_data: Dict[str, List[Dict[str, str]]], + n_clicks: int, + ) -> int: + # Initial load... + if not n_clicks or (n_clicks and not selected_data) or not selected_data: + raise PreventUpdate + headers = {point["text"] for point in selected_data["points"]} + source.save_selections_to_refinement( + metagenome_id=metagenome_id, headers=headers + ) + return 0 + + return html.Div( + dmc.Tooltip( + dmc.Button( + "Save MAG", + id=ids.MAG_REFINEMENTS_SAVE_BUTTON, + n_clicks=0, + size="md", + leftIcon=[DashIconify(icon="carbon:clean")], + variant="gradient", + gradient={"from": "#642E8D", "to": "#1f58a6", "deg": 150}, + disabled=True, + fullWidth=True, + ), + label="Save selection to MAG refinement", + transitionDuration=500, + openDelay=1500, + transition="fade", + ) + ) diff --git a/automappa/pages/mag_refinement/components/scatterplot_2d.py b/automappa/pages/mag_refinement/components/scatterplot_2d.py new file mode 100644 index 00000000..ca4f184a --- /dev/null +++ b/automappa/pages/mag_refinement/components/scatterplot_2d.py @@ -0,0 +1,185 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from typing import Dict, List, Literal, Optional, Protocol, Set, Tuple, Union +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html +from plotly import graph_objects as go + +from automappa.utils.figures import ( + format_axis_title, + get_scatterplot_2d, +) + +from automappa.components import ids + + +class Scatterplot2dDataSource(Protocol): + def get_scatterplot2d_records( + self, + metagenome_id: int, + x_axis: str, + y_axis: str, + color_by_col: str, + headers: Optional[List[str]], + ) -> Dict[ + Literal["x", "y", "marker_symbol", "marker_size", "text", "customdata"], + List[Union[float, str, Tuple[float, float, int]]], + ]: + ... + + def get_contig_headers_from_coverage_range( + self, metagenome_id: int, coverage_range: Tuple[float, float] + ) -> Set[str]: + ... + + def get_user_refinements_contig_headers(self, metagenome_id: int) -> Set[str]: + """Retrieve all contig headers for Refinements that are not outdated and that were not initially uploaded by the user""" + ... + + +def get_hovertemplate(x_axis: str, y_axis: str) -> str: + # Hovertemplate + x_hover_title = format_axis_title(x_axis) + y_hover_title = format_axis_title(y_axis) + text_hover_label = "Contig: %{text}" + coverage_label = "Coverage: %{customdata[0]:.2f}" + gc_content_label = "GC%: %{customdata[1]:.2f}" + length_label = "Length: %{customdata[2]:,} bp" + x_hover_label = f"{x_hover_title}: " + "%{x:.2f}" + y_hover_label = f"{y_hover_title}: " + "%{y:.2f}" + hovertemplate = "
".join( + [ + text_hover_label, + coverage_label, + gc_content_label, + length_label, + x_hover_label, + y_hover_label, + ] + ) + return hovertemplate + + +def get_traces( + data: Dict[ + str, + Dict[ + Literal[ + "x", "y", "z", "marker_size", "marker_symbol", "text", "customdata" + ], + List[Union[float, str, Tuple[float, float, int]]], + ], + ], + hovertemplate: Optional[str] = "Contig: %{text}", +) -> List[go.Scattergl]: + return [ + go.Scattergl( + x=trace["x"], + y=trace["y"], + text=trace["text"], # contig header + name=name, # groupby (color by column) value + mode="markers", + marker=dict( + size=trace["marker_size"], + line=dict(width=0.1, color="black"), + symbol=trace["marker_symbol"], + ), + customdata=trace["customdata"], + opacity=0.45, + hoverinfo="all", + hovertemplate=hovertemplate, + ) + for name, trace in data.items() + ] + + +def render(app: DashProxy, source: Scatterplot2dDataSource) -> html.Div: + @app.callback( + Output(ids.SCATTERPLOT_2D_FIGURE, "figure"), + [ + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.AXES_2D_DROPDOWN, "value"), + Input(ids.SCATTERPLOT_2D_LEGEND_TOGGLE, "checked"), + Input(ids.COLOR_BY_COLUMN_DROPDOWN, "value"), + Input(ids.HIDE_SELECTIONS_TOGGLE, "checked"), + Input(ids.COVERAGE_RANGE_SLIDER, "value"), + Input(ids.MAG_REFINEMENTS_SAVE_BUTTON, "n_clicks"), + ], + ) + def scatterplot_2d_figure_callback( + metagenome_id: int, + axes_columns: str, + show_legend: bool, + color_by_col: str, + hide_selection_toggle: bool, + coverage_range: Tuple[float, float], + btn_clicks: int, + ) -> go.Figure: + # NOTE: btn_clicks is an input so this figure is updated when new refinements are saved + # data: + # - data.x_axis # continuous values + # - data.y_axis # continuous values + # - data.text # Contig.header + # - data.groupby_value # Categoricals + # - data.marker_size + # - data.marker_symbol + # - data.customdata i.e. List[Tuple(coverage, gc_content, length)] + + x_axis, y_axis = axes_columns.split("|") + hovertemplate = get_hovertemplate(x_axis, y_axis) + + headers = source.get_contig_headers_from_coverage_range( + metagenome_id, coverage_range + ) + + if hide_selection_toggle: + refinements_headers = source.get_user_refinements_contig_headers( + metagenome_id + ) + headers = headers.difference(refinements_headers) + + records = source.get_scatterplot2d_records( + metagenome_id=metagenome_id, + x_axis=x_axis, + y_axis=y_axis, + color_by_col=color_by_col, + headers=headers, + ) + + traces = get_traces(records, hovertemplate=hovertemplate) + RIGHT_MARGIN = 20 + LEFT_MARGIN = 20 + BOTTOM_MARGIN = 20 + TOP_MARGIN = 20 + legend = go.layout.Legend(visible=show_legend, x=1, y=1) + # NOTE: Changing `uirevision` will trigger the graph to change + # graph properties state (like zooming, panning, clicking on legend items). + # i.e. if the axes change we want to reset the ui + # See: https://community.plotly.com/t/preserving-ui-state-like-zoom-in-dcc-graph-with-uirevision-with-dash/15793 + # for more details + layout = go.Layout( + legend=legend, + margin=dict(r=RIGHT_MARGIN, b=BOTTOM_MARGIN, l=LEFT_MARGIN, t=TOP_MARGIN), + hovermode="closest", + clickmode="event+select", + uirevision=axes_columns, + xaxis=go.layout.XAxis(title=format_axis_title(x_axis)), + yaxis=go.layout.YAxis(title=format_axis_title(y_axis)), + height=600, + ) + return go.Figure(data=traces, layout=layout) + + return html.Div( + [ + html.Label("Figure 1: 2D Metagenome Overview"), + dcc.Loading( + dcc.Graph( + id=ids.SCATTERPLOT_2D_FIGURE, + clear_on_unhover=True, + config={"displayModeBar": True, "displaylogo": False}, + ), + id=ids.LOADING_SCATTERPLOT_2D, + type="graph", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/scatterplot_2d_axes_dropdown.py b/automappa/pages/mag_refinement/components/scatterplot_2d_axes_dropdown.py new file mode 100644 index 00000000..44d02ed5 --- /dev/null +++ b/automappa/pages/mag_refinement/components/scatterplot_2d_axes_dropdown.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from typing import Dict, List, Literal, Protocol +from dash_extensions.enrich import DashProxy, dcc, html +import dash_mantine_components as dmc +from automappa.components import ids + + +class Scatterplot2dAxesDropdownDataSource(Protocol): + def get_scatterplot_2d_axes_options( + self, + ) -> List[Dict[Literal["label", "value", "disabled"], str]]: + ... + + +def render(app: DashProxy, source: Scatterplot2dAxesDropdownDataSource) -> html.Div: + options = [ + dmc.Radio(item["label"], value=item["value"], color="orange") + for item in source.get_scatterplot_2d_axes_options() + ] + return html.Div( + [ + html.Label("Axes:"), + dmc.RadioGroup( + options, + id=ids.AXES_2D_DROPDOWN, + value=ids.AXES_2D_DROPDOWN_VALUE_DEFAULT, + orientation="vertical", + size="sm", + spacing="xs", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/scatterplot_2d_legend_toggle.py b/automappa/pages/mag_refinement/components/scatterplot_2d_legend_toggle.py new file mode 100644 index 00000000..ecdf85bd --- /dev/null +++ b/automappa/pages/mag_refinement/components/scatterplot_2d_legend_toggle.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from dash_extensions.enrich import html +import dash_mantine_components as dmc +from automappa.components import ids + + +# Scatterplot 2D Legend Toggle +def render() -> html.Div: + return html.Div( + [ + html.Label("Legend"), + dmc.Switch( + id=ids.SCATTERPLOT_2D_LEGEND_TOGGLE, + checked=ids.SCATTERPLOT_2D_LEGEND_TOGGLE_VALUE_DEFAULT, + size="md", + color="dark", + offLabel="off", + onLabel="on", + label="Display", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/scatterplot_3d.py b/automappa/pages/mag_refinement/components/scatterplot_3d.py new file mode 100644 index 00000000..4af302ba --- /dev/null +++ b/automappa/pages/mag_refinement/components/scatterplot_3d.py @@ -0,0 +1,143 @@ +# -*- coding: utf-8 -*- + +from typing import Dict, List, Literal, Optional, Protocol, Union +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html +from plotly import graph_objects as go + +from automappa.components import ids + +from automappa.utils.figures import format_axis_title + + +class Scatterplot3dDataSource(Protocol): + def get_scaterplot3d_records( + self, + metagenome_id: int, + x_axis: str, + y_axis: str, + z_axis: str, + color_by_col: str, + headers: Optional[List[str]], + ) -> Dict[ + str, + Dict[Literal["x", "y", "z", "marker_size", "text"], List[Union[float, str]]], + ]: + ... + + +def get_hovertemplate(x_axis_label: str, y_axis_label: str, z_axis_label: str) -> str: + x_hover_label = f"{x_axis_label}: " + "%{x:.2f}" + y_hover_label = f"{y_axis_label}: " + "%{y:.2f}" + z_hover_label = f"{z_axis_label}: " + "%{z:.2f}" + text_hover_label = "Contig: %{text}" + hovertemplate = "
".join( + [text_hover_label, z_hover_label, x_hover_label, y_hover_label] + ) + return hovertemplate + + +def get_traces( + data: Dict[ + str, + Dict[Literal["x", "y", "z", "marker_size", "text"], List[Union[float, str]]], + ], + hovertemplate: Optional[str] = "Contig: %{text}", +) -> List[go.Scatter3d]: + return [ + go.Scatter3d( + x=trace["x"], + y=trace["y"], + z=trace["z"], + text=trace["text"], # contig header + name=name, # groupby (color by column) value + mode="markers", + marker=dict(size=trace["marker_size"], line=dict(width=0.1, color="black")), + opacity=0.45, + hoverinfo="all", + hovertemplate=hovertemplate, + ) + for name, trace in data.items() + ] + + +def render(app: DashProxy, source: Scatterplot3dDataSource) -> html.Div: + @app.callback( + Output(ids.SCATTERPLOT_3D, "figure"), + [ + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.AXES_2D_DROPDOWN, "value"), + Input(ids.SCATTERPLOT_3D_ZAXIS_DROPDOWN, "value"), + Input(ids.SCATTERPLOT_3D_LEGEND_TOGGLE, "checked"), + Input(ids.COLOR_BY_COLUMN_DROPDOWN, "value"), + Input(ids.SCATTERPLOT_2D_FIGURE, "selectedData"), + ], + ) + def scatterplot_3d_figure_callback( + metagenome_id: int, + axes_columns: str, + z_axis: str, + show_legend: bool, + color_by_col: str, + selected_contigs: Dict[str, List[Dict[str, str]]], + ) -> go.Figure: + headers = ( + {point["text"] for point in selected_contigs["points"]} + if selected_contigs + else None + ) + if headers and len(headers) == 1: + raise PreventUpdate + x_axis, y_axis = axes_columns.split("|") + traces_data = source.get_scaterplot3d_records( + metagenome_id=metagenome_id, + x_axis=x_axis, + y_axis=y_axis, + z_axis=z_axis, + color_by_col=color_by_col, + headers=headers, + ) + x_axis_title, y_axis_title, z_axis_title, color_by_col_title = map( + format_axis_title, [x_axis, y_axis, z_axis, color_by_col] + ) + hovertemplate = get_hovertemplate(x_axis_title, y_axis_title, z_axis_title) + traces = get_traces(traces_data, hovertemplate) + legend = go.layout.Legend( + title=color_by_col_title, x=1, y=1, visible=show_legend + ) + layout = go.Layout( + legend=legend, + scene=dict( + xaxis=dict(title=x_axis_title), + yaxis=dict(title=y_axis_title), + zaxis=dict(title=z_axis_title), + ), + autosize=True, + margin=dict(r=0, b=0, l=0, t=25), + hovermode="closest", + ) + fig = go.Figure(data=traces, layout=layout) + return fig + + graph_config = { + "toImageButtonOptions": dict( + format="svg", + filename="mag-refinement-scatterplot3d-figure", + ), + "displayModeBar": True, + "displaylogo": False, + } + return html.Div( + [ + html.Label("Figure 2: 3D Metagenome Overview"), + dcc.Loading( + dcc.Graph( + id=ids.SCATTERPLOT_3D, + clear_on_unhover=True, + config=graph_config, + ), + id=ids.LOADING_SCATTERPLOT_3D, + type="graph", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/scatterplot_3d_legend_toggle.py b/automappa/pages/mag_refinement/components/scatterplot_3d_legend_toggle.py new file mode 100644 index 00000000..275ccd21 --- /dev/null +++ b/automappa/pages/mag_refinement/components/scatterplot_3d_legend_toggle.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import dash_mantine_components as dmc +from dash_extensions.enrich import html +from automappa.components import ids + + +# Scatterplot 3D Legend Toggle +def render() -> html.Div: + return html.Div( + [ + html.Label("Legend"), + dmc.Switch( + id=ids.SCATTERPLOT_3D_LEGEND_TOGGLE, + checked=ids.SCATTERPLOT_2D_LEGEND_TOGGLE_VALUE_DEFAULT, + size="md", + color="dark", + offLabel="off", + onLabel="on", + label="Display", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/scatterplot_3d_zaxis_dropdown.py b/automappa/pages/mag_refinement/components/scatterplot_3d_zaxis_dropdown.py new file mode 100644 index 00000000..710c92a2 --- /dev/null +++ b/automappa/pages/mag_refinement/components/scatterplot_3d_zaxis_dropdown.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from dash_extensions.enrich import html, DashProxy +import dash_mantine_components as dmc +from automappa.components import ids +from typing import Protocol, List, Literal, Dict + + +class Scatterplot3dDropdownOptionsDataSource(Protocol): + def get_scatterplot_3d_zaxis_dropdown_options( + self, + ) -> List[Dict[Literal["label", "value"], str]]: + ... + + +def render(app: DashProxy, source: Scatterplot3dDropdownOptionsDataSource) -> html.Div: + options = source.get_scatterplot_3d_zaxis_dropdown_options() + radio_items = [ + dmc.Radio(option["label"], value=option["value"], color="orange") + for option in options + ] + return html.Div( + [ + html.Label("Z-axis:"), + dmc.RadioGroup( + radio_items, + id=ids.SCATTERPLOT_3D_ZAXIS_DROPDOWN, + value=ids.SCATTERPLOT_3D_ZAXIS_DROPDOWN_VALUE_DEFAULT, + spacing="xs", + size="sm", + orientation="vertical", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/settings_button.py b/automappa/pages/mag_refinement/components/settings_button.py new file mode 100644 index 00000000..9b3c0224 --- /dev/null +++ b/automappa/pages/mag_refinement/components/settings_button.py @@ -0,0 +1,25 @@ +import dash_mantine_components as dmc + +from dash_iconify import DashIconify + +from dash_extensions.enrich import DashProxy, html +from automappa.components import ids +from automappa.pages.mag_refinement.components import settings_offcanvas + + +def render(app: DashProxy, source) -> html.Div: + return html.Div( + [ + dmc.Button( + "Settings", + id=ids.SETTINGS_BUTTON, + n_clicks=0, + size="md", + leftIcon=[DashIconify(icon="clarity:settings-line")], + variant="gradient", + gradient={"from": "#CA2270", "to": "#F36E2D"}, + fullWidth=True, + ), + settings_offcanvas.render(app, source), + ] + ) diff --git a/automappa/pages/mag_refinement/components/settings_offcanvas.py b/automappa/pages/mag_refinement/components/settings_offcanvas.py new file mode 100644 index 00000000..3bd61f3c --- /dev/null +++ b/automappa/pages/mag_refinement/components/settings_offcanvas.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- + +import dash_bootstrap_components as dbc + +from dash_extensions.enrich import DashProxy, Input, Output, State +import dash_mantine_components as dmc + +from automappa.components import ids +from automappa.pages.mag_refinement.components import ( + binning_refinements_download_button, + color_by_col_dropdown, + scatterplot_2d_legend_toggle, + scatterplot_2d_axes_dropdown, + binning_refinements_clear_button, + scatterplot_3d_zaxis_dropdown, + scatterplot_3d_legend_toggle, + taxa_rank_dropdown, +) + + +def render(app: DashProxy, source) -> dbc.Offcanvas: + @app.callback( + Output(ids.SETTINGS_OFFCANVAS, "opened"), + Input(ids.SETTINGS_BUTTON, "n_clicks"), + [State(ids.SETTINGS_OFFCANVAS, "opened")], + ) + def toggle_offcanvas(n1: int, opened: bool) -> bool: + if n1: + return not opened + return opened + + return dmc.Drawer( + [ + dbc.Accordion( + [ + dbc.AccordionItem( + dmc.Stack( + [ + dmc.Group( + [ + scatterplot_2d_axes_dropdown.render( + app, source + ), + scatterplot_2d_legend_toggle.render(), + ], + spacing="xl", + ), + color_by_col_dropdown.render(app, source), + ] + ), + title="Figure 1: 2D Metagenome Overview", + ), + dbc.AccordionItem( + dmc.Group( + [ + scatterplot_3d_zaxis_dropdown.render(app, source), + scatterplot_3d_legend_toggle.render(), + ], + position="left", + spacing="xl", + ), + title="Figure 2: 3D Metagenome Overview", + ), + dbc.AccordionItem( + [ + dmc.Stack(taxa_rank_dropdown.render(app, source)), + ], + title="Figure 3: Taxonomic Distribution", + ), + ], + start_collapsed=True, + flush=True, + ), + dmc.Space(h=15), + dmc.Divider( + label="Get MAG refinements data", + labelPosition="center", + ), + dmc.Space(h=10), + dmc.Group( + [ + dmc.Space(w=10), + binning_refinements_download_button.render(app, source), + ] + ), + dmc.Space(h=15), + dmc.Divider( + label=dmc.Text("Danger zone", weight=700), + labelPosition="center", + color="red", + size="md", + ), + dmc.Space(h=10), + dmc.Group( + [dmc.Space(w=10), binning_refinements_clear_button.render(app, source)] + ), + ], + id=ids.SETTINGS_OFFCANVAS, + title="Settings", + opened=False, + position="right", + size=420, + ) diff --git a/automappa/pages/mag_refinement/components/taxa_rank_dropdown.py b/automappa/pages/mag_refinement/components/taxa_rank_dropdown.py new file mode 100644 index 00000000..41b2a0ba --- /dev/null +++ b/automappa/pages/mag_refinement/components/taxa_rank_dropdown.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from typing import Dict, List, Literal, Protocol +import dash_mantine_components as dmc +from dash_extensions.enrich import html, DashProxy +from automappa.components import ids + + +class TaxonomyDistributionDropdownDataSource(Protocol): + def get_taxonomy_distribution_dropdown_options( + self, + ) -> List[Dict[Literal["label", "value"], str]]: + ... + + +def render(app: DashProxy, source: TaxonomyDistributionDropdownDataSource) -> html.Div: + options = source.get_taxonomy_distribution_dropdown_options() + radios = [ + dmc.Radio(option["label"], value=option["value"], color="orange") + for option in options + ] + return html.Div( + [ + html.Label("Distribute taxa by rank:"), + dmc.RadioGroup( + radios, + id=ids.TAXONOMY_DISTRIBUTION_DROPDOWN, + value=ids.TAXONOMY_DISTRIBUTION_DROPDOWN_VALUE_DEFAULT, + orientation="vertical", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/components/taxonomy_distribution.py b/automappa/pages/mag_refinement/components/taxonomy_distribution.py new file mode 100644 index 00000000..9b11edb4 --- /dev/null +++ b/automappa/pages/mag_refinement/components/taxonomy_distribution.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from typing import Dict, List, Optional, Protocol +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html +import pandas as pd +from plotly import graph_objects as go + +# from automappa.data.source import SampleTables +from automappa.utils.figures import ( + taxonomy_sankey, +) + +from automappa.components import ids + + +class TaxonomyDistributionDataSource(Protocol): + def get_sankey_records( + self, + metagenome_id: int, + headers: Optional[List[str]], + selected_rank: Optional[str], + ) -> pd.DataFrame: + ... + + +def render(app: DashProxy, source: TaxonomyDistributionDataSource) -> html.Div: + @app.callback( + Output(ids.TAXONOMY_DISTRIBUTION, "figure"), + [ + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.SCATTERPLOT_2D_FIGURE, "selectedData"), + Input(ids.TAXONOMY_DISTRIBUTION_DROPDOWN, "value"), + ], + ) + def taxonomy_distribution_figure_callback( + metagenome_id: int, + selected_contigs: Dict[str, List[Dict[str, str]]], + selected_rank: str, + ) -> go.Figure: + if selected_contigs and selected_contigs["points"]: + headers = {point["text"] for point in selected_contigs["points"]} + else: + headers = None + df = source.get_sankey_records( + metagenome_id, headers=headers, selected_rank=selected_rank + ) + fig = taxonomy_sankey(df) + return fig + + return html.Div( + [ + html.Label("Figure 3: Taxonomic Distribution"), + dcc.Loading( + dcc.Graph( + id=ids.TAXONOMY_DISTRIBUTION, + config={ + "displayModeBar": False, + "displaylogo": False, + "staticPlot": False, + }, + ), + id=ids.LOADING_TAXONOMY_DISTRIBUTION, + type="graph", + ), + ] + ) diff --git a/automappa/pages/mag_refinement/layout.py b/automappa/pages/mag_refinement/layout.py new file mode 100644 index 00000000..deaff27e --- /dev/null +++ b/automappa/pages/mag_refinement/layout.py @@ -0,0 +1,94 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from dash_extensions.enrich import DashBlueprint +import dash_bootstrap_components as dbc +import dash_mantine_components as dmc +from automappa.pages.mag_refinement.source import RefinementDataSource +from automappa.components import ids +from automappa.pages.mag_refinement.components import ( + marker_symbols_legend, + scatterplot_2d, + settings_button, + save_selection_button, + hide_selections_switch, + mag_metrics_table, + taxonomy_distribution, + scatterplot_3d, + refinements_table, + mag_refinement_coverage_boxplot, + mag_refinement_gc_content_boxplot, + mag_refinement_length_boxplot, + # contig_cytoscape, # TODO + coverage_range_slider, +) + + +def render(source: RefinementDataSource) -> DashBlueprint: + app = DashBlueprint() + app.name = ids.MAG_REFINEMENT_TAB_ID + app.icon = "la:brush" + app.description = ( + "Automappa MAG refinement page to manually inspect genome binning results." + ) + app.title = "Automappa MAG refinement" + + app.layout = dbc.Container( + children=[ + dmc.Space(h=10), + dmc.Affix( + settings_button.render(app, source), position=dict(bottom=10, left=10) + ), + dmc.Affix( + save_selection_button.render(app, source), + position=dict(bottom=10, left=145), + ), + dbc.Row( + [ + dbc.Col(marker_symbols_legend.render(), width=9, align="center"), + dbc.Col(width=3), + ], + justify="center", + ), + dbc.Row( + [ + dbc.Col(scatterplot_2d.render(app, source), width=9), + dbc.Col(mag_metrics_table.render(app, source), width=3), + ] + ), + dbc.Row( + [ + dbc.Col(coverage_range_slider.render(app, source), width=9), + dbc.Col( + hide_selections_switch.render(app, source), + width=3, + align="center", + ), + ] + ), + dbc.Row( + [ + dbc.Col(taxonomy_distribution.render(app, source), width=7), + dbc.Col(scatterplot_3d.render(app, source), width=5), + ] + ), + dbc.Row( + [ + dbc.Col( + mag_refinement_coverage_boxplot.render(app, source), width=4 + ), + dbc.Col( + mag_refinement_gc_content_boxplot.render(app, source), width=4 + ), + dbc.Col(mag_refinement_length_boxplot.render(app, source), width=4), + ] + ), + # TODO Uncomment when cytoscape functionality implemented + # dbc.Row( + # [dbc.Col(contig_cytoscape.render(app, source), width=12)], + # justify="center", + # ), + dbc.Row(dbc.Col(refinements_table.render(app, source), width=12)), + ], + fluid=True, + ) + return app diff --git a/automappa/pages/mag_refinement/source.py b/automappa/pages/mag_refinement/source.py new file mode 100644 index 00000000..cd0071f3 --- /dev/null +++ b/automappa/pages/mag_refinement/source.py @@ -0,0 +1,705 @@ +#!/usr/bin/env python + +import logging +import pandas as pd +from pydantic import BaseModel +from typing import Dict, List, Literal, Optional, Set, Tuple, Union + +from sqlmodel import Session, and_, or_, select, func + +from automappa.data.database import engine +from automappa.data.models import ( + Metagenome, + Contig, + Marker, + CytoscapeConnection, + Refinement, +) +from automappa.data.schemas import ContigSchema +from datetime import datetime + +logger = logging.getLogger(__name__) + +MARKER_SET_SIZE = 139 + + +class RefinementDataSource(BaseModel): + def get_sankey_records( + self, + metagenome_id: int, + headers: Optional[List[str]], + selected_rank: Literal[ + "superkingdom", "phylum", "class", "order", "family", "genus", "species" + ] = ContigSchema.SPECIES, + ) -> pd.DataFrame: + ranks = [ + "superkingdom", + "phylum", + "class", + "order", + "family", + "genus", + "species", + ] + ranks = ranks[: ranks.index(selected_rank) + 1] + model_ranks = { + "superkingdom": Contig.superkingdom, + "phylum": Contig.phylum, + "class": Contig.klass, + "order": Contig.order, + "family": Contig.family, + "genus": Contig.genus, + "species": Contig.species, + } + selections = [model_ranks.get(rank) for rank in ranks] + selections.insert(0, Contig.header) + with Session(engine) as session: + statement = ( + select(*selections) + .join(Metagenome) + .where(Metagenome.id == metagenome_id) + ) + if headers: + statement = statement.where(Contig.header.in_(headers)) + results = session.exec(statement).all() + + schema_ranks = { + "superkingdom": ContigSchema.DOMAIN, + "phylum": ContigSchema.PHYLUM, + "class": ContigSchema.CLASS, + "order": ContigSchema.ORDER, + "family": ContigSchema.FAMILY, + "genus": ContigSchema.GENUS, + "species": ContigSchema.SPECIES, + } + columns = [schema_ranks[rank] for rank in ranks] + columns.insert(0, ContigSchema.HEADER) + + df = pd.DataFrame.from_records( + results, + index=ContigSchema.HEADER, + columns=columns, + ).fillna("unclassified") + + for rank in df.columns: + df[rank] = df[rank].map(lambda taxon: f"{rank[0]}_{taxon}") + + return df + + def get_coverage_min_max_values(self, metagenome_id: int) -> Tuple[float, float]: + with Session(engine) as session: + statement = select( + func.min(Contig.coverage), func.max(Contig.coverage) + ).where(Contig.metagenome_id == metagenome_id) + min_cov, max_cov = session.exec(statement).first() + return min_cov, max_cov + + def get_contig_headers_from_coverage_range( + self, metagenome_id: int, coverage_range: Tuple[float, float] + ) -> Set[str]: + min_cov, max_cov = coverage_range + with Session(engine) as session: + headers = session.exec( + select([Contig.header]).where( + Contig.metagenome_id == metagenome_id, + Contig.coverage >= min_cov, + Contig.coverage <= max_cov, + ) + ).all() + return set(headers) + + def get_user_refinements_contig_headers(self, metagenome_id: int) -> Set[str]: + stmt = select(Contig.header).where( + Contig.metagenome_id == metagenome_id, + Contig.refinements.any( + and_( + Refinement.initial_refinement == False, + Refinement.outdated == False, + ) + ), + ) + with Session(engine) as session: + headers = session.exec(stmt).all() + return set(headers) + + def get_scatterplot2d_records( + self, + metagenome_id: int, + x_axis: str, + y_axis: str, + color_by_col: str, + headers: Optional[List[str]] = [], + ) -> Dict[ + Literal["x", "y", "marker_symbol", "marker_size", "text", "customdata"], + List[Union[float, str, Tuple[float, float, int]]], + ]: + axes = { + ContigSchema.LENGTH: Contig.length, + ContigSchema.COVERAGE: Contig.coverage, + ContigSchema.GC_CONTENT: Contig.gc_content, + ContigSchema.X_1: Contig.x_1, + ContigSchema.X_2: Contig.x_2, + } + # Set color by column + categoricals = { + ContigSchema.CLUSTER: Contig.cluster, + ContigSchema.SUPERKINGDOM: Contig.superkingdom, + ContigSchema.PHYLUM: Contig.phylum, + ContigSchema.CLASS: Contig.klass, + ContigSchema.ORDER: Contig.order, + ContigSchema.FAMILY: Contig.family, + ContigSchema.GENUS: Contig.genus, + ContigSchema.SPECIES: Contig.species, + } + + name_select = categoricals[color_by_col] + x_select = axes[x_axis] + y_select = axes[y_axis] + stmt = select( + x_select, + y_select, + Contig.marker_size, + Contig.marker_symbol, + Contig.coverage, + Contig.gc_content, + Contig.length, + Contig.header, + name_select, + ).select_from(Contig) + + if headers: + stmt = stmt.where(Contig.header.in_(headers)) + + stmt = stmt.where(Contig.metagenome_id == metagenome_id) + + # query db + with Session(engine) as session: + results = session.exec(stmt).all() + + # format for traces + data = {} + for ( + x, + y, + marker_size, + marker_symbol, + coverage, + gc_content, + length, + header, + name, + ) in results: + customdata = (coverage, gc_content, length) + if name not in data: + data[name] = dict( + x=[x], + y=[y], + marker_size=[marker_size], + marker_symbol=[marker_symbol], + customdata=[customdata], + text=[header], + ) + else: + data[name]["x"].append(x) + data[name]["y"].append(y) + data[name]["marker_size"].append(marker_size) + data[name]["marker_symbol"].append(marker_symbol) + data[name]["customdata"].append(customdata) + data[name]["text"].append(header) + return data + + def get_scaterplot3d_records( + self, + metagenome_id: int, + x_axis: str, + y_axis: str, + z_axis: str, + color_by_col: str, + headers: Optional[List[str]] = [], + ) -> Dict[ + str, + Dict[Literal["x", "y", "z", "marker_size", "text"], List[Union[float, str]]], + ]: + # Set x,y,z axes + axes = { + ContigSchema.LENGTH: Contig.length, + ContigSchema.COVERAGE: Contig.coverage, + ContigSchema.GC_CONTENT: Contig.gc_content, + ContigSchema.X_1: Contig.x_1, + ContigSchema.X_2: Contig.x_2, + } + # Set color by column + categoricals = { + ContigSchema.CLUSTER: Contig.cluster, + ContigSchema.SUPERKINGDOM: Contig.superkingdom, + ContigSchema.PHYLUM: Contig.phylum, + ContigSchema.CLASS: Contig.klass, + ContigSchema.ORDER: Contig.order, + ContigSchema.FAMILY: Contig.family, + ContigSchema.GENUS: Contig.genus, + ContigSchema.SPECIES: Contig.species, + } + name_select = categoricals[color_by_col] + x_select = axes[x_axis] + y_select = axes[y_axis] + z_select = axes[z_axis] + stmt = select( + x_select, + y_select, + z_select, + ( + ( + func.ceil( + (Contig.length - func.min(Contig.length).over()) + / ( + func.max(Contig.length).over() + - func.min(Contig.length).over() + ) + ) + * 2 + + 4 + ).label("marker_size") + ), + Contig.header, + name_select, + ) + + if headers: + stmt = stmt.where(Contig.header.in_(headers)) + + stmt = stmt.where(Contig.metagenome_id == metagenome_id) + with Session(engine) as session: + results = session.exec(stmt).all() + + data = {} + for x, y, z, marker_size, header, name in results: + if name not in data: + data[name] = dict( + x=[x], y=[y], z=[z], marker_size=[marker_size], text=[header] + ) + else: + data[name]["x"].append(x) + data[name]["y"].append(y) + data[name]["z"].append(z) + data[name]["marker_size"].append(marker_size) + data[name]["text"].append(header) + return data + + def get_color_by_column_options(self) -> List[Dict[Literal["label", "value"], str]]: + categoricals = [ + ContigSchema.CLUSTER, + ContigSchema.SUPERKINGDOM, + ContigSchema.PHYLUM, + ContigSchema.CLASS, + ContigSchema.ORDER, + ContigSchema.FAMILY, + ContigSchema.GENUS, + ContigSchema.SPECIES, + ] + return [ + {"label": category.title(), "value": category} for category in categoricals + ] + + def get_scatterplot_2d_axes_options( + self, + ) -> List[Dict[Literal["label", "value", "disabled"], str]]: + options = [] + axes_combinations = [ + (ContigSchema.X_1, ContigSchema.X_2), + (ContigSchema.COVERAGE, ContigSchema.GC_CONTENT), + ] + for x_axis, y_axis in axes_combinations: + x_axis_label = ( + "GC content" if ContigSchema.GC_CONTENT in x_axis else x_axis.title() + ) + y_axis_label = ( + "GC content" if ContigSchema.GC_CONTENT in y_axis else y_axis.title() + ) + label = f"{x_axis_label} vs. {y_axis_label}" + value = "|".join([x_axis, y_axis]) + options.append(dict(label=label, value=value)) + return options + + def get_scatterplot_3d_zaxis_dropdown_options( + self, + ) -> List[Dict[Literal["label", "value", "disabled"], str]]: + axes = { + ContigSchema.LENGTH, + ContigSchema.COVERAGE, + ContigSchema.GC_CONTENT, + } + options = [] + for value in axes: + label = "GC content" if ContigSchema.GC_CONTENT in value else value.title() + options.append({"label": label, "value": value}) + + return options + + def get_taxonomy_distribution_dropdown_options( + self, + ) -> List[Dict[Literal["label", "value"], str]]: + ranks = [ + ContigSchema.CLASS, + ContigSchema.ORDER, + ContigSchema.FAMILY, + ContigSchema.GENUS, + ContigSchema.SPECIES, + ] + return [{"label": rank.title(), "value": rank} for rank in ranks] + + def get_marker_overview( + self, metagenome_id: int + ) -> List[Dict[Literal["metric", "metric_value"], Union[str, int, float]]]: + marker_count_stmt = ( + select(func.count(Marker.id)) + .join(Contig) + .where(Contig.metagenome_id == metagenome_id) + ) + marker_contig_count_stmt = ( + select(func.count(func.distinct(Marker.contig_id))) + .join(Contig) + .where(Contig.metagenome_id == metagenome_id) + ) + + with Session(engine) as session: + total_markers = session.exec(marker_count_stmt).first() + marker_contigs_count = session.exec(marker_contig_count_stmt).first() or 0 + + markers_sets = total_markers // MARKER_SET_SIZE + return [ + {"metric": "Total Markers", "metric_value": total_markers}, + {"metric": "Marker Set Size", "metric_value": MARKER_SET_SIZE}, + {"metric": "Approx. Marker Sets", "metric_value": markers_sets}, + {"metric": "Marker Contigs", "metric_value": marker_contigs_count}, + ] + + def get_mag_metrics_row_data( + self, metagenome_id: int, headers: Optional[List[str]] + ) -> List[Dict[Literal["metric", "metric_value"], Union[str, int, float]]]: + contig_count_stmt = select(func.count(Contig.id)).where( + Contig.metagenome_id == metagenome_id + ) + contig_length_stmt = select(func.sum(Contig.length)).where( + Contig.metagenome_id == metagenome_id + ) + marker_contig_count_stmt = ( + select(func.count(func.distinct(Marker.contig_id))) + .join(Contig) + .where(Contig.metagenome_id == metagenome_id) + ) + # - single-copy marker contig count + single_copy_stmt = ( + select(Contig.id) + .where(Contig.metagenome_id == metagenome_id) + .join(Marker) + .group_by(Contig.id) + .having(func.count(Marker.id) == 1) + ) + # - multi-copy marker contig count + multi_copy_stmt = ( + select(Contig.id) + .where(Contig.metagenome_id == metagenome_id) + .join(Marker) + .group_by(Contig.id) + .having(func.count(Marker.id) > 1) + ) + marker_count_stmt = ( + select(func.count(Marker.id)) + .join(Contig) + .where(Contig.metagenome_id == metagenome_id) + ) + unique_marker_stmt = ( + select(Marker.sacc) + .join(Contig) + .distinct() + .where(Contig.metagenome_id == metagenome_id) + ) + redundant_marker_sacc_stmt = ( + select(Marker.sacc) + .join(Contig) + .where(Contig.metagenome_id == metagenome_id) + .group_by(Marker.sacc) + .having(func.count(Marker.id) > 1) + ) + if headers: + contig_count_stmt = contig_count_stmt.where(Contig.header.in_(headers)) + contig_length_stmt = contig_length_stmt.where(Contig.header.in_(headers)) + marker_contig_count_stmt = marker_contig_count_stmt.where( + Contig.header.in_(headers) + ) + multi_copy_stmt = multi_copy_stmt.where(Contig.header.in_(headers)) + single_copy_stmt = single_copy_stmt.where(Contig.header.in_(headers)) + redundant_marker_sacc_stmt = redundant_marker_sacc_stmt.where( + Contig.header.in_(headers) + ) + marker_count_stmt = marker_count_stmt.where(Contig.header.in_(headers)) + unique_marker_stmt = unique_marker_stmt.where(Contig.header.in_(headers)) + + with Session(engine) as session: + contig_count = session.exec(contig_count_stmt).first() or 0 + length_sum = session.exec(contig_length_stmt).first() or 0 + marker_contigs_count = session.exec(marker_contig_count_stmt).first() or 0 + single_copy_contig_count = ( + session.exec(select(func.count()).select_from(single_copy_stmt)).first() + or 0 + ) + multi_copy_contig_count = ( + session.exec(select(func.count()).select_from(multi_copy_stmt)).first() + or 0 + ) + markers_count = session.exec(marker_count_stmt).first() or 0 + unique_marker_count = session.exec( + select(func.count()).select_from(unique_marker_stmt) + ).first() + redundant_marker_sacc = session.exec(redundant_marker_sacc_stmt).all() + + completeness = round(unique_marker_count / MARKER_SET_SIZE * 100, 2) + purity = ( + round(unique_marker_count / markers_count * 100, 2) if markers_count else 0 + ) + length_sum_mbp = round(length_sum / 1_000_000, 3) + + row_data = [ + {"metric": "Contigs", "metric_value": contig_count}, + {"metric": "Length Sum (Mbp)", "metric_value": length_sum_mbp}, + { + "metric": "Marker Contigs", + "metric_value": marker_contigs_count, + }, + { + "metric": "Multi-Marker Contigs", + "metric_value": multi_copy_contig_count, + }, + { + "metric": "Single-Marker Contigs", + "metric_value": single_copy_contig_count, + }, + {"metric": "Markers Count", "metric_value": markers_count}, + { + "metric": "Redundant Markers", + "metric_value": len(redundant_marker_sacc), + }, + { + "metric": "Redundant Marker Accessions", + "metric_value": ", ".join(redundant_marker_sacc), + }, + ] + if headers: + row_data.insert(0, {"metric": "Purity (%)", "metric_value": purity}) + row_data.insert( + 0, {"metric": "Completeness (%)", "metric_value": completeness} + ) + return row_data + + def get_coverage_boxplot_records( + self, metagenome_id: int, headers: Optional[List[str]] + ) -> List[Tuple[str, List[float]]]: + with Session(engine) as session: + stmt = select(Contig.coverage).where(Contig.metagenome_id == metagenome_id) + if headers: + stmt = stmt.where(Contig.header.in_(headers)) + coverages = session.exec(stmt).all() + coverages = [round(coverage, 2) for coverage in coverages] + return [(ContigSchema.COVERAGE.title(), coverages)] + + def get_gc_content_boxplot_records( + self, metagenome_id: int, headers: Optional[List[str]] + ) -> List[Tuple[str, List[float]]]: + with Session(engine) as session: + stmt = select(Contig.gc_content).where( + Contig.metagenome_id == metagenome_id + ) + if headers: + stmt = stmt.where(Contig.header.in_(headers)) + gc_contents = session.exec(stmt).all() + + gc_contents = [round(gc_content, 2) for gc_content in gc_contents] + return [("GC Content", gc_contents)] + + def get_length_boxplot_records( + self, metagenome_id: int, headers: Optional[List[str]] + ) -> List[Tuple[str, List[int]]]: + with Session(engine) as session: + stmt = select(Contig.length).where(Contig.metagenome_id == metagenome_id) + if headers: + stmt = stmt.where(Contig.header.in_(headers)) + lengths = session.exec(stmt).all() + return [(ContigSchema.LENGTH.title(), lengths)] + + def get_cytoscape_elements( + self, metagenome_id: int, headers: Optional[List[str]] = [] + ) -> List[ + Dict[ + Literal["data"], + Dict[ + Literal["id", "label", "source", "target", "connections"], + Union[str, int], + ], + ] + ]: + stmt = ( + select( + CytoscapeConnection.node1, + CytoscapeConnection.node2, + CytoscapeConnection.connections, + ) + .select_from(CytoscapeConnection) + .where(CytoscapeConnection.metagenome_id == metagenome_id) + ) + if headers: + start_nodes = {f"{header}s" for header in headers} + end_nodes = {f"{header}e" for header in headers} + nodes = start_nodes.union(end_nodes) + stmt = stmt.where( + or_( + CytoscapeConnection.node1.in_(nodes), + CytoscapeConnection.node2.in_(nodes), + ) + ) + with Session(engine) as session: + records = session.exec(stmt).all() + + src_nodes = {src_node for src_node, *_ in records} + target_nodes = {target_node for _, target_node, _ in records} + nodes = [ + dict(data=dict(id=node, label=node)) + for node in src_nodes.union(target_nodes) + ] + edges = [ + dict( + data=dict(source=src_node, target=target_node, connections=connections) + ) + for src_node, target_node, connections in records + ] + return nodes + edges + + def get_cytoscape_stylesheet( + self, metagenome_id: int, headers: Optional[List[str]] + ) -> List: + stmt = ( + select( + CytoscapeConnection.node1, + CytoscapeConnection.node2, + CytoscapeConnection.connections, + ) + .select_from(CytoscapeConnection) + .where(CytoscapeConnection.metagenome_id == metagenome_id) + ) + if headers: + start_nodes = {f"{header}s" for header in headers} + end_nodes = {f"{header}e" for header in headers} + nodes = start_nodes.union(end_nodes) + stmt = stmt.where( + or_( + CytoscapeConnection.node1.in_(nodes), + CytoscapeConnection.node2.in_(nodes), + ) + ) + with Session(engine) as session: + records = session.exec(stmt).all() + stylesheet = [ + dict( + selector=f"[label = {node1}]", + style={"line-color": "blue", "opacity": 0.8}, + ) + for node1, *_ in records + ] + stylesheet += [ + dict( + selector=f"[label = {node2}]", + style={"line-color": "blue", "opacity": 0.8}, + ) + for _, node2, _ in records + ] + return stylesheet + + def has_user_refinements(self, metagenome_id: int) -> bool: + with Session(engine) as session: + refinement = session.exec( + select(Refinement).where( + Refinement.metagenome_id == metagenome_id, + Refinement.initial_refinement == False, + Refinement.outdated == False, + ) + ).first() + if refinement: + return True + return False + + def clear_refinements(self, metagenome_id: int) -> int: + with Session(engine) as session: + refinements = session.exec( + select(Refinement).where( + Refinement.metagenome_id == metagenome_id, + Refinement.initial_refinement == False, + ) + ).all() + n_refinements = len(refinements) + for refinement in refinements: + session.delete(refinement) + session.commit() + return n_refinements + + def get_refinements_row_data( + self, metagenome_id: int + ) -> List[ + Dict[ + Literal["refinement_id", "timestamp", "contigs"], + Union[str, int, datetime], + ] + ]: + stmt = select(Refinement).where( + Refinement.metagenome_id == metagenome_id, + Refinement.outdated == False, + Refinement.initial_refinement == False, + ) + data = [] + with Session(engine) as session: + refinements = session.exec(stmt).all() + for refinement in refinements: + row = dict( + refinement_id=refinement.id, + timestamp=refinement.timestamp.strftime("%d-%b-%Y, %H:%M:%S"), + contigs=len(refinement.contigs), + ) + data.append(row) + return data + + def save_selections_to_refinement( + self, metagenome_id: int, headers: List[str] + ) -> None: + with Session(engine) as session: + contigs = session.exec( + select(Contig).where( + Contig.metagenome_id == metagenome_id, Contig.header.in_(headers) + ) + ).all() + for contig in contigs: + for refinement in contig.refinements: + refinement.outdated = True + refinement = Refinement( + contigs=contigs, + metagenome_id=metagenome_id, + outdated=False, + initial_refinement=False, + ) + session.add(refinement) + session.commit() + + def get_refinements_dataframe(self, metagenome_id: int) -> pd.DataFrame: + stmt = select(Refinement).where( + Refinement.metagenome_id == metagenome_id, + Refinement.outdated == False, + ) + data = [] + with Session(engine) as session: + refinements = session.exec(stmt).all() + for refinement in refinements: + data.append( + dict( + refinement_id=f"refinement_{refinement.id}", + timestamp=refinement.timestamp.strftime("%d-%b-%Y"), + contig=[contig.header for contig in refinement.contigs], + ) + ) + return pd.DataFrame(data).explode("contig") diff --git a/automappa/pages/mag_summary/__init__.py b/automappa/pages/mag_summary/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/automappa/pages/mag_summary/components/__init__.py b/automappa/pages/mag_summary/components/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/automappa/pages/mag_summary/components/mag_coverage_boxplot.py b/automappa/pages/mag_summary/components/mag_coverage_boxplot.py new file mode 100644 index 00000000..e29e833e --- /dev/null +++ b/automappa/pages/mag_summary/components/mag_coverage_boxplot.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- + +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html + +from plotly import graph_objects as go +from typing import Protocol, List, Tuple +from automappa.utils.figures import metric_boxplot +from automappa.components import ids + + +class ClusterCoverageBoxplotDataSource(Protocol): + def get_coverage_boxplot_records( + self, metagenome_id: int, refinement_id: int + ) -> List[Tuple[str, List[float]]]: + ... + + +def render(app: DashProxy, source: ClusterCoverageBoxplotDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_COVERAGE_BOXPLOT, "figure"), + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.MAG_SELECTION_DROPDOWN, "value"), + prevent_initial_call=True, + ) + def mag_summary_coverage_boxplot_callback( + metagenome_id: int, refinement_id: int + ) -> go.Figure: + data = source.get_coverage_boxplot_records( + metagenome_id, refinement_id=refinement_id + ) + fig = metric_boxplot(data) + return fig + + return html.Div( + dcc.Loading( + id=ids.LOADING_MAG_COVERAGE_BOXPLOT, + children=[ + dcc.Graph( + id=ids.MAG_COVERAGE_BOXPLOT, + config={"displayModeBar": False, "displaylogo": False}, + ) + ], + type="default", + color="#0479a8", + ) + ) diff --git a/automappa/pages/mag_summary/components/mag_gc_content_boxplot.py b/automappa/pages/mag_summary/components/mag_gc_content_boxplot.py new file mode 100644 index 00000000..0db58036 --- /dev/null +++ b/automappa/pages/mag_summary/components/mag_gc_content_boxplot.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- + +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html + +from plotly import graph_objects as go +from typing import Protocol, List, Tuple + +from automappa.utils.figures import metric_boxplot +from automappa.components import ids + + +class GcContentBoxplotDataSource(Protocol): + def get_gc_content_boxplot_records( + self, metagenome_id: int, refinement_id: int + ) -> List[Tuple[str, List[float]]]: + ... + + +def render(app: DashProxy, source: GcContentBoxplotDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_GC_CONTENT_BOXPLOT, "figure"), + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.MAG_SELECTION_DROPDOWN, "value"), + prevent_initial_call=True, + ) + def mag_summary_gc_content_boxplot_callback( + metagenome_id: int, refinement_id: int + ) -> go.Figure: + data = source.get_gc_content_boxplot_records( + metagenome_id, refinement_id=refinement_id + ) + fig = metric_boxplot(data) + return fig + + return html.Div( + [ + dcc.Loading( + dcc.Graph( + id=ids.MAG_GC_CONTENT_BOXPLOT, + config={"displayModeBar": False, "displaylogo": False}, + ), + id=ids.LOADING_MAG_GC_CONTENT_BOXPLOT, + type="default", + color="#0479a8", + ) + ] + ) diff --git a/automappa/pages/mag_summary/components/mag_length_boxplot.py b/automappa/pages/mag_summary/components/mag_length_boxplot.py new file mode 100644 index 00000000..3f346de5 --- /dev/null +++ b/automappa/pages/mag_summary/components/mag_length_boxplot.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- + +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html + +from plotly import graph_objects as go + +from typing import Protocol, List, Tuple +from automappa.utils.figures import metric_boxplot +from automappa.components import ids + + +class ClusterLengthBoxplotDataSource(Protocol): + def get_length_boxplot_records( + self, metagenome_id: int, refinement_id: int + ) -> List[Tuple[str, List[int]]]: + ... + + +def render(app: DashProxy, source: ClusterLengthBoxplotDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_LENGTH_BOXPLOT, "figure"), + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.MAG_SELECTION_DROPDOWN, "value"), + prevent_initial_call=True, + ) + def mag_summary_gc_content_boxplot_callback( + metagenome_id: int, refinement_id: int + ) -> go.Figure: + data = source.get_length_boxplot_records( + metagenome_id, refinement_id=refinement_id + ) + fig = metric_boxplot(data) + return fig + + return html.Div( + children=[ + dcc.Loading( + id=ids.LOADING_MAG_LENGTH_BOXPLOT, + children=[ + dcc.Graph( + id=ids.MAG_LENGTH_BOXPLOT, + config={"displayModeBar": False, "displaylogo": False}, + ) + ], + type="dot", + color="#646569", + ) + ] + ) diff --git a/automappa/pages/mag_summary/components/mag_metrics_barplot.py b/automappa/pages/mag_summary/components/mag_metrics_barplot.py new file mode 100644 index 00000000..9965569b --- /dev/null +++ b/automappa/pages/mag_summary/components/mag_metrics_barplot.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +from typing import Protocol, List, Tuple + +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html + +from plotly import graph_objects as go + +from automappa.utils.figures import metric_barplot +from automappa.components import ids + + +class ClusterMetricsBarplotDataSource(Protocol): + def get_metrics_barplot_records( + self, metagenome_id: int, refinement_id: int + ) -> Tuple[str, List[float], List[float]]: + ... + + +def render(app: DashProxy, source: ClusterMetricsBarplotDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_METRICS_BARPLOT, "figure"), + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.MAG_SELECTION_DROPDOWN, "value"), + prevent_initial_call=True, + ) + def mag_metrics_callback(metagenome_id: int, refinement_id: int) -> go.Figure: + data = source.get_metrics_barplot_records( + metagenome_id, refinement_id=refinement_id + ) + fig = metric_barplot(data) + return fig + + return html.Div( + children=[ + dcc.Loading( + id=ids.LOADING_MAG_METRICS_BARPLOT, + children=[ + dcc.Graph( + id=ids.MAG_METRICS_BARPLOT, + config={"displayModeBar": False, "displaylogo": False}, + ) + ], + type="dot", + color="#646569", + ) + ] + ) diff --git a/automappa/pages/mag_summary/components/mag_overview_coverage_boxplot.py b/automappa/pages/mag_summary/components/mag_overview_coverage_boxplot.py new file mode 100644 index 00000000..115c1482 --- /dev/null +++ b/automappa/pages/mag_summary/components/mag_overview_coverage_boxplot.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- + +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html + +from typing import Protocol, List, Tuple, Optional +from plotly import graph_objects as go + +from automappa.utils.figures import metric_boxplot +from automappa.components import ids + + +class OverviewCoverageBoxplotDataSource(Protocol): + def get_coverage_boxplot_records( + self, metagenome_id: int, cluster: Optional[str] + ) -> List[Tuple[str, List[float]]]: + ... + + +def render(app: DashProxy, source: OverviewCoverageBoxplotDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_OVERVIEW_COVERAGE_BOXPLOT, "figure"), + Input(ids.METAGENOME_ID_STORE, "data"), + ) + def mag_overview_coverage_boxplot_callback(metagenome_id: int) -> go.Figure: + data = source.get_coverage_boxplot_records(metagenome_id) + fig = metric_boxplot(data) + return fig + + return html.Div( + children=[ + dcc.Loading( + id=ids.LOADING_MAG_COVERAGE_BOXPLOT, + children=[ + dcc.Graph( + id=ids.MAG_OVERVIEW_COVERAGE_BOXPLOT, + config={"displayModeBar": False, "displaylogo": False}, + ) + ], + type="dot", + color="#646569", + ) + ] + ) diff --git a/automappa/pages/mag_summary/components/mag_overview_gc_content_boxplot.py b/automappa/pages/mag_summary/components/mag_overview_gc_content_boxplot.py new file mode 100644 index 00000000..4fd09606 --- /dev/null +++ b/automappa/pages/mag_summary/components/mag_overview_gc_content_boxplot.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +from typing import List, Optional, Protocol, Tuple +from dash.exceptions import PreventUpdate +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html + +from plotly import graph_objects as go + +from automappa.utils.figures import metric_boxplot +from automappa.components import ids + + +class GcContentBoxplotDataSource(Protocol): + def get_gc_content_boxplot_records( + self, metagenome_id: int, cluster: Optional[str] + ) -> List[Tuple[str, List[float]]]: + ... + + +def render(app: DashProxy, source: GcContentBoxplotDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_OVERVIEW_GC_CONTENT_BOXPLOT, "figure"), + Input(ids.METAGENOME_ID_STORE, "data"), + ) + def mag_overview_gc_content_boxplot_callback(metagenome_id: int) -> go.Figure: + data = source.get_gc_content_boxplot_records(metagenome_id=metagenome_id) + fig = metric_boxplot(data=data) + return fig + + return html.Div( + children=[ + dcc.Loading( + id=ids.LOADING_MAG_OVERVIEW_GC_CONTENT_BOXPLOT, + children=[ + dcc.Graph( + id=ids.MAG_OVERVIEW_GC_CONTENT_BOXPLOT, + config={"displayModeBar": False, "displaylogo": False}, + ) + ], + type="dot", + color="#646569", + ) + ] + ) diff --git a/automappa/pages/mag_summary/components/mag_overview_length_boxplot.py b/automappa/pages/mag_summary/components/mag_overview_length_boxplot.py new file mode 100644 index 00000000..b6753bf7 --- /dev/null +++ b/automappa/pages/mag_summary/components/mag_overview_length_boxplot.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- + +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html +from typing import Protocol, Optional, List, Tuple +from plotly import graph_objects as go + +from automappa.utils.figures import metric_boxplot +from automappa.components import ids + + +class LengthOverviewBoxplotDataSource(Protocol): + def get_length_boxplot_records( + self, metagenome_id: int, cluster: Optional[str] + ) -> List[Tuple[str, List[int]]]: + ... + + +def render(app: DashProxy, source: LengthOverviewBoxplotDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_OVERVIEW_LENGTH_BOXPLOT, "figure"), + Input(ids.METAGENOME_ID_STORE, "data"), + ) + def mag_overview_length_boxplot_callback(metagenome_id: int) -> go.Figure: + data = source.get_length_boxplot_records(metagenome_id) + fig = metric_boxplot(data) + return fig + + return html.Div( + children=[ + dcc.Loading( + id=ids.LOADING_MAG_OVERVIEW_LENGTH_BOXPLOT, + children=[ + dcc.Graph( + id=ids.MAG_OVERVIEW_LENGTH_BOXPLOT, + config={"displayModeBar": False, "displaylogo": False}, + ) + ], + type="default", + color="#0479a8", + ) + ] + ) diff --git a/automappa/pages/mag_summary/components/mag_overview_metrics_boxplot.py b/automappa/pages/mag_summary/components/mag_overview_metrics_boxplot.py new file mode 100644 index 00000000..288b8d44 --- /dev/null +++ b/automappa/pages/mag_summary/components/mag_overview_metrics_boxplot.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- + +from typing import List, Protocol, Tuple +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html + +from plotly import graph_objects as go + +from automappa.utils.figures import metric_boxplot +from automappa.components import ids + + +class OverviewMetricsBoxplotDataSource(Protocol): + def get_completeness_purity_boxplot_records( + self, metagenome_id: int + ) -> List[Tuple[str, List[float]]]: + ... + + +def render(app: DashProxy, source: OverviewMetricsBoxplotDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_OVERVIEW_METRICS_BOXPLOT, "figure"), + Input(ids.METAGENOME_ID_STORE, "data"), + ) + def subset_by_selected_mag(metagenome_id: int) -> go.Figure: + data = source.get_completeness_purity_boxplot_records(metagenome_id) + fig = metric_boxplot(data=data) + return fig + + return html.Div( + dcc.Loading( + dcc.Graph( + id=ids.MAG_OVERVIEW_METRICS_BOXPLOT, + config={"displayModeBar": False, "displaylogo": False}, + ), + id=ids.LOADING_MAG_OVERVIEW_METRICS_BOXPLOT, + type="default", + color="#0479a8", + ) + ) diff --git a/automappa/pages/mag_summary/components/mag_selection_dropdown.py b/automappa/pages/mag_summary/components/mag_selection_dropdown.py new file mode 100644 index 00000000..25b908fb --- /dev/null +++ b/automappa/pages/mag_summary/components/mag_selection_dropdown.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- + +from typing import Dict, List, Protocol, Literal +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html + +from automappa.components import ids + + +class ClusterSelectionDropdownOptionsDataSource(Protocol): + def get_refinement_selection_dropdown_options( + self, metagenome_id: int + ) -> List[Dict[Literal["label", "value"], str]]: + ... + + +def render( + app: DashProxy, source: ClusterSelectionDropdownOptionsDataSource +) -> html.Div: + @app.callback( + Output(ids.MAG_SELECTION_DROPDOWN, "options"), + Input(ids.METAGENOME_ID_STORE, "data"), + ) + def mag_selection_dropdown_options_callback( + metagenome_id: int, + ) -> List[Dict[Literal["label", "value"], str]]: + options = source.get_refinement_selection_dropdown_options(metagenome_id) + return options + + return html.Div( + [ + html.Label("MAG Selection Dropdown"), + dcc.Dropdown( + id=ids.MAG_SELECTION_DROPDOWN, + clearable=True, + placeholder="Select a MAG from this dropdown for a MAG-specific summary", + ), + ] + ) diff --git a/automappa/pages/mag_summary/components/mag_summary_stats_datatable.py b/automappa/pages/mag_summary/components/mag_summary_stats_datatable.py new file mode 100644 index 00000000..3ac22888 --- /dev/null +++ b/automappa/pages/mag_summary/components/mag_summary_stats_datatable.py @@ -0,0 +1,123 @@ +# -*- coding: utf-8 -*- +from typing import Protocol, List, Union, Literal, Dict +import dash_ag_grid as dag +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html + +from automappa.components import ids + + +class SummaryStatsTableDataSource(Protocol): + def get_mag_stats_summary_row_data( + self, metagenome_id: int + ) -> List[ + Dict[ + Literal[ + "refinement_id", + "refinement_label", + "length_sum_mbp", + "completeness", + "purity", + "contig_count", + ], + Union[str, int, float], + ] + ]: + ... + + +def render(app: DashProxy, source: SummaryStatsTableDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_SUMMARY_STATS_DATATABLE, "rowData"), + Input(ids.METAGENOME_ID_STORE, "data"), + ) + def mag_summary_stats_datatable_callback( + metagenome_id: int, + ) -> List[ + Dict[ + Literal[ + "refinement_id", + "refinement_label", + "length_sum_mbp", + "completeness", + "purity", + "contig_count", + ], + Union[str, int, float], + ] + ]: + row_data = source.get_mag_stats_summary_row_data(metagenome_id) + return row_data + + GREEN = "#2FCC90" + YELLOW = "#f2e530" + ORANGE = "#f57600" + MIMAG_STYLE_CONDITIONS = { + "styleConditions": [ + # High-quality >90% complete > 95% pure + { + "condition": "params.data.completeness == 'Completeness (%)' && params.value > 90", + "style": {"backgroundColor": GREEN}, + }, + { + "condition": "params.data.purity == 'Purity (%)' && params.value > 95", + "style": {"backgroundColor": GREEN}, + }, + # Medium-quality >=50% complete > 90% pure + { + "condition": "params.data.completeness == 'Completeness (%)' && params.value >= 50", + "style": {"backgroundColor": YELLOW}, + }, + { + "condition": "params.data.purity == 'Purity (%)' && params.value > 90", + "style": {"backgroundColor": YELLOW}, + }, + # Low-quality <50% complete < 90% pure + { + "condition": "params.data.completeness == 'Completeness (%)' && params.value < 50", + "style": {"backgroundColor": ORANGE, "color": "white"}, + }, + { + "condition": "params.data.purity == 'Purity (%)' && params.value < 90", + "style": {"backgroundColor": ORANGE, "color": "white"}, + }, + ] + } + + column_defs = [ + {"field": "refinement_id", "headerName": "Refinement Id", "resizable": True}, + { + "field": "refinement_label", + "headerName": "Refinement Label", + "resizable": True, + }, + { + "field": "length_sum_mbp", + "headerName": "Length Sum (Mbp)", + "resizable": True, + }, + {"field": "completeness", "headerName": "Completeness (%)", "resizable": True}, + {"field": "purity", "headerName": "Purity (%)", "resizable": True}, + { + "field": "contig_count", + "headerName": "Contig Count", + "cellStyle": MIMAG_STYLE_CONDITIONS, + }, + ] + + return html.Div( + [ + html.Label("Table 1. MAGs Summary"), + dcc.Loading( + dag.AgGrid( + id=ids.MAG_SUMMARY_STATS_DATATABLE, + className="ag-theme-material", + columnSize="responsiveSizeToFit", + style={"height": 600, "width": "100%"}, + columnDefs=column_defs, + ), + id=ids.LOADING_MAG_SUMMARY_STATS_DATATABLE, + type="circle", + color="#646569", + ), + ] + ) diff --git a/automappa/pages/mag_summary/components/mag_taxonomy_sankey.py b/automappa/pages/mag_summary/components/mag_taxonomy_sankey.py new file mode 100644 index 00000000..634df3d0 --- /dev/null +++ b/automappa/pages/mag_summary/components/mag_taxonomy_sankey.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +from typing import Protocol +from dash_extensions.enrich import DashProxy, Input, Output, dcc, html + +from plotly import graph_objects as go + +from automappa.utils.figures import taxonomy_sankey +from automappa.components import ids + + +class ClusterTaxonomySankeyDataSource(Protocol): + def get_taxonomy_sankey_records(self, metagenome_id: int, refinement_id: int): + ... + + +def render(app: DashProxy, source: ClusterTaxonomySankeyDataSource) -> html.Div: + @app.callback( + Output(ids.MAG_TAXONOMY_SANKEY, "figure"), + Input(ids.METAGENOME_ID_STORE, "data"), + Input(ids.MAG_SELECTION_DROPDOWN, "value"), + prevent_initial_call=True, + ) + def mag_taxonomy_sankey_callback( + metagenome_id: int, refinement_id: int + ) -> go.Figure: + data = source.get_taxonomy_sankey_records( + metagenome_id, refinement_id=refinement_id + ) + fig = taxonomy_sankey(data) + return fig + + return html.Div( + children=[ + dcc.Loading( + id=ids.LOADING_MAG_TAXONOMY_SANKEY, + children=[dcc.Graph(id=ids.MAG_TAXONOMY_SANKEY)], + type="graph", + ) + ] + ) diff --git a/automappa/pages/mag_summary/layout.py b/automappa/pages/mag_summary/layout.py new file mode 100644 index 00000000..f080f79e --- /dev/null +++ b/automappa/pages/mag_summary/layout.py @@ -0,0 +1,56 @@ +import dash_bootstrap_components as dbc +from dash_extensions.enrich import DashBlueprint +from automappa.components import ids +from automappa.pages.mag_summary.source import SummaryDataSource +from automappa.pages.mag_summary.components import ( + mag_coverage_boxplot, + mag_gc_content_boxplot, + mag_length_boxplot, + mag_overview_coverage_boxplot, + mag_overview_length_boxplot, + mag_overview_gc_content_boxplot, + mag_overview_metrics_boxplot, + mag_selection_dropdown, + mag_summary_stats_datatable, + mag_taxonomy_sankey, + mag_metrics_barplot, +) + + +def render(source: SummaryDataSource) -> DashBlueprint: + app = DashBlueprint() + app.name = ids.MAG_SUMMARY_TAB_ID + app.icon = "material-symbols:auto-graph-rounded" + app.description = ( + "Automappa MAG summary page displaying overview of genome binning results." + ) + app.title = "Automappa MAG summary" + + app.layout = dbc.Container( + [ + dbc.Row( + [ + dbc.Col(mag_overview_metrics_boxplot.render(app, source), width=3), + dbc.Col( + mag_overview_gc_content_boxplot.render(app, source), width=3 + ), + dbc.Col(mag_overview_length_boxplot.render(app, source), width=3), + dbc.Col(mag_overview_coverage_boxplot.render(app, source), width=3), + ] + ), + dbc.Row(dbc.Col(mag_summary_stats_datatable.render(app, source))), + dbc.Row(dbc.Col(mag_selection_dropdown.render(app, source))), + dbc.Row(dbc.Col(mag_taxonomy_sankey.render(app, source))), + dbc.Row( + [ + dbc.Col(mag_metrics_barplot.render(app, source), width=3), + dbc.Col(mag_gc_content_boxplot.render(app, source), width=3), + dbc.Col(mag_length_boxplot.render(app, source), width=3), + dbc.Col(mag_coverage_boxplot.render(app, source), width=3), + ] + ), + ], + fluid=True, + ) + + return app diff --git a/automappa/pages/mag_summary/source.py b/automappa/pages/mag_summary/source.py new file mode 100644 index 00000000..651ffd0e --- /dev/null +++ b/automappa/pages/mag_summary/source.py @@ -0,0 +1,258 @@ +#!/usr/bin/env python +import logging +import pandas as pd +from pydantic import BaseModel +from typing import Dict, List, Literal, Optional, Tuple, Union + +from sqlmodel import Session, and_, select, func +from automappa.data.database import engine +from automappa.data.models import Refinement, Contig, Marker +from automappa.data.schemas import ContigSchema + +logger = logging.getLogger(__name__) + +MARKER_SET_SIZE = 139 + + +class SummaryDataSource(BaseModel): + def compute_completeness_purity_metrics( + self, metagenome_id: int, refinement_id: int + ) -> Tuple[float, float]: + marker_count_stmt = ( + select(func.count(Marker.id)) + .join(Contig) + .where( + Contig.metagenome_id == metagenome_id, + Contig.refinements.any( + and_( + Refinement.outdated == False, + Refinement.id == refinement_id, + ) + ), + ) + ) + unique_marker_stmt = ( + select(Marker.sacc) + .join(Contig) + .distinct() + .where( + Contig.metagenome_id == metagenome_id, + Contig.refinements.any( + and_( + Refinement.outdated == False, + Refinement.id == refinement_id, + ) + ), + ) + ) + with Session(engine) as session: + markers_count = session.exec(marker_count_stmt).first() or 0 + unique_marker_count = session.exec( + select(func.count()).select_from(unique_marker_stmt) + ).first() + + completeness = round(unique_marker_count / MARKER_SET_SIZE * 100, 2) + purity = ( + round(unique_marker_count / markers_count * 100, 2) if markers_count else 0 + ) + return completeness, purity + + def compute_length_sum_mbp(self, metagenome_id: int, refinement_id: int) -> float: + contig_length_stmt = select(func.sum(Contig.length)).where( + Contig.metagenome_id == metagenome_id, + Contig.refinements.any( + and_( + Refinement.outdated == False, + Refinement.id == refinement_id, + ) + ), + ) + with Session(engine) as session: + length_sum = session.exec(contig_length_stmt).first() or 0 + length_sum_mbp = round(length_sum / 1_000_000, 3) + return length_sum_mbp + + def get_completeness_purity_boxplot_records( + self, metagenome_id: int + ) -> List[Tuple[str, List[float]]]: + completeness_metrics = [] + purities = [] + with Session(engine) as session: + stmt = select(Refinement.id).where( + Refinement.outdated == False, Refinement.metagenome_id == metagenome_id + ) + refinement_ids = session.exec(stmt).all() + for refinement_id in refinement_ids: + ( + completeness, + purity, + ) = self.compute_completeness_purity_metrics(metagenome_id, refinement_id) + completeness_metrics.append(completeness) + purities.append(purity) + return [ + (ContigSchema.COMPLETENESS.title(), completeness_metrics), + (ContigSchema.PURITY.title(), purities), + ] + + def get_gc_content_boxplot_records( + self, metagenome_id: int, refinement_id: Optional[int] = 0 + ) -> List[Tuple[str, List[float]]]: + stmt = select([Contig.gc_content]).where(Contig.metagenome_id == metagenome_id) + if refinement_id: + stmt = stmt.where( + Contig.refinements.any( + and_( + Refinement.outdated == False, + Refinement.id == refinement_id, + ) + ) + ) + with Session(engine) as session: + results = session.exec(stmt).all() + return [("GC Content", results)] + + def get_length_boxplot_records( + self, metagenome_id: int, refinement_id: Optional[int] = 0 + ) -> List[Tuple[str, List[int]]]: + stmt = select([Contig.length]).where(Contig.metagenome_id == metagenome_id) + if refinement_id: + stmt = stmt.where( + Contig.refinements.any( + and_( + Refinement.outdated == False, + Refinement.id == refinement_id, + ) + ) + ) + with Session(engine) as session: + results = session.exec(stmt).all() + return [(ContigSchema.LENGTH.title(), results)] + + def get_coverage_boxplot_records( + self, metagenome_id: int, refinement_id: Optional[int] = 0 + ) -> List[Tuple[str, List[float]]]: + stmt = select([Contig.coverage]).where(Contig.metagenome_id == metagenome_id) + if refinement_id: + stmt = stmt.where( + Contig.refinements.any( + and_( + Refinement.outdated == False, + Refinement.id == refinement_id, + ) + ) + ) + with Session(engine) as session: + results = session.exec(stmt).all() + return [(ContigSchema.COVERAGE.title(), results)] + + def get_metrics_barplot_records( + self, metagenome_id: int, refinement_id: int + ) -> Tuple[str, List[float], List[float]]: + completeness, purity = self.compute_completeness_purity_metrics( + metagenome_id=metagenome_id, refinement_id=refinement_id + ) + name = f"bin_{refinement_id} Metrics" + x = [ContigSchema.COMPLETENESS.title(), ContigSchema.PURITY.title()] + y = [completeness, purity] + return name, x, y + + def get_mag_stats_summary_row_data( + self, metagenome_id: int + ) -> List[ + Dict[ + Literal[ + "refinement_id", + "refinement_label", + "length_sum_mbp", + "completeness", + "purity", + "contig_count", + ], + Union[str, int, float], + ] + ]: + stmt = select(Refinement).where( + Refinement.metagenome_id == metagenome_id, + Refinement.outdated == False, + ) + row_data = {} + with Session(engine) as session: + refinements = session.exec(stmt).all() + for refinement in refinements: + contig_count = len(refinement.contigs) + row_data[refinement.id] = { + "refinement_id": refinement.id, + "refinement_label": f"bin_{refinement.id}", + "contig_count": contig_count, + } + for refinement_id in row_data: + completeness, purity = self.compute_completeness_purity_metrics( + metagenome_id, refinement_id + ) + length_sum_mbp = self.compute_length_sum_mbp( + metagenome_id, refinement_id + ) + row_data[refinement_id].update( + { + "completeness": completeness, + "purity": purity, + "length_sum_mbp": length_sum_mbp, + } + ) + return list(row_data.values()) + + def get_refinement_selection_dropdown_options( + self, metagenome_id: int + ) -> List[Dict[Literal["label", "value"], str]]: + stmt = ( + select([Refinement.id]) + .where( + Refinement.metagenome_id == metagenome_id, + Refinement.outdated == False, + ) + .distinct() + ) + with Session(engine) as session: + results = session.exec(stmt).all() + return [dict(label=f"bin_{result}", value=result) for result in results] + + def get_taxonomy_sankey_records( + self, metagenome_id: int, refinement_id: int + ) -> pd.DataFrame: + statement = select( + Contig.header, + Contig.superkingdom, + Contig.phylum, + Contig.klass, + Contig.order, + Contig.family, + Contig.genus, + Contig.species, + ).where( + Contig.metagenome_id == metagenome_id, + Contig.refinements.any(Refinement.id == refinement_id), + ) + with Session(engine) as session: + results = session.exec(statement).all() + + columns = [ + ContigSchema.HEADER, + ContigSchema.DOMAIN, + ContigSchema.PHYLUM, + ContigSchema.CLASS, + ContigSchema.ORDER, + ContigSchema.FAMILY, + ContigSchema.GENUS, + ContigSchema.SPECIES, + ] + + df = pd.DataFrame.from_records( + results, + index=ContigSchema.HEADER, + columns=columns, + ).fillna("unclassified") + + for rank in df.columns: + df[rank] = df[rank].map(lambda taxon: f"{rank[0]}_{taxon}") + + return df diff --git a/automappa/pages/not_found_404.py b/automappa/pages/not_found_404.py new file mode 100644 index 00000000..89a1e29b --- /dev/null +++ b/automappa/pages/not_found_404.py @@ -0,0 +1,197 @@ +from typing import Union +import dash_mantine_components as dmc +from dash_extensions.enrich import DashBlueprint, html +from dash_iconify import DashIconify + + +def get_icon( + icon: str, height: int = 30, width: Union[int, None] = None +) -> DashIconify: + return DashIconify(icon=icon, height=height, width=width) + + +alert = dmc.Alert( + dmc.Stack( + [ + dmc.Text( + "Uh oh! Looks like you've hit a broken link. Try returning home to continue...", + size="xl", + color="gray", + ), + dmc.Anchor( + dmc.Button( + "Home", + variant="outline", + leftIcon=get_icon("line-md:home"), + color="info", + fullWidth=True, + ), + href="/", + underline=False, + ), + ], + style={"height": 100}, + spacing="xs", + align="stretch", + justify="space-around", + ), + title="Something went wrong", + color="info", +) + +evan_rees_hover_card = dmc.HoverCard( + shadow="md", + children=[ + dmc.HoverCardTarget( + dmc.Avatar( + src="https://avatars.githubusercontent.com/u/25933122?v=4", + radius="xl", + size="xl", + ) + ), + dmc.HoverCardDropdown( + [ + dmc.Text("Evan Rees", align="center"), + dmc.Group( + [ + dmc.Anchor( + get_icon(icon="openmoji:github", width=40), + href="https://www.github.com/WiscEvan/", + target="_blank", + ), + dmc.Anchor( + get_icon(icon="openmoji:linkedin", width=40), + href="https://www.linkedin.com/in/evanroyrees/", + target="_blank", + ), + dmc.Anchor( + get_icon(icon="academicons:google-scholar", width=40), + href="https://scholar.google.com/citations?user=9TL02VUAAAAJ", + target="_blank", + ), + dmc.Anchor( + get_icon(icon="ic:round-self-improvement", width=40), + href="https://wiscevan.github.io", + target="_blank", + ), + ], + p=0, + ), + dmc.Text("Say hi!", color="dimmed", align="center"), + ] + ), + ], +) +kwanlab_hover_card = dmc.HoverCard( + shadow="md", + children=[ + dmc.HoverCardTarget( + dmc.Avatar( + src="https://avatars.githubusercontent.com/u/6548561?v=4", + radius="xl", + size="xl", + ) + ), + dmc.HoverCardDropdown( + [ + dmc.Text("Jason C. Kwan Lab", align="center"), + dmc.Group( + [ + dmc.Anchor( + get_icon(icon="openmoji:github", width=40), + href="https://www.github.com/KwanLab/", + target="_blank", + ), + dmc.Anchor( + get_icon(icon="openmoji:linkedin", width=40), + href="https://www.linkedin.com/in/jason-kwan-79137324/", + target="_blank", + ), + dmc.Anchor( + get_icon(icon="academicons:google-scholar", width=40), + href="https://scholar.google.com/citations?user=zKnYsSsAAAAJ", + target="_blank", + ), + dmc.Anchor( + get_icon(icon="openmoji:twitter", width=40), + href="https://twitter.com/kwan_lab", + target="_blank", + ), + dmc.Anchor( + get_icon(icon="guidance:medical-laboratory", width=40), + href="https://kwanlab.github.io/", + target="_blank", + ), + ], + p=0, + ), + ] + ), + ], +) + + +sample_icons = [ + get_icon("file-icons:influxdata"), + get_icon("healthicons:animal-spider-outline"), + get_icon("ph:plant"), + get_icon("healthicons:animal-chicken-outline"), + get_icon("healthicons:bacteria-outline"), + get_icon("game-icons:mushrooms-cluster"), + get_icon("healthicons:malaria-mixed-microscope-outline"), + get_icon("game-icons:scarab-beetle"), + get_icon("healthicons:animal-cow-outline"), + get_icon("fluent-emoji-high-contrast:fish"), + get_icon("streamline:nature-ecology-potted-cactus-tree-plant-succulent-pot"), +] + +new_issue_avatar = html.A( + dmc.Tooltip( + dmc.Avatar( + get_icon("emojione:waving-hand-medium-light-skin-tone"), + size="md", + radius="xl", + ), + label="Provide feedback", + position="bottom", + ), + href="https://github.com/WiscEvan/Automappa/issues/new", + target="_blank", +) + + +def render() -> DashBlueprint: + app = DashBlueprint() + app.name = "not_found_404" + app.description = "Automappa app link not found 404 page" + app.title = "Automappa 404" + app.layout = dmc.Container( + [ + dmc.Space(h=30), + dmc.Center(alert), + dmc.Center( + [ + new_issue_avatar, + dmc.Space(w=30), + evan_rees_hover_card, + dmc.Space(w=30), + kwanlab_hover_card, + ], + style={"width": "100%", "height": 200}, + ), + dmc.Footer( + dmc.Grid( + children=[dmc.Col(icon, span="auto") for icon in sample_icons], + justify="space-between", + align="center", + gutter="xs", + grow=True, + ), + height=40, + fixed=True, + withBorder=False, + ), + ], + fluid=True, + ) + return app diff --git a/automappa/settings.py b/automappa/settings.py index 4e4d18a4..a756f5d9 100644 --- a/automappa/settings.py +++ b/automappa/settings.py @@ -16,7 +16,7 @@ class DatabaseSettings(BaseSettings): url: PostgresDsn - pool_size: Optional[int] = 3 + pool_size: Optional[int] = 4 pool_pre_ping: Optional[bool] = False class Config: @@ -25,6 +25,18 @@ class Config: env_file_encoding: str = "utf-8" +class RedisSettings(BaseSettings): + host: str + port: int + db: int + password: str + + class Config: + env_prefix: str = "REDIS_BACKEND_" + env_file: str = ".env" + env_file_encoding: str = "utf-8" + + class RabbitmqSettings(BaseSettings): url: AmqpDsn @@ -68,6 +80,7 @@ class Config: server = ServerSettings() db = DatabaseSettings() +redis = RedisSettings() database = DatabaseSettings() rabbitmq = RabbitmqSettings() celery = CelerySettings() diff --git a/automappa/tasks.py b/automappa/tasks.py index d353bc4f..faa3d09c 100644 --- a/automappa/tasks.py +++ b/automappa/tasks.py @@ -1,319 +1,35 @@ #!/usr/bin/env python +import logging - -# import random -import tempfile - -# import time -from typing import List -import numpy as np -import pandas as pd - -from geom_median.numpy import compute_geometric_median - -from celery import Celery, group +from celery import Celery from celery.utils.log import get_task_logger -from celery.result import AsyncResult -# from dash.long_callback import CeleryLongCallbackManager - -from Bio import SeqIO - -from autometa.common import kmers -from autometa.binning.summary import fragmentation_metric, get_metabin_stats from automappa import settings -from automappa.utils.figures import get_scattergl_traces -from automappa.utils.markers import get_marker_symbols -from automappa.utils.serializers import ( - get_metagenome_seqrecords, - get_table, - table_to_db, +logging.basicConfig( + format="[%(levelname)s] %(name)s: %(message)s", + level=logging.DEBUG, ) +numba_logger = logging.getLogger("numba") +numba_logger.setLevel(logging.WARNING) +numba_logger.propagate = False +h5py_logger = logging.getLogger("h5py") +h5py_logger.setLevel(logging.WARNING) +h5py_logger.propagate = False +root_logger = logging.getLogger() +root_logger.setLevel(logging.WARNING) queue = Celery( - __name__, backend=settings.celery.backend_url, broker=settings.celery.broker_url + __name__, + backend=settings.celery.backend_url, + broker=settings.celery.broker_url, ) queue.config_from_object("automappa.conf.celeryconfig") -# long_callback_manager = CeleryLongCallbackManager(queue) -logger = get_task_logger(__name__) +task_logger = get_task_logger(__name__) if settings.server.debug: - logger.debug( + task_logger.debug( f"celery config:\n{queue.conf.humanize(with_defaults=False, censored=True)}" ) - - -def get_job(job_id): - """ - To be called from automappa web app. - The job ID is passed and the celery job is returned. - """ - return AsyncResult(job_id, app=queue) - -def get_task(job_id) -> dict[str,str]: - """ - To be called from automappa web app. - The job ID is passed and the celery job is returned. - """ - task = AsyncResult(job_id, app=queue) - task_attrs = { - "task_name": task.name, - "state": task.state, - "task_id": task.id, - } - if hasattr(task, "track_started"): - task_attrs.update({"started": task.track_started}) - return task_attrs - - -@queue.task(bind=True) -def preprocess_marker_symbols(self, binning_table: str, markers_table: str) -> str: - bin_df = get_table(binning_table, index_col="contig") - markers_df = get_table(markers_table, index_col="contig") - marker_symbols_df = get_marker_symbols(bin_df, markers_df).set_index("contig") - marker_symbols_table = markers_table.replace("-markers", "-marker-symbols") - table_to_db(marker_symbols_df, marker_symbols_table, index=True) - return marker_symbols_table - - -# TODO: STRETCH GOALS -# Data loader [stretch...] -# CheckM annotation - -# TODO -# Create 2d-scatterplot figure -# Marker symbols table -# kmer freq. analysis pipeline -# scatterplot 2-d embedding views - - -@queue.task(bind=True) -def count_kmer(self, metagenome_table: str, size: int = 5, cpus: int = None) -> str: - records = get_metagenome_seqrecords(metagenome_table) - # Uncomment next line to speed-up debugging... - # FIXME: Comment out below: - # records = random.sample(records, k=1_000) - with tempfile.NamedTemporaryFile(mode="w") as tmp: - SeqIO.write(records, tmp.name, "fasta") - tmp.seek(0) - counts = kmers.count( - assembly=tmp.name, - size=size, - out=None, - force=False, - verbose=False, - cpus=cpus, - ) - logger.info("count finished") - # Strip sample name of -metagenome tag (will append the rest for kmer-pipeline) - counts_table = metagenome_table.replace("-metagenome", f"-{size}mers") - table_to_db(counts, name=counts_table, index=True) - return counts_table - - -@queue.task(bind=True) -def normalize_kmer(self, counts_table: str, norm_method: str) -> str: - counts = get_table(counts_table, index_col="contig") - norm_df = kmers.normalize(counts, method=norm_method) - norm_table = f"{counts_table}-{norm_method}" - table_to_db(norm_df, name=norm_table, index=True) - return norm_table - - -@queue.task(bind=True) -def embed_kmer( - self, norm_table: str, embed_method: str, embed_dims: int = 2, n_jobs: int = 1 -) -> str: - norm_df = get_table(norm_table, index_col="contig") - # FIXME: Refactor renaming of embed_df.columns - prev_kmer_params = norm_table.split("-", 1)[-1] - method_kwargs = {"output_dens": True} if embed_method in {"umap", "densmap"} else {} - # output_dens: float (optional, default False) - # Determines whether the local radii of the final embedding (an inverse - # measure of local density) are computed and returned in addition to - # the embedding. If set to True, local radii of the original data - # are also included in the output for comparison; the output is a tuple - # (embedding, original local radii, embedding local radii). This option - # can also be used when densmap=False to calculate the densities for - # UMAP embeddings. - embed_df = kmers.embed( - norm_df, - method=embed_method, - embed_dimensions=embed_dims, - n_jobs=n_jobs, - **method_kwargs, - ).rename( - columns={ - "x_1": f"{prev_kmer_params}-{embed_method}_x_1", - "x_2": f"{prev_kmer_params}-{embed_method}_x_2", - } - ) - embed_table = f"{norm_table}-{embed_method}" - table_to_db(embed_df, name=embed_table, index=True) - return embed_table - - -@queue.task(bind=True) -def aggregate_embeddings(self, embed_tables: List[str], table_name: str) -> str: - df = pd.concat( - [get_table(embed_table, index_col="contig") for embed_table in embed_tables], - axis=1, - ) - table_to_db(df, table_name, index=True) - return table_name - - -def preprocess_embeddings( - metagenome_table: str, - cpus: int = 1, - kmer_size: int = 5, - norm_method: str = "am_clr", - embed_dims: int = 2, - embed_methods: List[str] = ["bhsne", "densmap", "trimap", "umap"], -): - embeddings_table = metagenome_table.replace( - "-metagenome", f"-{kmer_size}mers-{norm_method}-embeddings" - ) - kmer_pipeline = ( - count_kmer.s(metagenome_table, kmer_size, cpus) - | normalize_kmer.s(norm_method) - | group( - embed_kmer.s(embed_method, embed_dims, cpus) - for embed_method in embed_methods - ) - | aggregate_embeddings.s(embeddings_table) - ) - result = kmer_pipeline() - # while not result.ready(): - # time.sleep(1) - # with open('graph.dot', 'w') as fh: - # result.parent.parent.parent.graph.to_dot(fh) - logger.debug(f"k-mer pipeline result: {result}") - return result - - -@queue.task -def preprocess_clusters_geom_medians( - binning_table: str, cluster_col: str = "cluster", weight_col: str = "length" -) -> str: - """Compute each cluster's (`cluster_col`) geometric median weighted by contig length (`weight_col`) - - Parameters - ---------- - df : pd.DataFrame - Table containing x_1 and x_2 coordinates and `cluster_col` from embedding - cluster_col : str, optional - Value to use for cluster column, by default "cluster" - weight_col : str, optional - Column to use for weighting the geometric median computation, by default 'length' - - Returns - ------- - pd.DataFrame - index=range(cluster_1, cluster_n), cols=[cluster_col, x_1, x_2, termination, weighted] - `x_1` and `x_2` correspond to the computed geometric median values corresponding to the respective cluster. - `termination` is the reason of termination passed from the `compute_geometric_median` function - `weighted` denotes the value that was used for weighting the cluster's geometric median (`weight_col`) - - """ - df = get_table(binning_table, index_col="contig") - medians = [] - for cluster, dff in df.groupby(cluster_col): - points = dff[["x_1", "x_2"]].to_numpy() - if weight_col: - weights = dff[weight_col].to_numpy() - out = compute_geometric_median(points=points, weights=weights) - median = out.median - medians.append( - { - cluster_col: cluster, - "x_1": median[0], - "x_2": median[1], - "termination": out.termination, - "weighted": weight_col, - } - ) - medians_df = pd.DataFrame(medians) - medians_table = binning_table.replace("-binning", f"{cluster_col}-gmedians") - table_to_db(medians_df, medians_table) - return medians_table - - -@queue.task -def get_embedding_traces_df(embeddings_table: str) -> pd.DataFrame: - # 1. Compute all embeddings for assembly... - # 2. groupby cluster - # 3. Extract k-mer size, norm method, embed method - df = get_table(embeddings_table, index_col="contig") - embed_traces = [] - for embed_method in ["trimap", "densmap", "bhsne", "umap", "sksne"]: - traces_df = get_scattergl_traces( - df, f"{embed_method}_x_1", f"{embed_method}_x_2", "cluster" - ) - traces_df.rename(columns={"trace": embed_method}, inplace=True) - embed_traces.append(traces_df) - embed_traces_df = pd.concat(embed_traces, axis=1) - return embed_traces_df - -# @queue.task(bind=True) -# def get_metabin_stats_summary(self, binning_table:str, refinements_table:str, markers_table:str, cluster_col:str = "cluster"): -def get_metabin_stats_summary(binning_table:str, refinements_table:str, markers_table:str, cluster_col:str = "cluster") -> pd.DataFrame: - bin_df = get_table(binning_table, index_col="contig") - refinements_df = get_table(refinements_table, index_col='contig').drop( - columns="cluster" - ) - bin_df = bin_df.join(refinements_df, how="right") - markers = get_table(markers_table, index_col='contig') - if cluster_col not in bin_df.columns: - num_expected_markers = markers.shape[1] - length_weighted_coverage = np.average( - a=bin_df.coverage, weights=bin_df.length / bin_df.length.sum() - ) - length_weighted_gc = np.average( - a=bin_df.gc_content, weights=bin_df.length / bin_df.length.sum() - ) - cluster_pfams = markers[markers.index.isin(bin_df.index)] - pfam_counts = cluster_pfams.sum() - total_markers = pfam_counts.sum() - num_single_copy_markers = pfam_counts[pfam_counts == 1].count() - num_markers_present = pfam_counts[pfam_counts >= 1].count() - stats_df = pd.DataFrame( - [ - { - cluster_col: "metagenome", - "nseqs": bin_df.shape[0], - "size (bp)": bin_df.length.sum(), - "N90": fragmentation_metric(bin_df, quality_measure=0.9), - "N50": fragmentation_metric(bin_df, quality_measure=0.5), - "N10": fragmentation_metric(bin_df, quality_measure=0.1), - "length_weighted_gc_content": length_weighted_gc, - "min_gc_content": bin_df.gc_content.min(), - "max_gc_content": bin_df.gc_content.max(), - "std_gc_content": bin_df.gc_content.std(), - "length_weighted_coverage": length_weighted_coverage, - "min_coverage": bin_df.coverage.min(), - "max_coverage": bin_df.coverage.max(), - "std_coverage": bin_df.coverage.std(), - "num_total_markers": total_markers, - f"num_unique_markers (expected {num_expected_markers})": num_markers_present, - "num_single_copy_markers": num_single_copy_markers, - } - ] - ).convert_dtypes() - else: - stats_df = ( - get_metabin_stats( - bin_df=bin_df, - markers=markers, - cluster_col=cluster_col, - ) - .reset_index() - .fillna(0) - ) - return stats_df - -if __name__ == "__main__": - pass diff --git a/automappa/utils/figures.py b/automappa/utils/figures.py index 72a1172d..df3cc488 100644 --- a/automappa/utils/figures.py +++ b/automappa/utils/figures.py @@ -7,25 +7,17 @@ from plotly import graph_objects as go -def taxonomy_sankey(df: pd.DataFrame, selected_rank: str = "species") -> go.Figure: - ranks = ["superkingdom", "phylum", "class", "order", "family", "genus", "species"] - n_ranks = len(ranks[: ranks.index(selected_rank)]) - dff = df[[col for col in df.columns if col in ranks]].fillna("unclassified") - for rank in ranks: - if rank in dff: - dff[rank] = dff[rank].map( - lambda taxon: f"{rank[0]}_{taxon}" - if rank != "superkingdom" - else f"d_{taxon}" - ) +def taxonomy_sankey(df: pd.DataFrame) -> go.Figure: + ranks = df.columns.tolist() + n_ranks = len(ranks) label = [] - for rank in ranks[:n_ranks]: - label.extend(dff[rank].unique().tolist()) + for rank in ranks: + label.extend(df[rank].unique().tolist()) source = [] target = [] value = [] - for rank in ranks[:n_ranks]: - for rank_name, rank_df in dff.groupby(rank): + for rank in ranks: + for rank_name, rank_df in df.groupby(rank): source_index = label.index(rank_name) next_rank_i = ranks.index(rank) + 1 if next_rank_i >= len(ranks[:n_ranks]): @@ -40,27 +32,24 @@ def taxonomy_sankey(df: pd.DataFrame, selected_rank: str = "species") -> go.Figu target.append(target_index) value.append(value_count) return go.Figure( - data=[ - go.Sankey( - node=dict( - pad=8, - thickness=13, - line=dict(width=0.3), - label=label, - ), - link=dict( - source=source, - target=target, - value=value, - ), - ) - ] + go.Sankey( + node=dict( + pad=8, + thickness=13, + line=dict(width=0.3), + label=label, + ), + link=dict( + source=source, + target=target, + value=value, + ), + ), ) def metric_boxplot( - df: pd.DataFrame, - metrics: List[str] = [], + data: List[Tuple[str, pd.Series]], horizontal: bool = False, boxmean: Union[bool, str] = True, ) -> go.Figure: @@ -68,10 +57,8 @@ def metric_boxplot( Parameters ---------- - df : pd.DataFrame + data : List[Tuple[str,pd.Series]] MAG annotations dataframe - metrics : List[str], optional - MAG metrics to use for generating traces horizontal : bool, optional Whether to generate horizontal or vertical boxplot traces in the figure. boxmean : Union[bool,str], optional @@ -88,45 +75,28 @@ def metric_boxplot( PreventUpdate No metrics were provided to generate traces. """ - fig = go.Figure() - if not metrics: + if not data: raise PreventUpdate - for metric in metrics: - name = metric.replace("_", " ").title() + traces = [] + for metric, series in data: if horizontal: - trace = go.Box(x=df[metric], name=name, boxmean=boxmean) + trace = go.Box(x=series, name=metric, boxmean=boxmean) else: - trace = go.Box(y=df[metric], name=name, boxmean=boxmean) - # TODO: round to two decimal places - # Perhaps a hovertemplate formatting issue? - fig.add_trace(trace) - return fig + trace = go.Box(y=series, name=metric, boxmean=boxmean) + traces.append(trace) + return go.Figure(data=traces) def metric_barplot( - df: pd.DataFrame, - metrics: List[str] = [], + data: Tuple[str, List[float], List[float]], horizontal: bool = False, - name: str = None, ) -> go.Figure: - if not metrics: + if not data: raise PreventUpdate - x = [metric.replace("_", " ").title() for metric in metrics] - y = [df[metric].iat[0] for metric in metrics] + name, x, y = data orientation = "h" if horizontal else "v" - return go.Figure([go.Bar(x=x, y=y, orientation=orientation, name=name)]) - - -def marker_size_scaler(x: pd.DataFrame, scale_by: str = "length") -> int: - x_min_scaler = x[scale_by] - x[scale_by].min() - x_max_scaler = x[scale_by].max() - x[scale_by].min() - if not x_max_scaler: - # Protect Division by 0 - x_ceil = np.ceil(x_min_scaler / x_max_scaler + 1) - else: - x_ceil = np.ceil(x_min_scaler / x_max_scaler) - x_scaled = x_ceil * 2 + 4 - return x_scaled + trace = go.Bar(x=x, y=y, orientation=orientation, name=name) + return go.Figure([trace]) def format_axis_title(axis_title: str) -> str: @@ -305,6 +275,8 @@ def get_scatterplot_2d( margin=dict(r=50, b=50, l=50, t=50), hovermode="closest", clickmode="event+select", + height=600, + width="100%", ) fig = go.Figure(layout=layout) traces_df = get_scattergl_traces( @@ -321,10 +293,10 @@ def get_scatterplot_2d( def get_scatterplot_3d( df: pd.DataFrame, - x_axis: str = "x_1", - y_axis: str = "x_2", - z_axis: str = "coverage", - color_by_col: str = "cluster", + x_axis: str, + y_axis: str, + z_axis: str, + color_by_col: str, ) -> go.Figure: """Create go.Figure from `df` @@ -332,20 +304,32 @@ def get_scatterplot_3d( ---------- df : pd.DataFrame index_col=[contig], cols=[`x_axis`, `y_axis`, `z_axis`] - x_axis : str, optional - _description_, by default "x_1" - y_axis : str, optional - _description_, by default "x_2" - z_axis : str, optional - _description_, by default "coverage" - color_by_col : str, optional - _description_, by default "cluster" + x_axis : str + continuous column for x-axis + y_axis : str + continuous column for y-axis + z_axis : str + continuous column for z-axis + color_by_col : str + categorical column for color-by-col Returns ------- go.Figure _description_ """ + + def marker_size_scaler(x: pd.DataFrame, scale_by: str = "length") -> int: + x_min_scaler = x[scale_by] - x[scale_by].min() + x_max_scaler = x[scale_by].max() - x[scale_by].min() + if not x_max_scaler: + # Protect Division by 0 + x_ceil = np.ceil(x_min_scaler / x_max_scaler + 1) + else: + x_ceil = np.ceil(x_min_scaler / x_max_scaler) + x_scaled = x_ceil * 2 + 4 + return x_scaled + x_axis_title = format_axis_title(x_axis) y_axis_title = format_axis_title(y_axis) z_axis_title = format_axis_title(z_axis) diff --git a/automappa/utils/kmers.py b/automappa/utils/kmers.py deleted file mode 100644 index fb9ca31e..00000000 --- a/automappa/utils/kmers.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python - -import glob -import os -import re - -# from autometa.common.kmers import count, normalize, embed, load -import pandas as pd -from plotly import graph_objects as go - -from automappa.utils.figures import get_scattergl_traces - - -embedding_fpaths = glob.glob("data/nubbins/kmers/*5mers*am_clr.*2.tsv.gz") - -embeddings = [] -for fp in embedding_fpaths: - df = pd.read_csv(fp, sep="\t", index_col="contig") - basename = os.path.basename(fp) - mers, norm_method, embed_method_dim, *__ = basename.split(".") - match = re.match("(\w+)(\d+)", embed_method_dim) - if match: - embed_method, embed_dim = match.groups() - df.rename( - columns={ - "x_1": f"{embed_method}_x_1", - "x_2": f"{embed_method}_x_2", - }, - inplace=True, - ) - embeddings.append(df) -embeddings_df = pd.concat(embeddings, axis=1) - -df = pd.read_csv("data/nubbins/nubbins.tsv", sep="\t") -main_df = df.drop(columns=["x_1", "x_2"]).set_index("contig").join(embeddings_df) - -embed_traces = [] -for embed_method in ["trimap", "densmap", "bhsne", "umap", "sksne"]: - traces_df = get_scattergl_traces( - main_df, f"{embed_method}_x_1", f"{embed_method}_x_2", "cluster" - ) - traces_df.rename(columns={"trace": embed_method}, inplace=True) - embed_traces.append(traces_df) - -traces = pd.concat(embed_traces, axis=1) - -fig = go.Figure() -fig.add_traces(traces.umap.tolist()) -# Update according to selected embed_method... -embed_method = "densmap" -fig.for_each_trace(lambda trace: trace.update(traces.loc[trace.name, embed_method])) -embed_method = "trimap" -fig.for_each_trace(lambda trace: trace.update(traces.loc[trace.name, embed_method])) -embed_method = "umap" -fig.for_each_trace(lambda trace: trace.update(traces.loc[trace.name, embed_method])) -embed_method = "sksne" -fig.for_each_trace(lambda trace: trace.update(traces.loc[trace.name, embed_method])) -embed_method = "bhsne" -fig.for_each_trace(lambda trace: trace.update(traces.loc[trace.name, embed_method])) diff --git a/automappa/utils/models.py b/automappa/utils/models.py deleted file mode 100644 index 4a163512..00000000 --- a/automappa/utils/models.py +++ /dev/null @@ -1,265 +0,0 @@ -import itertools -from pydantic import BaseModel, Field, PydanticValueError, create_model -from pydantic.fields import ModelField -from typing import List, Literal, Optional - -# from cached_property import cached_property_with_ttl -from celery.result import AsyncResult - -from automappa.db import engine, metadata -from automappa.utils.serializers import get_table - - -class RestrictedAlphabetStr(str): - @classmethod - def __get_validators__(cls): - yield cls.validate - - @classmethod - def validate(cls, value, field: ModelField): - alphabet = field.field_info.extra["alphabet"] - if any(c not in alphabet for c in value): - raise ValueError(f"{value!r} is not restricted to {alphabet!r}") - return cls(value) - - @classmethod - def __modify_schema__(cls, field_schema, field: Optional[ModelField]): - if field: - alphabet = field.field_info.extra["alphabet"] - field_schema["examples"] = [c * 4 for c in alphabet] - - -class MetagenomeAnnotations(BaseModel): - contig: str - binning: Optional[str] - refinements: Optional[str] - markers: Optional[str] - # marker_symbols: Union[AsyncResult,str] - metagenome: Optional[str] - - -class NotInDatabaseError(PydanticValueError): - code = "table_name_not_in_db" - tables = metadata.tables.keys() - msg_template = f'value "TEMPLATE" is not in {tables}' - msg_template = msg_template.replace("TEMPLATE", "{table_name}") - - -class AnnotationTable(BaseModel): - id: str - index_col: Optional[str] = "contig" - - # @validator('id', pre=True) - # @validator('id') - # def table_must_exist_in_database(cls, v): - # if v not in metadata.tables.keys(): - # # if not engine.has_table(v): - # raise NotInDatabaseError(table_name=v) - # return v - - # @validator('index_col') - # def index_col_in_table(cls, v, values): - # table_id = values.get('id') - # table_cols = get_table(table_id).columns - # if v not in table_cols: - # raise ValueError(f'{v} not in {table_id} columns {table_cols}') - # return v - - # class Config: - # keep_untouched=(cached_property,) - - @property - def sample_checksum(self): - return self.id.split("-")[0] - - @property - def name(self): - return "-".join(self.id.split("-")[1:]) - - @property - def table(self): - return get_table(self.id, index_col=self.index_col) - - @property - def exists(self): - return engine.has_table(self.id) - - @property - def columns(self): - return self.table.columns - - -# class ResultTable(AnnotationTable): -# task: Optional[AsyncResult] -# def table(self): -# return get_table(self.id, index_col=self.index_col) - -# class Config: -# arbitrary_types_allowed = True -# smart_union = True - - -class KmerTable(BaseModel): - assembly: AnnotationTable - size: Literal[3, 4, 5] = 5 - norm_method: Literal["am_clr", "ilr", "clr"] = "am_clr" - embed_dims: Optional[int] = 2 - # embed_dims: conint(gt=1, lt=100) = 2 - embed_method: Literal["bhsne", "sksne", "umap", "densmap", "trimap"] = "bhsne" - - @property - def counts(self) -> AnnotationTable: - return AnnotationTable( - id=self.assembly.id.replace("-metagenome", f"-{self.size}mers") - ) - - @property - def norm_freqs(self) -> AnnotationTable: - return AnnotationTable( - id=self.assembly.id.replace( - "-metagenome", f"-{self.size}mers-{self.norm_method}" - ) - ) - - @property - def embedding(self) -> AnnotationTable: - return AnnotationTable( - id=self.assembly.id.replace( - "-metagenome", - f"-{self.size}mers-{self.norm_method}-{self.embed_method}", - ) - ) - - -class SampleTables(BaseModel): - binning: Optional[AnnotationTable] - markers: Optional[AnnotationTable] - metagenome: Optional[AnnotationTable] - # The following are created after user upload - # via: - # db ingestion (see serializers.py) - # celery tasks (see tasks.py) - refinements: Optional[AnnotationTable] - # geom_medians: Optional[str] - - @property - def kmers(self) -> List[KmerTable]: - settings = [] - sizes = [3, 4, 5] - norm_methods = ["am_clr", "ilr"] - embed_methods = ["bhsne", "densmap", "umap", "sksne", "trimap"] - for size, norm_method, embed_method in itertools.product( - sizes, norm_methods, embed_methods - ): - settings.append( - KmerTable( - assembly=self.metagenome, - size=size, - norm_method=norm_method, - embed_dims=2, - embed_method=embed_method, - ) - ) - return settings - - @property - def embeddings(self) -> List[AnnotationTable]: - kmer_sizes = set([kmer_table.size for kmer_table in self.kmers]) - norm_methods = set([kmer_table.norm_method for kmer_table in self.kmers]) - # NOTE: The corresponding table-name suffix format (AnnotationTable(id=...)) - # is at https://github.com/WiscEvan/Automappa/blob/977dbbf6dca8cc62f974eb1c6a2f48fc25f2ddb2/automappa/tasks.py#L149 - return [ - AnnotationTable( - id=self.metagenome.id.replace( - "-metagenome", f"-{kmer_size}mers-{norm_method}-embeddings" - ), - index_col="contig", - ) - for kmer_size, norm_method in itertools.product(kmer_sizes, norm_methods) - ] - - @property - def marker_symbols(self) -> AnnotationTable: - return AnnotationTable( - id=self.markers.id.replace("-markers", "-marker-symbols") - ) - - # embeddings: Union[AsyncResult,str] - - # validators - # @validator('*') - # def table_must_exist_in_database(cls, v): - # if not engine.has_table(v): - # raise NotInDatabaseError(table_name=v) - # return v - - # Generic Class Methods - # e.g. tables.binning.get_table() - # or tables.binning.exists() - # or tables.binning.tasks() - # etc. - # Need generic methods (listed above) to apply from namespace - - class Config: - arbitrary_types_allowed = True - smart_union = True - - -MarkerAnnotations = create_model( - "MarkerAnnotations", - apple="russet", - banana="yellow", - __base__=MetagenomeAnnotations, -) - - -class TaxonomyAnnotations(MetagenomeAnnotations): - taxid: int - superkingdom: Optional[str] - phylum: Optional[str] - klass: Optional[str] - order: Optional[str] - family: Optional[str] - genus: Optional[str] - species: Optional[str] - - -class CoverageAnnotations(MetagenomeAnnotations): - coverage: float - - -class KmerAnnotations(MetagenomeAnnotations): - x_1: float - x_2: float - kmer_size: Optional[int] - norm_method: Optional[str] - embed_method: Optional[str] - - -class Kmer(BaseModel): - value: RestrictedAlphabetStr = Field(alphabet="ATCG") - - -class KmerCounts(MetagenomeAnnotations): - # kmers: Field(..., regex = 'ATCG') - kmers: List[Kmer] - - -class BinningAnnotations(MetagenomeAnnotations): - cluster: str - completeness: Optional[float] - purity: Optional[float] - gc_stddev: Optional[float] - coverage_stddev: Optional[float] - - -class MetagenomeModel(BaseModel): - # Json - contig: str - gc_content: Optional[float] - length: Optional[int] - markers: Optional[MarkerAnnotations] - kmers: Optional[KmerAnnotations] - binning: Optional[BinningAnnotations] - coverage: Optional[CoverageAnnotations] - taxonomy: Optional[TaxonomyAnnotations] diff --git a/automappa/utils/serializers.py b/automappa/utils/serializers.py deleted file mode 100644 index 46ef0ce6..00000000 --- a/automappa/utils/serializers.py +++ /dev/null @@ -1,361 +0,0 @@ -#!/usr/bin/env python - -from datetime import datetime -import logging -from pathlib import Path -from typing import List, Optional -import uuid -import pandas as pd - -from Bio.SeqIO import SeqRecord -from Bio.Seq import Seq - -from autometa.common.markers import load as load_markers -from autometa.common.utilities import calc_checksum -from autometa.common.metagenome import Metagenome - -from automappa.db import engine, metadata -from automappa.settings import server - -logging.basicConfig( - format="[%(levelname)s] %(name)s: %(message)s", - level=logging.DEBUG, -) - -logger = logging.getLogger(__name__) - - -def get_uploaded_datatables() -> List[str]: - return list(metadata.tables.keys()) - - -def get_uploaded_files_table() -> pd.DataFrame: - df = pd.DataFrame() - tables = [table for table in metadata.tables.keys() if "fileupload" in table] - with engine.connect() as conn: - if tables: - df = pd.DataFrame([pd.read_sql(table, conn) for table in tables]) - return df - - -def get_table(table_name: str, index_col: Optional[str] = None) -> pd.DataFrame: - if not engine.has_table(table_name): - tables = metadata.tables.keys() - raise ValueError(f"{table_name} not in database! available: {tables}") - df = pd.read_sql(table_name, engine) - if index_col: - df = df.set_index(index_col) - logger.debug(f"retrieved {table_name} datatable, shape: {df.shape}") - return df - - -def convert_bytes(size: int, unit: str = "MB", ndigits: int = 2) -> float: - """Convert bytes from os.path.getsize(...) to provided `unit` - choices include: 'KB', 'MB' or 'GB' - - Parameters - ---------- - size : int - bytes returned from `os.path.getsize(...)` - unit : str, optional - size to convert from bytes, by default MB - - Returns - ------- - float - Converted bytes value - """ - # Yoinked from - # https://amiradata.com/python-get-file-size-in-kb-mb-or-gb/#Get_file_size_in_KiloBytes_MegaBytes_or_GigaBytes - if unit == "KB": - return round(size / 1024, ndigits) - elif unit == "MB": - return round(size / (1024 * 1024), ndigits) - elif unit == "GB": - return round(size / (1024 * 1024 * 1024), ndigits) - else: - return size - - -def store_binning_main(filepath: Path, if_exists: str = "replace") -> str: - """Parse `filepath` into `pd.DataFrame` then save to postgres table - - `table_id` is composed of 2 pieces: - - 1. md5 checksum of 'filepath' - 2. binning - - e.g. `'{checksum}-binning'` - - Parameters - ---------- - filepath : Path - Path to binning.main.tsv Autometa results - if_exists : str - {'fail', 'replace', 'append'}, by default 'replace' - How to behave if the table already exists. - - Returns - ------- - str - table_id = postgres table id for retrieving stored data (AKA name of SQL table) - e.g. `'{checksum}-binning'` - """ - # Construct table name - checksum = calc_checksum(str(filepath)).split()[0] - table_name = f"{checksum}-binning" - # Read filepath contents - df = pd.read_csv(filepath, sep="\t") - # NOTE: table_name must not exceed maximum length of 63 characters - df.to_sql(table_name, engine, if_exists=if_exists, index=False) - logger.debug(f"Saved {df.shape[0]:,} contigs to postgres table: {table_name}") - # MAG Refinement Data Store - # TODO: Refactor this to move it out of store_binning(...) - # Should be another celery task... - # NOTE: MAG refinement columns are enumerated (1-indexed) and prepended with 'refinement_' - if "cluster" not in df.columns: - df["cluster"] = "unclustered" - else: - df["cluster"].fillna("unclustered", inplace=True) - - refine_cols = [ - col - for col in df.columns - if "refinement_" in col or "cluster" in col or "contig" in col - ] - refinement_table_name = table_name.replace("-binning", "-refinement") - df[refine_cols].to_sql( - refinement_table_name, engine, if_exists=if_exists, index=False - ) - logger.debug( - f"Saved refinements (shape={df[refine_cols].shape}) to postgres table: {refinement_table_name}" - ) - return table_name - - -def store_markers(filepath: Path, if_exists: str = "replace") -> str: - """Parse `filepath` into `pd.DataFrame` then save to postgres table - - `table_id` is composed of 2 pieces: - - 1. md5 checksum of 'filepath' - 2. binning - - e.g. `'{checksum}-markers'` - - Parameters - ---------- - filepath : Path - Path to markers.tsv Autometa results - if_exists : str - {'fail', 'replace', 'append'}, by default 'replace' - How to behave if the table already exists. - - Returns - ------- - str - table_id = postgres table id for retrieving stored data (AKA name of SQL table) - e.g. `'{checksum}-markers'` - """ - # Construct table name - checksum = calc_checksum(str(filepath)).split()[0] - table_name = f"{checksum}-markers" - # Read filepath contents - df = load_markers(filepath) - # NOTE: table_name must not exceed maximum length of 63 characters - df.to_sql(table_name, engine, if_exists=if_exists, index=True) - logger.debug( - f"Saved {df.shape[0]:,} contigs and {df.shape[1]:,} markers to postgres table: {table_name}" - ) - return table_name - - -def store_metagenome(filepath: Path, if_exists: str = "replace") -> str: - """Parse `filepath` into `pd.DataFrame` then save to postgres table - - `table_id` is composed of 2 pieces: - - 1. md5 checksum of 'filepath' - 2. metagenome - - e.g. `'{checksum}-metagenome'` - - Parameters - ---------- - filepath : Path - Path to metagenome.main.tsv Autometa results - if_exists : str - {'fail', 'replace', 'append'}, by default 'replace' - How to behave if the table already exists. - - Returns - ------- - str - table_id = postgres table id for retrieving stored data (AKA name of SQL table) - e.g. `'{checksum}-metagenome'` - """ - # Read filepath contents - logger.debug(f"{filepath} uploaded... converting for datatable...") - metagenome = Metagenome(assembly=filepath) - df = pd.DataFrame( - [ - {"contig": seqrecord.id, "sequence": str(seqrecord.seq)} - for seqrecord in metagenome.seqrecords - ] - ) - logger.debug(f"converted... saving to datatable...") - # NOTE: table_name must not exceed maximum length of 63 characters - # Construct table name - checksum = calc_checksum(str(filepath)).split()[0] - table_name = f"{checksum}-metagenome" - df.to_sql(table_name, engine, if_exists=if_exists, index=False) - logger.debug(f"Saved {df.shape[0]:,} contigs to postgres table: {table_name}") - return table_name - - -def get_metagenome_seqrecords(table_name) -> List[SeqRecord]: - df = get_table(table_name) - return [ - SeqRecord(seq=Seq(record[1]), id=record[0], name=record[0]) - for record in df.to_records(index=False) - ] - - -def table_to_db( - df: pd.DataFrame, name: str, if_exists: str = "replace", index: bool = False -) -> None: - """Write `df` to `table_name` in database. - - Parameters - ---------- - df : pd.DataFrame - Dataframe of data to write - name : str - Name of data table to store in database - if_exists : str, optional - What to do if `name` exists. - Choices include: 'fail', 'replace', 'append', by default "replace" - index : bool, optional - Whether to write the index to the database, by default False - - Returns - ------- - NoneType - Nothing is returned... - """ - return df.to_sql(name=name, con=engine, if_exists=if_exists, index=index) - - -def file_to_db( - filepath: Path, - filetype: str, - if_exists: str = "replace", - rm_after_upload: bool = True, -) -> pd.DataFrame: - """Store `filepath` to db table based on `filetype` - - Parameters - ---------- - filepath : Path - Path to uploaded file to be stored in db table - filetype : str - type of file to be stored - choices include 'markers', 'metagenome', 'binning' - - Returns - ------- - pd.DataFrame - cols=[filetype, filename, filesize (MB), table_id, uploaded, timestamp] - - Raises - ------ - ValueError - `filetype` not in filetype store methods - """ - bytes_size = filepath.stat().st_size - filesize = convert_bytes(bytes_size, "MB") - timestamp = filepath.stat().st_mtime - last_modified = datetime.fromtimestamp(timestamp).strftime("%Y-%b-%d, %H:%M:%S") - filetype_store_methods = { - "markers": store_markers, - "metagenome": store_metagenome, - "binning": store_binning_main, - } - if filetype not in filetype_store_methods: - raise ValueError( - f"{filetype} not in filetype store methods {','.join(filetype_store_methods.keys())}" - ) - store_method = filetype_store_methods[filetype] - try: - table_id = store_method(filepath, if_exists=if_exists) - if rm_after_upload: - # TODO: Should have some way to retrieve data if already uploaded to server... - # ...but data ingestion fails for some reason... - logger.debug(f"Removed upload: {filepath.name} from server") - filepath.unlink(missing_ok=True) - except Exception as err: - logger.error(err) - return pd.DataFrame() - df = pd.DataFrame( - [ - { - "filetype": filetype, - "filename": filepath.name, - "filesize (MB)": filesize, - "table_id": table_id, - "uploaded": last_modified, - "timestamp": timestamp, - } - ] - ) - - # Create table_name specific to uploaded file with the upload file metadata... - # This should contain mapping to where the file contents are stored (i.e. table_id) - table_name = f"{uuid.uuid4()}-fileupload" - df.to_sql(table_name, engine, if_exists=if_exists, index=False) - logger.debug(f"Saved {table_name} to db") - return df - - -def validate_uploader(iscompleted: bool, filenames, upload_id) -> Path: - """Ensure only one file was uploaded and create Path to uploaded file - - Parameters - ---------- - iscompleted : bool - Whether or not the upload has finished - filenames : list - list of filenames - upload_id : uuid - unique user id associated with upload - - Returns - ------- - Path - Server-side path to uploaded file - - Raises - ------ - ValueError - You may only upload one file at a time! - """ - if not iscompleted: - return - if filenames is None: - return - if upload_id: - root_folder = server.root_upload_folder / upload_id - else: - root_folder = server.root_upload_folder - - uploaded_files = [] - for filename in filenames: - file = root_folder / filename - uploaded_files.append(file) - if len(uploaded_files) > 1: - raise ValueError("You may only upload one file at a time!") - return uploaded_files[0] - - -if __name__ == "__main__": - pass diff --git a/automappa/utils/tests/test_models.py b/automappa/utils/tests/test_models.py deleted file mode 100644 index 26dd2c07..00000000 --- a/automappa/utils/tests/test_models.py +++ /dev/null @@ -1,26 +0,0 @@ -import pytest - -from automappa.utils.models import SampleTables, AnnotationTable - -# @pytest.fixture() -# def sampletables_fixture(): - - -def get_sampletable(): - return SampleTables( - **{ - "metagenome": {"id": "2a32c68b68deb1b2c5c82871b592d392-metagenome"}, - "binning": {"id": "d92e6e59d3fb4f7d988ed6adf5e71cc4-binning"}, - "refinements": {"id": "d92e6e59d3fb4f7d988ed6adf5e71cc4-refinement"}, - "markers": {"id": "1bba604b9ebb737e20af35c66fa9cff9-markers"}, - } - ) - - -def get_no_binning_sampletable(): - return SampleTables( - **{ - "metagenome": {"id": "2a32c68b68deb1b2c5c82871b592d392-metagenome"}, - "markers": {"id": "1bba604b9ebb737e20af35c66fa9cff9-markers"}, - } - ) diff --git a/docker-compose.yml b/docker-compose.yml index 2a609a86..c899edff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,62 +1,88 @@ version: '3.8' -services: +services: - postgres: image: postgres restart: always ports: - - 5432:5432 + - "5432:5432" env_file: - .env volumes: - postgres-data:/var/lib/postgresql/data + redis: + image: redis:latest + ports: + - "6379:6379" + rabbitmq: image: rabbitmq:3.10.1-management-alpine restart: always ports: # AMQP protocol port - - 5672:5672 + - "5672:5672" # HTTP management UI - - 15672:15672 + - "15672:15672" env_file: - .env volumes: - ./automappa/conf/rabbitmq.conf:/etc/rabbitmq/rabbitmq.conf - - celery: + + queue: build: context: . dockerfile: Dockerfile restart: always - command: celery --app=automappa.tasks.queue worker --loglevel INFO --uid automappa -E + command: celery --app=automappa.tasks.queue worker --loglevel=INFO -E + user: automappa + env_file: + - .env volumes: - .:/usr/src/app depends_on: - redis - rabbitmq + flower: build: context: . dockerfile: Dockerfile restart: always command: celery --app=automappa.tasks.queue flower --port=5555 + user: automappa volumes: - .:/usr/src/app ports: - - 5555:5555 + - "5555:5555" env_file: - .env depends_on: - - celery + - queue - rabbitmq + web: + build: + context: . + dockerfile: Dockerfile + restart: always + mem_limit: 4GB + command: python -m automappa.__main__ + user: automappa + volumes: + # /usr/src/app is location of install in Dockerfile + - .:/usr/src/app + ports: + - "8050:8050" + depends_on: + - postgres + - queue + # prometheus: # image: prom/prometheus:latest # ports: - # - 9090:9090 + # - "9090:9090" # volumes: # - prometheus-data:/prometheus # # - ./prometheus.yml:/etc/prometheus/prometheus.yml @@ -66,7 +92,7 @@ services: # grafana: # image: grafana/grafana:latest # ports: - # - 3000:3000 + # - "3000:3000" # env_file: # - .env # volumes: @@ -77,29 +103,6 @@ services: # depends_on: # - prometheus - redis: - image: "redis:latest" - ports: - - 6379:6379 - web: - build: - context: . - dockerfile: Dockerfile - restart: always - mem_limit: 4GB - # Not sure which is more appropriate for production :shrug: - # The current command allows live-reloading for dev - # command: automappa - command: python -m automappa.index - volumes: - # /usr/src/app is location of install in Dockerfile - - .:/usr/src/app - ports: - - 8050:8050 - depends_on: - - postgres - - celery - volumes: postgres-data: # grafana-storage: diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 00000000..a6973503 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,1238 @@ +# Contributing + +- [Getting started](#getting-started-with-development) + 1. [Clone Automappa](#1-retrieve-repository) + 2. [Start Automappa services](#2-create-services-using-docker-compose) + 3. [Navigate to browser url](#3-navigate-to-the-automappa-page-in-your-browser) + +- [Creating a component](#adding-a-new-component) + + 0. [Before you begin](#0-before-your-begin) + 1. [Create `component-id` in `ids.py`](#1-create-a-unique-component-id) + 2. [Create component file](#2-create-your-componentpy-file) + 3. [Define a `render` function](#3-define-a-render-function) + 4. [Component interactions](#4-defining-component-interactions) + - [The `app` argument](#the-app-argument) + - [The `source` argument](#the-source-argument) + 5. [Add component to the page's layout](#5-import-and-render-your-component-into-the-page-layout) + 6. [Component `Input` to existing components](#6-using-your_component-as-an-input-to-existing-components) + +- [Adding a page](#pages) +- [Services and dependencies](#automappa-services-and-dependencies) + - [Postgres](#postgres) + - [RabbitMQ](#rabbitmq) + - [Celery](#celery-task-queue) + - [Redis](#redis) + - [Flower](#flower) +- [dev resources](#development-resources) + - [component libraries](#libraries) + +## Getting started with development + +### 1. Retrieve repository + +```bash +git clone https://github.com/WiscEvan/Automappa.git +cd Automappa +``` + +### 2. Create services using `docker-compose` + +For convenience, the command may be found in the `Makefile` and the services setup with: + +```bash +make up +``` + +> NOTE: You can see a list of make commands by only tying `make` in the `Automappa` directory. + +This may take a few minutes if all of the images need to be pulled and constructed. + +### 3. Navigate to the Automappa page in your browser + +After all of the images have been created the services will be started in their +respective containers and you should eventually see this in the terminal: + +```console +automappa-web-1 | Dash is running on http://0.0.0.0:8050/ +automappa-web-1 | +automappa-web-1 | [INFO] dash.dash: Dash is running on http://0.0.0.0:8050/ +automappa-web-1 | +automappa-web-1 | * Serving Flask app 'automappa.app' +automappa-web-1 | * Debug mode: on +``` + +## Adding a new component + +### 0. Before your begin + +Checkout a new branch + +```bash +git checkout -b develop +``` + +Change `SERVER_DEBUG = False` in `.env` to `SERVER_DEBUG = True` + +### 1. Create a unique component id + +A unique id is required to specify what data should be set or retrieved based on the component being implemented. + +To do this, simply create a unique id for the component in `automappa/components/ids.py` + +```python +# Example contents of automappa/components/ids.py +COMPONENT_ID = "unique-component-id" +``` + +This should ultimately be imported by the respective component's file (`automappa/pages//components/your_component.py`) like so: + +```python +from automappa.components import ids +# Now you can access 'unique-component-id' with +ids.COMPONENT_ID +``` + +### 2. Create your `component.py` file + +The component should be placed respective to the page where it will be added. + +The name of the file should describe your component and be located in the `components` sub-directory. + +i.e. `automappa/pages//components/your_component.py` + +>NOTE: Try to be clear and concise when naming the component file + +### 3. define a `render` function + +Define a `render` function in `your_component.py`. + +We'll start with: + +```python +# contents of your_component.py +from dash_extensions.enrich import html + +def render() -> html.Div: + ... +``` + +All components follow a standard syntax of a `render` function. + +### 4. Defining component interactions + +#### The `app` argument + +>Prior to this you should read about [Dash basic callbacks]( "Dash callbacks documentation") +> +> and if the component behavior is more complex, you may also find +> the [Dash advanced callbacks docs](https://dash.plotly.com/advanced-callbacks "Dash advanced callbacks documentation") +> helpful. + +To create a reactive component you will need to define a *callback function* +and describe the component's behavior when interacting with other components. + +I'll provide a simple example here for registering callbacks specifically to your +component. + +Remember, all components follow a standard syntax of a `render` function and +this takes as an argument the page's app (or `DashProxy`) as input arguments. + +Here is a simple example with these arguments: + +```python +# contents of your_component.py +from dash_extensions.enrich import html, DashProxy + +def render(app: DashProxy) -> html.Div: + ... +``` + +You'll notice the type hint is a `DashProxy` object. This is especially important +when defining callbacks specific to this component. We will need to register these +callbacks using the same `dash` basic callback syntax so we pass this in as input +to decouple the presenter (the `app`) from the component. This provides us two wonderful features. + +1. We now have much greater flexibility +2. We can avoid having to put `from automappa.app import app` everywhere! + +>This also reduces our chances of running in to problematic circular imports from +>having to pass the app around the entire codebase. + +Let us proceed... + +```python +# contents of your_component.py +from automappa.components import ids +from dash_extensions.enrich import html, DashProxy + +def render(app: DashProxy) -> html.Div: + @app.callback( + Output(ids.YOUR_COMPONENT_ID, "property"), + Input(ids.A_COMPONENT_ID, "property"), + ... + ) + def callback_function(a_component_id_property): + ... +``` + +The `app.callback(...)` can take any number of `Input`s and `Output`s. +Typically we'll only use one `Output` and may have multiple `Inputs` to have our +callback perform a specific task according to the user's interactions. + +>NOTE: There are other keywords that may be supplied to `@app.callback` and you can +find more information on this in the +[Dash basic callbacks docs]( "Dash callbacks documentation") +and [Dash advanced callbacks docs](https://dash.plotly.com/advanced-callbacks "Dash advanced callbacks documentation"). + +The `callback_function` to create a reactive component in the app may have an +arbitrary name but I tend to stick with something related to the type of +reactivity of the component and what property is being updated. + +You will have to check your particular component's documentation to determine +the properties that are available (often referred to as `props`). + +```python +from automappa.components import ids +from dash_extensions.enrich import DashProxy,html,dcc + + +def render(app: DashProxy) -> html.Div: + @app.callback( + Output(ids.COMPONENT_ID, ""), + Input(ids., ""), + ) + def your_component_callback(some_input_component_prop): + ... + return ... + ... + return html.Div(dcc.Component(id=ids.COMPONENT_ID, ...), ...) +``` + +>NOTE: The actual `app` object that will ultimately be passed to this is + a `DashBlueprint` which is a wrapper of `DashProxy` used similarly to flask +blueprint templates. For more details on this see the respective +`automappa/pages//layout.py` file. + +#### The `source` argument + +Up until now we've only discussed the `app: DashProxy` argument in +`def render(...)`, but we still require one more argument--a "data source". +We will use this to access uploaded user data. + +The database setup has been isolated to its own directory under: +`automappa/data/{database,models,schemas,loader}.py` + +The respective data source will use these database objects for the respective page. + +All of the Automappa pages have their own data source. + +For example if we look at the `mag_refinement` page, we will notice three items: + +a `components` directory containing the page's components and two files, `layout.py` and `source.py` + +The components and data source are implemented, then imported in +`layout.py` to be instantiated in the page's layout (i.e. in a `render` function) + +```bash +automappa/pages/mag_refinement +├── __init__.py +├── components +├── layout.py +├── source.py +└── tests + +2 directories, 3 files +``` + +The data source handles all of the component methods used to interact with the database. + +Here we outline our database, models (w/schemas), loading and pre-processing +methods to construct a `DataSource` object that can handle any database operations. +Furthermore datasources may be defined for each page allowing us to avoid one. +"god" class that requires all data operations for all pages and components + +>(What a headache that would be 🤮) + +At startup, Automappa will create multiple sources respective to each page, e.g: a `HomeDataSource`, `RefinementDataSource` and `SummaryDataSource`. + +> (I've left `MAG` from the MAG-summary and MAG-refinement page +>names b/c I don't think they look so nice in CamelCase) + +Instead of running in to a similar problem to `from automappa.app import app` we +can decouple our data source from our components by *dependency inversion*. + +We do this by defining our component's data source *protocol* to ensure we have +all of the methods available in our data source that are required by the component. + +For a simple example, let's say our component needs to retrieve the number of contigs +within a GC% range. We can define the data source protocol with the necessary method +to use in our component. + +We will need to add a `ComponentDataSource` protocol to access the user's `Contig` data. + +>NOTE: For more information on the `Contig` model see: `automappa/data/models.py` + +```python +# contents of automappa/pages/mag_refinement/components/your_component.py +from typing import Protocol,Tuple + +class ComponentDataSource(Protocol): + def get_contig_count_in_gc_content_range(self, min_max_values: Tuple[float,float]) -> int: + ... +``` + +This allows the python typing system to detect when this method is available while +the provided data source is being used. Notice the arguments and the returned +object are typehinted to tell the editor the protocol behavior. + +Now let's see this in action with a fake example broken into bitesize pieces: + +```python +# contents of automappa/pages/mag_refinement/components/your_component.py +from dash_extenesions.enrich import DashProxy,html +from typing import Protocol,Tuple + +class ComponentDataSource(Protocol): + def get_contig_count_in_gc_content_range(self, min_max_values: Tuple[float,float]) -> int: + ... + +def render(app: DashProxy, source: ComponentDataSource) -> html.Div: + # More code below +``` + +Here we provide our source type using our protocol: `source: ComponentDataSource` and this allows us +to continue on with the component's implementation without any data source imports! 🤯 + +Let's continue with using the `source` in a callback... + +> NOTE: I've also added typehints to the callback function below (this was omitted in +>previous examples for simplicity, but should always be done 🙈). + +```python +# contents of automappa/pages/mag_refinement/components/your_component.py +from dash_extenesions.enrich import DashProxy,html +import dash_mantine_components as dmc +from typing import Protocol,Tuple + +class ComponentDataSource(Protocol): + def get_contig_count_in_gc_content_range(self, min_max_values: Tuple[float,float]) -> int: + ... + +def render(app: DashProxy, source: ComponentDataSource) -> html.Div: + @app.callback( + Output(ids.COMPONENT_ID, "children"), + Input(ids.GC_CONTENT_RANGE_SLIDER, "value"), + ) + def show_contig_count_in_range(gc_content_range: Tuple[float,float]) -> dmc.Text: + # we use our data source object here to get our count in the component + contig_count = source.get_contig_count_in_gc_content_range( + min_max_values=gc_content_range + ) + return dmc.Text(f"{contig_count=}") +``` + +Notice we can use our `source.get_contig_count_in_gc_content_range` method +(Possibly without it yet being implemented) in the callback behavior. Of course, +once all of this is in place, you will need to make sure the actual passed +`source` object has this method implemented. + +>NOTE: You should be able to find the passed `source` object's type by looking +at the respective layout file: +> +>i.e. `automappa/pages/mag_refinement/components/your_component.py` is placed in +>`automappa/pages/mag_refinement/layout.py` which gets passed a `RefinementDataSource` +>in `automappa/components/layout.py` + +#### The data source graph + +The passing of the data source objects may be visualized in the following graph + +Here is an overview of all of the data sources + +>NOTE: The square rectangles contain the implemented database operations +> while the rounded rectangles depict protocol classes to define component behavior. + +```mermaid +graph TD; + subgraph MAG-summary Page + A[SummaryDataSource]-->B(SummaryLayoutDataSource); + B(SummaryLayoutDataSource)-->C(CoverageOverviewBoxplotDataSource); + B(SummaryLayoutDataSource)-->D(GcPercentOverviewBoxplotDataSource); + B(SummaryLayoutDataSource)-->E(LengthOverviewBoxplotDataSource); + B(SummaryLayoutDataSource)-->F(ClusterMetricsBoxplotDataSource); + B(SummaryLayoutDataSource)-->G(ClusterDropdownDataSource); + B(SummaryLayoutDataSource)-->H(ClusterStatsTableDataSource); + B(SummaryLayoutDataSource)-->I(ClusterTaxonomyDistributionDataSource); + B(SummaryLayoutDataSource)-->J(ClusterMetricsBarplotDataSource); + end + subgraph MAG-refinement Page + AA[RefinementDataSource]-->BB(RefinementLayoutDataSource); + BB(RefinementLayoutDataSource)-->CC(Scatterplot2dDataSource); + BB(RefinementLayoutDataSource)-->DD(SettingsOffcanvasDataSource); + BB(RefinementLayoutDataSource)-->EE(MagMetricsDataSource); + BB(RefinementLayoutDataSource)-->FF(TaxonomyDistributionDataSource); + BB(RefinementLayoutDataSource)-->GG(Scatterplot3dDataSource); + BB(RefinementLayoutDataSource)-->HH(CoverageBoxplotDataSource); + BB(RefinementLayoutDataSource)-->II(GcPercentBoxplotDataSource); + BB(RefinementLayoutDataSource)-->JJ(LengthBoxplotDataSource); + BB(RefinementLayoutDataSource)-->KK(ContigCytoscapeDataSource); + BB(RefinementLayoutDataSource)-->LL(CoverageRangeSliderDataSource); + end + subgraph Home Page + AAA[HomeDataSource]-->BBB(HomeLayoutDataSource); + BBB(HomeLayoutDataSource)-->CCC(UploadModalButtonDataSource); + BBB(HomeLayoutDataSource)-->DDD(SampleCardsDataSource); + end +``` + +
+ +Home page data source graph + +```mermaid +graph TD; + A[HomeDataSource]-->B(HomeLayoutDataSource); + B(HomeLayoutDataSource)-->C(UploadModalButtonDataSource); + B(HomeLayoutDataSource)-->D(SampleCardsDataSource); +``` + +
+ +
+ +MAG-refinement data source graph + +```mermaid +graph TD; + A[RefinementDataSource]-->B(RefinementLayoutDataSource); + B(RefinementLayoutDataSource)-->C(Scatterplot2dDataSource); + B(RefinementLayoutDataSource)-->D(SettingsOffcanvasDataSource); + B(RefinementLayoutDataSource)-->E(MagMetricsDataSource); + B(RefinementLayoutDataSource)-->F(TaxonomyDistributionDataSource); + B(RefinementLayoutDataSource)-->G(Scatterplot3dDataSource); + B(RefinementLayoutDataSource)-->H(CoverageBoxplotDataSource); + B(RefinementLayoutDataSource)-->I(GcPercentBoxplotDataSource); + B(RefinementLayoutDataSource)-->J(LengthBoxplotDataSource); + B(RefinementLayoutDataSource)-->K(ContigCytoscapeDataSource); + B(RefinementLayoutDataSource)-->L(CoverageRangeSliderDataSource); +``` + +
+ +
+ +MAG-summary data source graph + +```mermaid +graph TD; + A[SummaryDataSource]-->B(SummaryLayoutDataSource); + B(SummaryLayoutDataSource)-->C(CoverageOverviewBoxplotDataSource); + B(SummaryLayoutDataSource)-->D(GcPercentOverviewBoxplotDataSource); + B(SummaryLayoutDataSource)-->E(LengthOverviewBoxplotDataSource); + B(SummaryLayoutDataSource)-->F(ClusterMetricsBoxplotDataSource); + B(SummaryLayoutDataSource)-->G(ClusterDropdownDataSource); + B(SummaryLayoutDataSource)-->H(ClusterStatsTableDataSource); + B(SummaryLayoutDataSource)-->I(ClusterTaxonomyDistributionDataSource); + B(SummaryLayoutDataSource)-->J(ClusterMetricsBarplotDataSource); +``` + +
+ +A placeholder definition may be made within the passed `source` objects +class while working out the component's behavior and interactions. +This makes the required method easier to find when returning to the +data handling aspect of the implementation. + +For example, we know we want a `get_contig_count_in_gc_content_range` method and because +we are placing our component in the MAG-refinement layout can we define this in +`RefinementDataSource`. + +>i.e. `RefinementDataSource` is the object that will ultimately +>be passed to `your_component.render(app, source)`. + +```python +# contents of automappa/pages/mag_refinement/source.py +class RefinementDataSource(BaseModel): + def get_contig_count_in_gc_content_range(self, min_max_values: Tuple[float,float]) -> int: + raise NotImplemented +``` + +Let's say we've finalized our component behavior and aesthetics. + +Here's the final fake example component in its entirety: + +```python +# contents of automappa/pages/mag_refinement/components/your_component.py +from dash_extenesions.enrich import DashProxy,html +import dash_mantine_components as dmc +from typing import Protocol,Tuple + +class ComponentDataSource(Protocol): + def get_contig_count_in_gc_content_range(self, min_max_values: Tuple[float,float]) -> int: + ... + +def render(app: DashProxy, source: ComponentDataSource) -> html.Div: + @app.callback( + Output(ids.COMPONENT_ID, "children"), + Input(ids.GC_CONTENT_RANGE_SLIDER, "value"), + ) + def show_contig_count_in_range(gc_content_range: Tuple[float,float]) -> dmc.Text: + contig_count = source.get_contig_count_in_gc_content_range( + min_max_values=gc_content_range + ) + return dmc.Text(f"{contig_count=}") + + return html.Div(ids.COMPONENT_ID, + children = [ + """ + Interact with the GC% slider to see how many contigs are within the + selected range. + """ + ] + ) +``` + +We can now return to the data source and should not need to make any further changes with the +component: + +Returning to our data source implementation... + +```python +# contents of automappa/pages/mag_refinement/source.py +class RefinementDataSource(BaseModel): + def get_contig_count_in_gc_content_range(self, min_max_values: Tuple[float,float]) -> int: + raise NotImplemented +``` + +...we implement the required database query operation + +```python +# contents of automappa/pages/mag_refinement/source.py +class RefinementDataSource(BaseModel): + def get_contig_count_in_gc_content_range(self, min_max_values: Tuple[float,float]) -> int: + min_gc_content, max_gc_content = min_max_values + with Session(engine) as session: + statement = ( + select(Contig) # Here we use the Contig model defined in models.py + .where(Contig.gc_content >= min_gc_content, Contig.gc_content <= max_gc_content) + ) + results = session.exec(statement).all() + return len(results) +``` + +### 5. Import and render your component into the page layout + +At this point, the majority of the work has been done, now all that is left is to simply place the component +into the layout of the page. This should correspond to the same page for which the component is implemented + +- `automappa/pages//layout.py` + +Following the example above, this would be `automappa/pages/mag_refinement/layout.py` + +```python +# contents of automappa/pages/mag_refinement/layout.py +from dash_extenesions.enrich import DashProxy,html +from automappa.pages.mag_refinement.components import your_component # importing your component +from typing import Protocol + +class RefinementLayoutDataSource(Protocol): + ... + def get_contig_count_in_gc_content_range(self, min_max_values: Tuple[float,float]) -> int: + ... + +def render(app: DashProxy, source: RefinementLayoutDataSource) -> html.Div: + # ... code above this + + # Including the component within the page layout + app.layout = html.Div(your_component.render(app, source)) + + return app +``` + +### 6. Using `your_component` as an input to existing components + +To retrieve information from components while interacting with the application +`dash` uses the `@app.callback(Output, Input)` syntax as we have seen. + +>NOTE: There are other keywords that may be supplied to `@app.callback` and you can +find more information on this in the [Dash basic callbacks docs](< "Dash callbacks documentation") and [Dash advanced callbacks docs](https://dash.plotly.com/advanced-callbacks "Dash advanced callbacks documentation"). + +###### [Back to top](#contributing) + +## Pages + +> If you are not adding a page to Automappa but simply a component to an existing page, you may skip this section. + +Automappa uses a dash feature called [`pages`](https://dash.plotly.com/urls "dash pages documentation") to allow multi-page navigation +*without* having to explicitly define callbacks for rendering each page (more on this later). + +Similarly, many useful dash utilities are also available in a package called +[`dash-extensions`](https://www.dash-extensions.com/ "dash extensions documentation") +which has been used throughout. Unfortunately these packages are not completely synchronized, +so the simple approach as described in the dash documentation may not be taken. However, some workarounds +are described in the [dash-extensions docs](https://www.dash-extensions.com/getting_started/enrich). + +###### [Back to top](#contributing) + +## Automappa services and dependencies + +Currently Automappa utilizes multiple services to manage its backend database, +task-queue and monitoring. You may find additional details on these services with +their respective docker image builds, Dockerfiles, commands, dependencies and their +ports in the `docker-compose.yml` file. + +These services are: + +| Service | Purpose | Description | +| -: | :- | :- | +| `postgres` | sample DB | backend database to store/retrieve user uploaded data | +| `rabbitmq` | task-queue broker | task-queue broker for managing worker tasks | +| `celery` | task-queue worker | task-queue worker | +| `redis` | task-queue DB | task-queue broker & worker backend for passing tasks to/from the task-queue | +| `flower` | task-queue monitor | for monitoring `celery` and `rabbitmq` task-queue | +| `web` | App | The automappa web instance | +| \(Not currently in use\) `prometheus` | service monitoring | service monitoring dashboard | +| \(Not currently in use\) `grafana` | service monitoring | service monitoring dashboard | + +Customization of the urls to these services may be +performed by editing the `.env` files as many of +these settings are configured from here. + +###### [Back to top](#contributing) + +### Postgres + +
+ +What to see at startup + +```console +The files belonging to this database system will be owned by user "postgres". +This user must also own the server process. +The database cluster will be initialized with locale "en_US.utf8". +The default database encoding has accordingly been set to "UTF8". +The default text search configuration will be set to "english". +Data page checksums are disabled. +fixing permissions on existing directory /var/lib/postgresql/data ... ok +creating subdirectories ... ok +selecting dynamic shared memory implementation ... posix +selecting default max_connections ... 100 +selecting default shared_buffers ... 128MB +selecting default time zone ... Etc/UTC +creating configuration files ... ok +running bootstrap script ... ok +performing post-bootstrap initialization ... ok +syncing data to disk ... ok +Success. You can now start the database server using: + pg_ctl -D /var/lib/postgresql/data -l logfile start +initdb: warning: enabling "trust" authentication for local connections +initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb. +waiting for server to start....2023-06-30 16:28:09.344 UTC [49] LOG: starting PostgreSQL 15.3 (Debian 15.3-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit +2023-06-30 16:28:09.348 UTC [49] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" +2023-06-30 16:28:09.404 UTC [52] LOG: database system was shut down at 2023-06-30 16:28:08 UTC +2023-06-30 16:28:09.436 UTC [49] LOG: database system is ready to accept connections + done +server started +CREATE DATABASE +/usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/* +waiting for server to shut down....2023-06-30 16:28:09.728 UTC [49] LOG: received fast shutdown request +2023-06-30 16:28:09.737 UTC [49] LOG: aborting any active transactions +2023-06-30 16:28:09.742 UTC [49] LOG: background worker "logical replication launcher" (PID 55) exited with exit code 1 +2023-06-30 16:28:09.742 UTC [50] LOG: shutting down +2023-06-30 16:28:09.744 UTC [50] LOG: checkpoint starting: shutdown immediate +2023-06-30 16:28:09.845 UTC [50] LOG: checkpoint complete: wrote 918 buffers (5.6%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.044 s, sync=0.048 s, total=0.103 s; sync files=250, longest=0.017 s, average=0.001 s; distance=4217 kB, estimate=4217 kB +2023-06-30 16:28:09.857 UTC [49] LOG: database system is shut down + done +server stopped +PostgreSQL init process complete; ready for start up. +2023-06-30 16:28:10.158 UTC [1] LOG: starting PostgreSQL 15.3 (Debian 15.3-1.pgdg110+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 10.2.1-6) 10.2.1 20210110, 64-bit +2023-06-30 16:28:10.159 UTC [1] LOG: listening on IPv4 address "0.0.0.0", port 5432 +2023-06-30 16:28:10.159 UTC [1] LOG: listening on IPv6 address "::", port 5432 +2023-06-30 16:28:10.234 UTC [1] LOG: listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432" +2023-06-30 16:28:10.264 UTC [65] LOG: database system was shut down at 2023-06-30 16:28:09 UTC +2023-06-30 16:28:10.317 UTC [1] LOG: database system is ready to accept connections +``` + +
+ +###### [Back to top](#contributing) + +### RabbitMQ + +
+ +What to see at startup + +```console +2023-06-30 17:34:00.261066+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:00.283893+00:00 [info] <0.221.0> Feature flags: [ ] classic_mirrored_queue_version +2023-06-30 17:34:00.284137+00:00 [info] <0.221.0> Feature flags: [ ] implicit_default_bindings +2023-06-30 17:34:00.284185+00:00 [info] <0.221.0> Feature flags: [ ] maintenance_mode_status +2023-06-30 17:34:00.284446+00:00 [info] <0.221.0> Feature flags: [ ] quorum_queue +2023-06-30 17:34:00.284501+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:00.284579+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:00.284604+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:00.284852+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:01.106535+00:00 [noti] <0.44.0> Application syslog exited with reason: stopped +2023-06-30 17:34:01.106765+00:00 [noti] <0.221.0> Logging: switching to configured handler(s); following messages may not be visible in this log output +2023-06-30 17:34:01.148394+00:00 [notice] <0.221.0> Logging: configured log handlers are now ACTIVE +2023-06-30 17:34:03.546573+00:00 [info] <0.221.0> ra: starting system quorum_queues +2023-06-30 17:34:03.546895+00:00 [info] <0.221.0> starting Ra system: quorum_queues in directory: /var/lib/rabbitmq/mnesia/rabbit@2b6eb27dd567/quorum/rabbit@2b6eb27dd567 +2023-06-30 17:34:03.748480+00:00 [info] <0.259.0> ra system 'quorum_queues' running pre init for 0 registered servers +2023-06-30 17:34:03.805062+00:00 [info] <0.260.0> ra: meta data store initialised for system quorum_queues. 0 record(s) recovered +2023-06-30 17:34:03.831474+00:00 [notice] <0.265.0> WAL: ra_log_wal init, open tbls: ra_log_open_mem_tables, closed tbls: ra_log_closed_mem_tables +2023-06-30 17:34:03.905788+00:00 [info] <0.221.0> ra: starting system coordination +2023-06-30 17:34:03.905986+00:00 [info] <0.221.0> starting Ra system: coordination in directory: /var/lib/rabbitmq/mnesia/rabbit@2b6eb27dd567/coordination/rabbit@2b6eb27dd567 +2023-06-30 17:34:03.964700+00:00 [info] <0.272.0> ra system 'coordination' running pre init for 0 registered servers +2023-06-30 17:34:03.985701+00:00 [info] <0.273.0> ra: meta data store initialised for system coordination. 0 record(s) recovered +2023-06-30 17:34:03.986268+00:00 [notice] <0.278.0> WAL: ra_coordination_log_wal init, open tbls: ra_coordination_log_open_mem_tables, closed tbls: ra_coordination_log_closed_mem_tables +2023-06-30 17:34:03.995172+00:00 [info] <0.221.0> +2023-06-30 17:34:03.995172+00:00 [info] <0.221.0> Starting RabbitMQ 3.10.1 on Erlang 24.3.4 [jit] +2023-06-30 17:34:03.995172+00:00 [info] <0.221.0> Copyright (c) 2007-2022 VMware, Inc. or its affiliates. +2023-06-30 17:34:03.995172+00:00 [info] <0.221.0> Licensed under the MPL 2.0. Website: https://rabbitmq.com + ## ## RabbitMQ 3.10.1 + ## ## + ########## Copyright (c) 2007-2022 VMware, Inc. or its affiliates. + ###### ## + ########## Licensed under the MPL 2.0. Website: https://rabbitmq.com + Erlang: 24.3.4 [jit] + TLS Library: OpenSSL - OpenSSL 1.1.1o 3 May 2022 + Doc guides: https://rabbitmq.com/documentation.html + Support: https://rabbitmq.com/contact.html + Tutorials: https://rabbitmq.com/getstarted.html + Monitoring: https://rabbitmq.com/monitoring.html + Logs: /var/log/rabbitmq/rabbit@2b6eb27dd567_upgrade.log + + Config file(s): /etc/rabbitmq/rabbitmq.conf + /etc/rabbitmq/conf.d/10-defaults.conf + Starting broker...2023-06-30 17:34:04.002324+00:00 [info] <0.221.0> +2023-06-30 17:34:04.002324+00:00 [info] <0.221.0> node : rabbit@2b6eb27dd567 +2023-06-30 17:34:04.002324+00:00 [info] <0.221.0> home dir : /var/lib/rabbitmq +2023-06-30 17:34:04.002324+00:00 [info] <0.221.0> config file(s) : /etc/rabbitmq/rabbitmq.conf +2023-06-30 17:34:04.002324+00:00 [info] <0.221.0> : /etc/rabbitmq/conf.d/10-defaults.conf +2023-06-30 17:34:04.002324+00:00 [info] <0.221.0> cookie hash : 8HBUegOS1ZYW39ARQeqjQw== +2023-06-30 17:34:04.002324+00:00 [info] <0.221.0> log(s) : /var/log/rabbitmq/rabbit@2b6eb27dd567_upgrade.log +2023-06-30 17:34:04.002324+00:00 [info] <0.221.0> : +2023-06-30 17:34:04.002324+00:00 [info] <0.221.0> database dir : /var/lib/rabbitmq/mnesia/rabbit@2b6eb27dd567 +2023-06-30 17:34:05.276918+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:05.277003+00:00 [info] <0.221.0> Feature flags: [ ] classic_mirrored_queue_version +2023-06-30 17:34:05.277036+00:00 [info] <0.221.0> Feature flags: [ ] drop_unroutable_metric +2023-06-30 17:34:05.277062+00:00 [info] <0.221.0> Feature flags: [ ] empty_basic_get_metric +2023-06-30 17:34:05.277174+00:00 [info] <0.221.0> Feature flags: [ ] implicit_default_bindings +2023-06-30 17:34:05.277205+00:00 [info] <0.221.0> Feature flags: [ ] maintenance_mode_status +2023-06-30 17:34:05.277256+00:00 [info] <0.221.0> Feature flags: [ ] quorum_queue +2023-06-30 17:34:05.277362+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:05.277392+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:05.277414+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:05.277436+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:15.876746+00:00 [info] <0.221.0> Running boot step pre_boot defined by app rabbit +2023-06-30 17:34:15.876861+00:00 [info] <0.221.0> Running boot step rabbit_global_counters defined by app rabbit +2023-06-30 17:34:15.877406+00:00 [info] <0.221.0> Running boot step rabbit_osiris_metrics defined by app rabbit +2023-06-30 17:34:15.878840+00:00 [info] <0.221.0> Running boot step rabbit_core_metrics defined by app rabbit +2023-06-30 17:34:15.884054+00:00 [info] <0.221.0> Running boot step rabbit_alarm defined by app rabbit +2023-06-30 17:34:15.903243+00:00 [info] <0.293.0> Memory high watermark set to 3183 MiB (3338434969 bytes) of 7959 MiB (8346087424 bytes) total +2023-06-30 17:34:15.925580+00:00 [info] <0.295.0> Enabling free disk space monitoring +2023-06-30 17:34:15.926199+00:00 [info] <0.295.0> Disk free limit set to 50MB +2023-06-30 17:34:15.939181+00:00 [info] <0.221.0> Running boot step code_server_cache defined by app rabbit +2023-06-30 17:34:15.939630+00:00 [info] <0.221.0> Running boot step file_handle_cache defined by app rabbit +2023-06-30 17:34:15.940030+00:00 [info] <0.300.0> Limiting to approx 1048479 file handles (943629 sockets) +2023-06-30 17:34:15.940233+00:00 [info] <0.301.0> FHC read buffering: OFF +2023-06-30 17:34:15.940415+00:00 [info] <0.301.0> FHC write buffering: ON +2023-06-30 17:34:15.951372+00:00 [info] <0.221.0> Running boot step worker_pool defined by app rabbit +2023-06-30 17:34:15.963058+00:00 [info] <0.280.0> Will use 4 processes for default worker pool +2023-06-30 17:34:15.963293+00:00 [info] <0.280.0> Starting worker pool 'worker_pool' with 4 processes in it +2023-06-30 17:34:15.967054+00:00 [info] <0.221.0> Running boot step database defined by app rabbit +2023-06-30 17:34:15.969249+00:00 [info] <0.221.0> Node database directory at /var/lib/rabbitmq/mnesia/rabbit@2b6eb27dd567 is empty. Assuming we need to join an existing cluster or initialise from scratch... +2023-06-30 17:34:15.969382+00:00 [info] <0.221.0> Configured peer discovery backend: rabbit_peer_discovery_classic_config +2023-06-30 17:34:15.969952+00:00 [info] <0.221.0> Will try to lock with peer discovery backend rabbit_peer_discovery_classic_config +2023-06-30 17:34:15.970220+00:00 [info] <0.221.0> All discovered existing cluster peers: +2023-06-30 17:34:15.970294+00:00 [info] <0.221.0> Discovered no peer nodes to cluster with. Some discovery backends can filter nodes out based on a readiness criteria. Enabling debug logging might help troubleshoot. +2023-06-30 17:34:15.987668+00:00 [notice] <0.44.0> Application mnesia exited with reason: stopped +2023-06-30 17:34:17.120283+00:00 [info] <0.221.0> Waiting for Mnesia tables for 30000 ms, 9 retries left +2023-06-30 17:34:17.120457+00:00 [info] <0.221.0> Successfully synced tables from a peer +2023-06-30 17:34:17.227311+00:00 [info] <0.221.0> Waiting for Mnesia tables for 30000 ms, 9 retries left +2023-06-30 17:34:17.228954+00:00 [info] <0.221.0> Successfully synced tables from a peer +2023-06-30 17:34:17.229093+00:00 [info] <0.221.0> Feature flag `classic_mirrored_queue_version`: supported, attempt to enable... +2023-06-30 17:34:17.229312+00:00 [info] <0.221.0> Feature flag `classic_mirrored_queue_version`: mark as enabled=state_changing +2023-06-30 17:34:17.247984+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:17.248114+00:00 [info] <0.221.0> Feature flags: [~] classic_mirrored_queue_version +2023-06-30 17:34:17.248175+00:00 [info] <0.221.0> Feature flags: [ ] drop_unroutable_metric +2023-06-30 17:34:17.248316+00:00 [info] <0.221.0> Feature flags: [ ] empty_basic_get_metric +2023-06-30 17:34:17.248362+00:00 [info] <0.221.0> Feature flags: [ ] implicit_default_bindings +2023-06-30 17:34:17.248402+00:00 [info] <0.221.0> Feature flags: [ ] maintenance_mode_status +2023-06-30 17:34:17.249184+00:00 [info] <0.221.0> Feature flags: [ ] quorum_queue +2023-06-30 17:34:17.249280+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:17.249326+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:17.249362+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:17.249757+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:17.366149+00:00 [info] <0.221.0> Feature flag `classic_mirrored_queue_version`: mark as enabled=true +2023-06-30 17:34:17.371476+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:17.372173+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:17.373462+00:00 [info] <0.221.0> Feature flags: [ ] drop_unroutable_metric +2023-06-30 17:34:17.373578+00:00 [info] <0.221.0> Feature flags: [ ] empty_basic_get_metric +2023-06-30 17:34:17.373652+00:00 [info] <0.221.0> Feature flags: [ ] implicit_default_bindings +2023-06-30 17:34:17.376918+00:00 [info] <0.221.0> Feature flags: [ ] maintenance_mode_status +2023-06-30 17:34:17.377137+00:00 [info] <0.221.0> Feature flags: [ ] quorum_queue +2023-06-30 17:34:17.377231+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:17.377740+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:17.377890+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:17.378438+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:17.467765+00:00 [info] <0.221.0> Feature flag `drop_unroutable_metric`: supported, attempt to enable... +2023-06-30 17:34:17.467873+00:00 [info] <0.221.0> Feature flag `drop_unroutable_metric`: mark as enabled=state_changing +2023-06-30 17:34:17.470253+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:17.470443+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:17.470530+00:00 [info] <0.221.0> Feature flags: [~] drop_unroutable_metric +2023-06-30 17:34:17.470602+00:00 [info] <0.221.0> Feature flags: [ ] empty_basic_get_metric +2023-06-30 17:34:17.470699+00:00 [info] <0.221.0> Feature flags: [ ] implicit_default_bindings +2023-06-30 17:34:17.470794+00:00 [info] <0.221.0> Feature flags: [ ] maintenance_mode_status +2023-06-30 17:34:17.470853+00:00 [info] <0.221.0> Feature flags: [ ] quorum_queue +2023-06-30 17:34:17.470908+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:17.470969+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:17.471009+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:17.471050+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:17.578161+00:00 [info] <0.221.0> Feature flag `drop_unroutable_metric`: mark as enabled=true +2023-06-30 17:34:17.586106+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:17.586306+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:17.586371+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:17.586414+00:00 [info] <0.221.0> Feature flags: [ ] empty_basic_get_metric +2023-06-30 17:34:17.586466+00:00 [info] <0.221.0> Feature flags: [ ] implicit_default_bindings +2023-06-30 17:34:17.586505+00:00 [info] <0.221.0> Feature flags: [ ] maintenance_mode_status +2023-06-30 17:34:17.586671+00:00 [info] <0.221.0> Feature flags: [ ] quorum_queue +2023-06-30 17:34:17.586878+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:17.586979+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:17.587169+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:17.587332+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:17.611053+00:00 [info] <0.221.0> Feature flag `empty_basic_get_metric`: supported, attempt to enable... +2023-06-30 17:34:17.611249+00:00 [info] <0.221.0> Feature flag `empty_basic_get_metric`: mark as enabled=state_changing +2023-06-30 17:34:17.613076+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:17.613208+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:17.617252+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:17.617363+00:00 [info] <0.221.0> Feature flags: [~] empty_basic_get_metric +2023-06-30 17:34:17.617442+00:00 [info] <0.221.0> Feature flags: [ ] implicit_default_bindings +2023-06-30 17:34:17.617486+00:00 [info] <0.221.0> Feature flags: [ ] maintenance_mode_status +2023-06-30 17:34:17.617529+00:00 [info] <0.221.0> Feature flags: [ ] quorum_queue +2023-06-30 17:34:17.617565+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:17.618385+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:17.618431+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:17.618478+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:17.785959+00:00 [info] <0.221.0> Feature flag `empty_basic_get_metric`: mark as enabled=true +2023-06-30 17:34:17.831807+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:17.831993+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:17.832037+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:17.832065+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:17.832103+00:00 [info] <0.221.0> Feature flags: [ ] implicit_default_bindings +2023-06-30 17:34:17.837501+00:00 [info] <0.221.0> Feature flags: [ ] maintenance_mode_status +2023-06-30 17:34:17.837584+00:00 [info] <0.221.0> Feature flags: [ ] quorum_queue +2023-06-30 17:34:17.838586+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:17.838693+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:17.838740+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:17.838797+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:17.996578+00:00 [info] <0.221.0> Feature flag `implicit_default_bindings`: supported, attempt to enable... +2023-06-30 17:34:17.996729+00:00 [info] <0.221.0> Feature flag `implicit_default_bindings`: mark as enabled=state_changing +2023-06-30 17:34:18.052555+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:18.052958+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:18.053030+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:18.053096+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:18.053138+00:00 [info] <0.221.0> Feature flags: [~] implicit_default_bindings +2023-06-30 17:34:18.053176+00:00 [info] <0.221.0> Feature flags: [ ] maintenance_mode_status +2023-06-30 17:34:18.053286+00:00 [info] <0.221.0> Feature flags: [ ] quorum_queue +2023-06-30 17:34:18.053349+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:18.053421+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:18.053459+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:18.053500+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:18.188674+00:00 [info] <0.221.0> Waiting for Mnesia tables for 30000 ms, 0 retries left +2023-06-30 17:34:18.189301+00:00 [info] <0.221.0> Successfully synced tables from a peer +2023-06-30 17:34:18.189450+00:00 [info] <0.221.0> Feature flag `implicit_default_bindings`: mark as enabled=true +2023-06-30 17:34:18.249730+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:18.249885+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:18.250009+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:18.250158+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:18.250212+00:00 [info] <0.221.0> Feature flags: [x] implicit_default_bindings +2023-06-30 17:34:18.250251+00:00 [info] <0.221.0> Feature flags: [ ] maintenance_mode_status +2023-06-30 17:34:18.250336+00:00 [info] <0.221.0> Feature flags: [ ] quorum_queue +2023-06-30 17:34:18.250380+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:18.250436+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:18.250834+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:18.250971+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:18.312801+00:00 [info] <0.221.0> Feature flag `maintenance_mode_status`: supported, attempt to enable... +2023-06-30 17:34:18.313899+00:00 [info] <0.221.0> Feature flag `maintenance_mode_status`: mark as enabled=state_changing +2023-06-30 17:34:18.328203+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:18.328321+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:18.328428+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:18.328699+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:18.328753+00:00 [info] <0.221.0> Feature flags: [x] implicit_default_bindings +2023-06-30 17:34:18.328799+00:00 [info] <0.221.0> Feature flags: [~] maintenance_mode_status +2023-06-30 17:34:18.328943+00:00 [info] <0.221.0> Feature flags: [ ] quorum_queue +2023-06-30 17:34:18.329027+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:18.329072+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:18.329111+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:18.329176+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:18.413958+00:00 [info] <0.221.0> Creating table rabbit_node_maintenance_states for feature flag `maintenance_mode_status` +2023-06-30 17:34:18.464988+00:00 [info] <0.221.0> Feature flag `maintenance_mode_status`: mark as enabled=true +2023-06-30 17:34:18.488211+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:18.498343+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:18.498424+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:18.498470+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:18.498518+00:00 [info] <0.221.0> Feature flags: [x] implicit_default_bindings +2023-06-30 17:34:18.498561+00:00 [info] <0.221.0> Feature flags: [x] maintenance_mode_status +2023-06-30 17:34:18.514068+00:00 [info] <0.221.0> Feature flags: [ ] quorum_queue +2023-06-30 17:34:18.514310+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:18.514394+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:18.514458+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:18.514617+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:18.679019+00:00 [info] <0.221.0> Feature flag `quorum_queue`: supported, attempt to enable... +2023-06-30 17:34:18.679162+00:00 [info] <0.221.0> Feature flag `quorum_queue`: mark as enabled=state_changing +2023-06-30 17:34:18.681463+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:18.681711+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:18.681775+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:18.681825+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:18.681868+00:00 [info] <0.221.0> Feature flags: [x] implicit_default_bindings +2023-06-30 17:34:18.682082+00:00 [info] <0.221.0> Feature flags: [x] maintenance_mode_status +2023-06-30 17:34:18.762213+00:00 [info] <0.221.0> Feature flags: [~] quorum_queue +2023-06-30 17:34:18.762283+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:18.762357+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:18.762395+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:18.762545+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:18.888555+00:00 [info] <0.221.0> Waiting for Mnesia tables for 30000 ms, 9 retries left +2023-06-30 17:34:18.890845+00:00 [info] <0.221.0> Successfully synced tables from a peer +2023-06-30 17:34:18.890992+00:00 [info] <0.221.0> Feature flag `quorum_queue`: migrating Mnesia table rabbit_queue... +2023-06-30 17:34:19.122993+00:00 [info] <0.221.0> Feature flag `quorum_queue`: migrating Mnesia table rabbit_durable_queue... +2023-06-30 17:34:19.230644+00:00 [info] <0.221.0> Feature flag `quorum_queue`: Mnesia tables migration done +2023-06-30 17:34:19.230874+00:00 [info] <0.221.0> Feature flag `quorum_queue`: mark as enabled=true +2023-06-30 17:34:19.254091+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:19.254198+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:19.254235+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:19.254314+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:19.254344+00:00 [info] <0.221.0> Feature flags: [x] implicit_default_bindings +2023-06-30 17:34:19.254369+00:00 [info] <0.221.0> Feature flags: [x] maintenance_mode_status +2023-06-30 17:34:19.254452+00:00 [info] <0.221.0> Feature flags: [x] quorum_queue +2023-06-30 17:34:19.254758+00:00 [info] <0.221.0> Feature flags: [ ] stream_queue +2023-06-30 17:34:19.254814+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:19.254878+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:19.254922+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:19.358776+00:00 [info] <0.221.0> Feature flag `stream_queue`: supported, attempt to enable... +2023-06-30 17:34:19.358927+00:00 [info] <0.221.0> Feature flag `stream_queue`: mark as enabled=state_changing +2023-06-30 17:34:19.362442+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:19.362990+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:19.363061+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:19.363112+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:19.363175+00:00 [info] <0.221.0> Feature flags: [x] implicit_default_bindings +2023-06-30 17:34:19.363241+00:00 [info] <0.221.0> Feature flags: [x] maintenance_mode_status +2023-06-30 17:34:19.363291+00:00 [info] <0.221.0> Feature flags: [x] quorum_queue +2023-06-30 17:34:19.363337+00:00 [info] <0.221.0> Feature flags: [~] stream_queue +2023-06-30 17:34:19.367963+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:19.368021+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:19.369106+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:19.483130+00:00 [info] <0.221.0> Feature flag `stream_queue`: mark as enabled=true +2023-06-30 17:34:19.587581+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:19.589150+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:19.589213+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:19.589254+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:19.589339+00:00 [info] <0.221.0> Feature flags: [x] implicit_default_bindings +2023-06-30 17:34:19.589381+00:00 [info] <0.221.0> Feature flags: [x] maintenance_mode_status +2023-06-30 17:34:19.589421+00:00 [info] <0.221.0> Feature flags: [x] quorum_queue +2023-06-30 17:34:19.589460+00:00 [info] <0.221.0> Feature flags: [x] stream_queue +2023-06-30 17:34:19.589673+00:00 [info] <0.221.0> Feature flags: [ ] user_limits +2023-06-30 17:34:19.597309+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:19.601754+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:19.681271+00:00 [info] <0.221.0> Feature flag `user_limits`: supported, attempt to enable... +2023-06-30 17:34:19.681467+00:00 [info] <0.221.0> Feature flag `user_limits`: mark as enabled=state_changing +2023-06-30 17:34:19.724505+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:19.724668+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:19.724728+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:19.724803+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:19.724843+00:00 [info] <0.221.0> Feature flags: [x] implicit_default_bindings +2023-06-30 17:34:19.724885+00:00 [info] <0.221.0> Feature flags: [x] maintenance_mode_status +2023-06-30 17:34:19.725115+00:00 [info] <0.221.0> Feature flags: [x] quorum_queue +2023-06-30 17:34:19.728205+00:00 [info] <0.221.0> Feature flags: [x] stream_queue +2023-06-30 17:34:19.734686+00:00 [info] <0.221.0> Feature flags: [~] user_limits +2023-06-30 17:34:19.734834+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:19.734894+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:19.810254+00:00 [info] <0.221.0> Waiting for Mnesia tables for 30000 ms, 9 retries left +2023-06-30 17:34:19.813713+00:00 [info] <0.221.0> Successfully synced tables from a peer +2023-06-30 17:34:19.935551+00:00 [info] <0.221.0> Feature flag `user_limits`: mark as enabled=true +2023-06-30 17:34:19.952882+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:19.952996+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:19.953032+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:19.953059+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:19.953109+00:00 [info] <0.221.0> Feature flags: [x] implicit_default_bindings +2023-06-30 17:34:19.953136+00:00 [info] <0.221.0> Feature flags: [x] maintenance_mode_status +2023-06-30 17:34:19.953174+00:00 [info] <0.221.0> Feature flags: [x] quorum_queue +2023-06-30 17:34:19.953200+00:00 [info] <0.221.0> Feature flags: [x] stream_queue +2023-06-30 17:34:19.953261+00:00 [info] <0.221.0> Feature flags: [x] user_limits +2023-06-30 17:34:19.953286+00:00 [info] <0.221.0> Feature flags: [ ] virtual_host_metadata +2023-06-30 17:34:19.953311+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:20.078835+00:00 [info] <0.221.0> Feature flag `virtual_host_metadata`: supported, attempt to enable... +2023-06-30 17:34:20.079270+00:00 [info] <0.221.0> Feature flag `virtual_host_metadata`: mark as enabled=state_changing +2023-06-30 17:34:20.084762+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:20.084955+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:20.085022+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:20.123619+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:20.123670+00:00 [info] <0.221.0> Feature flags: [x] implicit_default_bindings +2023-06-30 17:34:20.123709+00:00 [info] <0.221.0> Feature flags: [x] maintenance_mode_status +2023-06-30 17:34:20.123795+00:00 [info] <0.221.0> Feature flags: [x] quorum_queue +2023-06-30 17:34:20.123868+00:00 [info] <0.221.0> Feature flags: [x] stream_queue +2023-06-30 17:34:20.123904+00:00 [info] <0.221.0> Feature flags: [x] user_limits +2023-06-30 17:34:20.123938+00:00 [info] <0.221.0> Feature flags: [~] virtual_host_metadata +2023-06-30 17:34:20.129496+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:20.762339+00:00 [info] <0.221.0> Waiting for Mnesia tables for 30000 ms, 9 retries left +2023-06-30 17:34:20.764058+00:00 [info] <0.221.0> Successfully synced tables from a peer +2023-06-30 17:34:20.954333+00:00 [info] <0.221.0> Feature flag `virtual_host_metadata`: mark as enabled=true +2023-06-30 17:34:20.971476+00:00 [info] <0.221.0> Feature flags: list of feature flags found: +2023-06-30 17:34:20.972331+00:00 [info] <0.221.0> Feature flags: [x] classic_mirrored_queue_version +2023-06-30 17:34:20.974129+00:00 [info] <0.221.0> Feature flags: [x] drop_unroutable_metric +2023-06-30 17:34:20.974567+00:00 [info] <0.221.0> Feature flags: [x] empty_basic_get_metric +2023-06-30 17:34:20.975869+00:00 [info] <0.221.0> Feature flags: [x] implicit_default_bindings +2023-06-30 17:34:20.976144+00:00 [info] <0.221.0> Feature flags: [x] maintenance_mode_status +2023-06-30 17:34:20.976385+00:00 [info] <0.221.0> Feature flags: [x] quorum_queue +2023-06-30 17:34:20.985341+00:00 [info] <0.221.0> Feature flags: [x] stream_queue +2023-06-30 17:34:20.985453+00:00 [info] <0.221.0> Feature flags: [x] user_limits +2023-06-30 17:34:20.985503+00:00 [info] <0.221.0> Feature flags: [x] virtual_host_metadata +2023-06-30 17:34:20.985546+00:00 [info] <0.221.0> Feature flags: feature flag states written to disk: yes +2023-06-30 17:34:21.115745+00:00 [info] <0.221.0> Waiting for Mnesia tables for 30000 ms, 9 retries left +2023-06-30 17:34:21.116040+00:00 [info] <0.221.0> Successfully synced tables from a peer +2023-06-30 17:34:21.299833+00:00 [info] <0.221.0> Waiting for Mnesia tables for 30000 ms, 9 retries left +2023-06-30 17:34:21.300111+00:00 [info] <0.221.0> Successfully synced tables from a peer +2023-06-30 17:34:21.300184+00:00 [info] <0.221.0> Peer discovery backend rabbit_peer_discovery_classic_config does not support registration, skipping registration. +2023-06-30 17:34:21.300272+00:00 [info] <0.221.0> Will try to unlock with peer discovery backend rabbit_peer_discovery_classic_config +2023-06-30 17:34:21.300468+00:00 [info] <0.221.0> Running boot step database_sync defined by app rabbit +2023-06-30 17:34:21.300694+00:00 [info] <0.221.0> Running boot step feature_flags defined by app rabbit +2023-06-30 17:34:21.338224+00:00 [info] <0.221.0> Running boot step codec_correctness_check defined by app rabbit +2023-06-30 17:34:21.338352+00:00 [info] <0.221.0> Running boot step external_infrastructure defined by app rabbit +2023-06-30 17:34:21.342333+00:00 [info] <0.221.0> Running boot step rabbit_registry defined by app rabbit +2023-06-30 17:34:21.342824+00:00 [info] <0.221.0> Running boot step rabbit_auth_mechanism_cr_demo defined by app rabbit +2023-06-30 17:34:21.343205+00:00 [info] <0.221.0> Running boot step rabbit_queue_location_random defined by app rabbit +2023-06-30 17:34:21.343375+00:00 [info] <0.221.0> Running boot step rabbit_event defined by app rabbit +2023-06-30 17:34:21.352964+00:00 [info] <0.221.0> Running boot step rabbit_auth_mechanism_amqplain defined by app rabbit +2023-06-30 17:34:21.353234+00:00 [info] <0.221.0> Running boot step rabbit_auth_mechanism_plain defined by app rabbit +2023-06-30 17:34:21.353365+00:00 [info] <0.221.0> Running boot step rabbit_exchange_type_direct defined by app rabbit +2023-06-30 17:34:21.362955+00:00 [info] <0.221.0> Running boot step rabbit_exchange_type_fanout defined by app rabbit +2023-06-30 17:34:21.363138+00:00 [info] <0.221.0> Running boot step rabbit_exchange_type_headers defined by app rabbit +2023-06-30 17:34:21.363295+00:00 [info] <0.221.0> Running boot step rabbit_exchange_type_topic defined by app rabbit +2023-06-30 17:34:21.363383+00:00 [info] <0.221.0> Running boot step rabbit_mirror_queue_mode_all defined by app rabbit +2023-06-30 17:34:21.363463+00:00 [info] <0.221.0> Running boot step rabbit_mirror_queue_mode_exactly defined by app rabbit +2023-06-30 17:34:21.363565+00:00 [info] <0.221.0> Running boot step rabbit_mirror_queue_mode_nodes defined by app rabbit +2023-06-30 17:34:21.400801+00:00 [info] <0.221.0> Running boot step rabbit_priority_queue defined by app rabbit +2023-06-30 17:34:21.400929+00:00 [info] <0.221.0> Priority queues enabled, real BQ is rabbit_variable_queue +2023-06-30 17:34:21.401092+00:00 [info] <0.221.0> Running boot step rabbit_queue_location_client_local defined by app rabbit +2023-06-30 17:34:21.401244+00:00 [info] <0.221.0> Running boot step rabbit_queue_location_min_masters defined by app rabbit +2023-06-30 17:34:21.401378+00:00 [info] <0.221.0> Running boot step kernel_ready defined by app rabbit +2023-06-30 17:34:21.401695+00:00 [info] <0.221.0> Running boot step rabbit_sysmon_minder defined by app rabbit +2023-06-30 17:34:21.402464+00:00 [info] <0.221.0> Running boot step rabbit_epmd_monitor defined by app rabbit +2023-06-30 17:34:21.448988+00:00 [info] <0.568.0> epmd monitor knows us, inter-node communication (distribution) port: 25672 +2023-06-30 17:34:21.449413+00:00 [info] <0.221.0> Running boot step guid_generator defined by app rabbit +2023-06-30 17:34:21.480870+00:00 [info] <0.221.0> Running boot step rabbit_node_monitor defined by app rabbit +2023-06-30 17:34:21.481474+00:00 [info] <0.572.0> Starting rabbit_node_monitor +2023-06-30 17:34:21.481865+00:00 [info] <0.221.0> Running boot step delegate_sup defined by app rabbit +2023-06-30 17:34:21.484047+00:00 [info] <0.221.0> Running boot step rabbit_memory_monitor defined by app rabbit +2023-06-30 17:34:21.484625+00:00 [info] <0.221.0> Running boot step rabbit_fifo_dlx_sup defined by app rabbit +2023-06-30 17:34:21.484903+00:00 [info] <0.221.0> Running boot step core_initialized defined by app rabbit +2023-06-30 17:34:21.484975+00:00 [info] <0.221.0> Running boot step upgrade_queues defined by app rabbit +2023-06-30 17:34:21.607289+00:00 [info] <0.221.0> message_store upgrades: 1 to apply +2023-06-30 17:34:21.609176+00:00 [info] <0.221.0> message_store upgrades: Applying rabbit_variable_queue:move_messages_to_vhost_store +2023-06-30 17:34:21.609673+00:00 [info] <0.221.0> message_store upgrades: No durable queues found. Skipping message store migration +2023-06-30 17:34:21.609956+00:00 [info] <0.221.0> message_store upgrades: Removing the old message store data +2023-06-30 17:34:21.651076+00:00 [info] <0.221.0> message_store upgrades: All upgrades applied successfully +2023-06-30 17:34:21.775782+00:00 [info] <0.221.0> Running boot step channel_tracking defined by app rabbit +2023-06-30 17:34:21.805945+00:00 [info] <0.221.0> Setting up a table for channel tracking on this node: tracked_channel_on_node_rabbit@2b6eb27dd567 +2023-06-30 17:34:21.837092+00:00 [info] <0.221.0> Setting up a table for channel tracking on this node: tracked_channel_table_per_user_on_node_rabbit@2b6eb27dd567 +2023-06-30 17:34:21.838071+00:00 [info] <0.221.0> Running boot step rabbit_channel_tracking_handler defined by app rabbit +2023-06-30 17:34:21.838988+00:00 [info] <0.221.0> Running boot step connection_tracking defined by app rabbit +2023-06-30 17:34:21.959051+00:00 [info] <0.221.0> Setting up a table for connection tracking on this node: tracked_connection_on_node_rabbit@2b6eb27dd567 +2023-06-30 17:34:22.006458+00:00 [info] <0.221.0> Setting up a table for per-vhost connection counting on this node: tracked_connection_per_vhost_on_node_rabbit@2b6eb27dd567 +2023-06-30 17:34:22.072286+00:00 [info] <0.221.0> Setting up a table for per-user connection counting on this node: tracked_connection_table_per_user_on_node_rabbit@2b6eb27dd567 +2023-06-30 17:34:22.074361+00:00 [info] <0.221.0> Running boot step rabbit_connection_tracking_handler defined by app rabbit +2023-06-30 17:34:22.074797+00:00 [info] <0.221.0> Running boot step rabbit_definitions_hashing defined by app rabbit +2023-06-30 17:34:22.075030+00:00 [info] <0.221.0> Running boot step rabbit_exchange_parameters defined by app rabbit +2023-06-30 17:34:22.075251+00:00 [info] <0.221.0> Running boot step rabbit_mirror_queue_misc defined by app rabbit +2023-06-30 17:34:22.087175+00:00 [info] <0.221.0> Running boot step rabbit_policies defined by app rabbit +2023-06-30 17:34:22.125476+00:00 [info] <0.221.0> Running boot step rabbit_policy defined by app rabbit +2023-06-30 17:34:22.125766+00:00 [info] <0.221.0> Running boot step rabbit_queue_location_validator defined by app rabbit +2023-06-30 17:34:22.125904+00:00 [info] <0.221.0> Running boot step rabbit_quorum_memory_manager defined by app rabbit +2023-06-30 17:34:22.125988+00:00 [info] <0.221.0> Running boot step rabbit_stream_coordinator defined by app rabbit +2023-06-30 17:34:22.126347+00:00 [info] <0.221.0> Running boot step rabbit_vhost_limit defined by app rabbit +2023-06-30 17:34:22.126475+00:00 [info] <0.221.0> Running boot step rabbit_mgmt_reset_handler defined by app rabbitmq_management +2023-06-30 17:34:22.126701+00:00 [info] <0.221.0> Running boot step rabbit_mgmt_db_handler defined by app rabbitmq_management_agent +2023-06-30 17:34:22.126766+00:00 [info] <0.221.0> Management plugin: using rates mode 'basic' +2023-06-30 17:34:22.127371+00:00 [info] <0.221.0> Running boot step recovery defined by app rabbit +2023-06-30 17:34:22.138992+00:00 [info] <0.221.0> Running boot step empty_db_check defined by app rabbit +2023-06-30 17:34:22.139442+00:00 [info] <0.221.0> Will seed default virtual host and user... +2023-06-30 17:34:22.168841+00:00 [info] <0.221.0> Adding vhost '/' (description: 'Default virtual host', tags: []) +2023-06-30 17:34:22.329641+00:00 [info] <0.634.0> Making sure data directory '/var/lib/rabbitmq/mnesia/rabbit@2b6eb27dd567/msg_stores/vhosts/628WB79CIFDYO9LJI6DKMI09L' for vhost '/' exists +2023-06-30 17:34:22.379852+00:00 [info] <0.634.0> Setting segment_entry_count for vhost '/' with 0 queues to '2048' +2023-06-30 17:34:22.401937+00:00 [info] <0.634.0> Starting message stores for vhost '/' +2023-06-30 17:34:22.402333+00:00 [info] <0.639.0> Message store "628WB79CIFDYO9LJI6DKMI09L/msg_store_transient": using rabbit_msg_store_ets_index to provide index +2023-06-30 17:34:22.417358+00:00 [info] <0.634.0> Started message store of type transient for vhost '/' +2023-06-30 17:34:22.417698+00:00 [info] <0.643.0> Message store "628WB79CIFDYO9LJI6DKMI09L/msg_store_persistent": using rabbit_msg_store_ets_index to provide index +2023-06-30 17:34:22.433077+00:00 [warning] <0.643.0> Message store "628WB79CIFDYO9LJI6DKMI09L/msg_store_persistent": rebuilding indices from scratch +2023-06-30 17:34:22.438457+00:00 [info] <0.634.0> Started message store of type persistent for vhost '/' +2023-06-30 17:34:22.439242+00:00 [info] <0.634.0> Recovering 0 queues of type rabbit_classic_queue took 55ms +2023-06-30 17:34:22.439365+00:00 [info] <0.634.0> Recovering 0 queues of type rabbit_quorum_queue took 0ms +2023-06-30 17:34:22.439437+00:00 [info] <0.634.0> Recovering 0 queues of type rabbit_stream_queue took 0ms +2023-06-30 17:34:22.459104+00:00 [info] <0.221.0> Created user 'user' +2023-06-30 17:34:22.472005+00:00 [info] <0.221.0> Successfully set user tags for user 'user' to [administrator] +2023-06-30 17:34:22.516067+00:00 [info] <0.221.0> Successfully set permissions for 'user' in virtual host '/' to '.*', '.*', '.*' +2023-06-30 17:34:22.516354+00:00 [info] <0.221.0> Running boot step rabbit_looking_glass defined by app rabbit +2023-06-30 17:34:22.516441+00:00 [info] <0.221.0> Running boot step rabbit_core_metrics_gc defined by app rabbit +2023-06-30 17:34:22.517439+00:00 [info] <0.221.0> Running boot step background_gc defined by app rabbit +2023-06-30 17:34:22.519124+00:00 [info] <0.221.0> Running boot step routing_ready defined by app rabbit +2023-06-30 17:34:22.519203+00:00 [info] <0.221.0> Running boot step pre_flight defined by app rabbit +2023-06-30 17:34:22.519235+00:00 [info] <0.221.0> Running boot step notify_cluster defined by app rabbit +2023-06-30 17:34:22.521005+00:00 [info] <0.221.0> Running boot step networking defined by app rabbit +2023-06-30 17:34:22.521259+00:00 [info] <0.221.0> Running boot step rabbit_maintenance_mode_state defined by app rabbit +2023-06-30 17:34:22.521321+00:00 [info] <0.221.0> Creating table rabbit_node_maintenance_states for feature flag `maintenance_mode_status` +2023-06-30 17:34:22.532493+00:00 [info] <0.221.0> Running boot step definition_import_worker_pool defined by app rabbit +2023-06-30 17:34:22.532964+00:00 [info] <0.280.0> Starting worker pool 'definition_import_pool' with 4 processes in it +2023-06-30 17:34:22.534741+00:00 [info] <0.221.0> Running boot step cluster_name defined by app rabbit +2023-06-30 17:34:22.535101+00:00 [info] <0.221.0> Initialising internal cluster ID to 'rabbitmq-cluster-id-3EC3aXLvrXWw67gAEbyFaw' +2023-06-30 17:34:22.551935+00:00 [info] <0.221.0> Running boot step direct_client defined by app rabbit +2023-06-30 17:34:22.552259+00:00 [info] <0.221.0> Running boot step rabbit_management_load_definitions defined by app rabbitmq_management +2023-06-30 17:34:22.552438+00:00 [info] <0.677.0> Resetting node maintenance status +2023-06-30 17:34:22.708404+00:00 [info] <0.736.0> Management plugin: HTTP (non-TLS) listener started on port 15672 +2023-06-30 17:34:22.708736+00:00 [info] <0.764.0> Statistics database started. +2023-06-30 17:34:22.711912+00:00 [info] <0.763.0> Starting worker pool 'management_worker_pool' with 3 processes in it +2023-06-30 17:34:22.795797+00:00 [info] <0.778.0> Prometheus metrics: HTTP (non-TLS) listener started on port 15692 +2023-06-30 17:34:22.796227+00:00 [info] <0.677.0> Ready to start client connection listeners +2023-06-30 17:34:22.821136+00:00 [info] <0.822.0> started TCP listener on [::]:5672 + completed with 4 plugins. +2023-06-30 17:34:23.738441+00:00 [info] <0.677.0> Server startup complete; 4 plugins started. +2023-06-30 17:34:23.738441+00:00 [info] <0.677.0> * rabbitmq_prometheus +2023-06-30 17:34:23.738441+00:00 [info] <0.677.0> * rabbitmq_management +2023-06-30 17:34:23.738441+00:00 [info] <0.677.0> * rabbitmq_web_dispatch +2023-06-30 17:34:23.738441+00:00 [info] <0.677.0> * rabbitmq_management_agent +``` + +
+ +###### [Back to top](#contributing) + +### Celery task-queue + +Celery is currently being used as to process background jobs via a task-queue + +If you are implementing a new task, you will need to restart All task-queue related +Automappa services (`redis`, `flower`, `web`, `celery`, `rabbitmq`) +as tasks are registered with celery at instantiation and will not be +'hot-reloaded' like other parts of the app. + +For more information on implementing new tasks see the [task docs](../automappa/pages/home/tasks/README.md) + +
+ +What to see at startup + +```console +automappa-celery-1 | +automappa-celery-1 | -------------- celery@beade5f64d0f v5.3.0 (emerald-rush) +automappa-celery-1 | --- ***** ----- +automappa-celery-1 | -- ******* ---- Linux-5.15.49-linuxkit-x86_64-with-glibc2.31 2023-06-09 11:18:28 +automappa-celery-1 | - *** --- * --- +automappa-celery-1 | - ** ---------- [config] +automappa-celery-1 | - ** ---------- .> app: automappa.tasks:0x7f66305c1070 +automappa-celery-1 | - ** ---------- .> transport: amqp://user:**@rabbitmq:5672// +automappa-celery-1 | - ** ---------- .> results: redis://redis:6379/0 +automappa-celery-1 | - *** --- * --- .> concurrency: 2 (prefork) +automappa-celery-1 | -- ******* ---- .> task events: ON +automappa-celery-1 | --- ***** ----- +automappa-celery-1 | -------------- [queues] +automappa-celery-1 | .> celery exchange=celery(direct) key=celery +automappa-celery-1 | +automappa-celery-1 | +automappa-celery-1 | [tasks] +automappa-celery-1 | . automappa.tasks.aggregate_embeddings +automappa-celery-1 | . automappa.tasks.count_kmer +automappa-celery-1 | . automappa.tasks.embed_kmer +automappa-celery-1 | . automappa.tasks.get_embedding_traces_df +automappa-celery-1 | . automappa.tasks.normalize_kmer +automappa-celery-1 | . automappa.tasks.preprocess_clusters_geom_medians +automappa-celery-1 | . automappa.tasks.preprocess_marker_symbols +automappa-celery-1 | +``` + +
+ +###### [Back to top](#contributing) + +### Redis + +
+ +What to see at startup + +```console +1:C 30 Jun 2023 16:28:07.865 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo +1:C 30 Jun 2023 16:28:07.865 # Redis version=7.0.11, bits=64, commit=00000000, modified=0, pid=1, just started +1:C 30 Jun 2023 16:28:07.865 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf +1:M 30 Jun 2023 16:28:07.867 * monotonic clock: POSIX clock_gettime +1:M 30 Jun 2023 16:28:07.868 * Running mode=standalone, port=6379. +1:M 30 Jun 2023 16:28:07.868 # Server initialized +1:M 30 Jun 2023 16:28:07.868 * Ready to accept connections +``` + +
+ +###### [Back to top](#contributing) + +### Flower + +
+ +What to see on successful startup + +```console +DEBUG:automappa.tasks:celery config: +task_send_sent_event: True +task_track_started: True +worker_concurrency: 2 +worker_prefetch_multiplier: 1 +worker_send_task_events: True +broker_url: 'amqp://user:********@rabbitmq:5672//' +result_backend: 'redis://redis:6379/0' +deprecated_settings: None +INFO:flower.command:Visit me at http://0.0.0.0:5555 +INFO:flower.command:Broker: amqp://user:**@rabbitmq:5672// +INFO:flower.command:Registered tasks: +['automappa.tasks.aggregate_embeddings', + 'automappa.tasks.count_kmer', + 'automappa.tasks.embed_kmer', + 'automappa.tasks.get_embedding_traces_df', + 'automappa.tasks.normalize_kmer', + 'automappa.tasks.preprocess_clusters_geom_medians', + 'automappa.tasks.preprocess_marker_symbols', + 'celery.accumulate', + 'celery.backend_cleanup', + 'celery.chain', + 'celery.chord', + 'celery.chord_unlock', + 'celery.chunks', + 'celery.group', + 'celery.map', + 'celery.starmap'] +INFO:kombu.mixins:Connected to amqp://user:**@rabbitmq:5672// +WARNING:flower.inspector:Inspect method registered failed +WARNING:flower.inspector:Inspect method active failed +WARNING:flower.inspector:Inspect method scheduled failed +WARNING:flower.inspector:Inspect method reserved failed +WARNING:flower.inspector:Inspect method active_queues failed +WARNING:flower.inspector:Inspect method conf failed +WARNING:flower.inspector:Inspect method stats failed +WARNING:flower.inspector:Inspect method revoked failed +``` + +
+ +###### [Back to top](#contributing) + +## Development resources + +### Libraries + +- [dash-extensions docs](https://www.dash-extensions.com/ "dash-extensions documentation") +- [dash-extensions GitHub](https://github.com/thedirtyfew/dash-extensions "dash-extensions GitHub repository") +- [plotly Dash docs](https://dash.plotly.com/ "plotly Dash documentation") +- [dash-bootstrap-components docs](http://dash-bootstrap-components.opensource.faculty.ai/ "dash-bootstrap-components documentation") +- [dash-mantine-components docs](https://www.dash-mantine-components.com/ "dash-mantine-components documentation") +- [dash-iconify icons browser]( "Iconify icon sets") + +[Back to top](#contributing) \ No newline at end of file diff --git a/docs/IDEA.md b/docs/IDEA.md index da2f50d6..ed439c04 100644 --- a/docs/IDEA.md +++ b/docs/IDEA.md @@ -1,5 +1,6 @@ ## Random Ideas for potential incorporation to Automappa -1. [Dash Mantine Components](https://www.dash-mantine-components.com/) -2. [Multipage web app using dash /pages](https://community.plotly.com/t/introducing-dash-pages-a-dash-2-x-feature-preview/57775#introducing-dash-pages-2) -3. [dashlabs.pythonanywhere.com](https://dashlabs.pythonanywhere.com/) +1. ~[Dash Mantine Components](https://www.dash-mantine-components.com/)~ +2. ~[Multipage web app using dash /pages](https://community.plotly.com/t/introducing-dash-pages-a-dash-2-x-feature-preview/57775#introducing-dash-pages-2)~ +3. ~[dashlabs.pythonanywhere.com](https://dashlabs.pythonanywhere.com/)~ +4. [pyarrow](https://arrow.apache.org/docs/python/pandas.html) \ No newline at end of file diff --git a/docs/TODO.md b/docs/TODO.md index 0f6aaf28..75a12554 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -1,40 +1,130 @@ # TODO (WIP) -1. [x] :art:::racehorse: Pass postgres datatable `table_name` back from `dash-uploader` callback -2. [x] :racehorse::art: `dcc.Store(...)` datatable id in `dcc.Store(...)` -3. [x] :racehorse::art: Save uploaded file metadata to postgres datatable -4. [x] :racehorse::art: Populate `dcc.Store(...)` with user's existing datatables -5. [ ] ~:racehorse::art: Populate `data_table.DataTable` with clickable `dcc.Link` that navigates user to `mag_refinement.py` (will load data relevant to link, e.g. metagenome annotations, markers, etc.)~ -6. [x] :racehorse: Retrieve datatable within `mag_refinement.py` - - [x] (`home.py`) binning, markers, metagenome dropdowns for selecting data available in database - - [x] (`home.py`) dropdown values correspond to `table_id` and are sent to `mag_refinement.py` callbacks, replacing `pd.read_json(...)` - - [x] (`home.py`/`index.py`) dropdowns should have a "NA" or placeholder disabling navigation to other layouts when data for the particular filetype is unavailable -7. [x] :art::bug: Finish addition of other embeddings within 2D scatterplot in `mag_refinement.py` -8. [x] :carrot::racehorse: Add celery task-queue -9. [x] :carrot::racehorse: Add celery-monitoring services -10. [ ] :carrot::racehorse: Add uploaded data ingestion to task-queue -11. [x] :carrot::racehorse: Add k-mer embedding tasks to task-queue -12. [x] :fire: Remove parser args for data inputs (i.e. not relevant to running automappa server) -13. [x] :art: Refactor `on_{binning,markers,metagenome}_upload` callbacks to one function that takes in the filetype to determine storage method -14. [x] :racehorse: Retrieve datatable within `mag_summary.py` -15. [x] :whale::elephant::sunflower: Grafana provision from within `docker-compose.yml` -16. [ ] :art::fire: Refactor `store_binning_main(...)` s.t. refinement-data is stored in a separate async process. -17. [ ] :art::carrot: Add selections/dropdowns/progress updates for metagenome embeddings -18. [x] :art: Add embeddings selection dropdown in settings for 2d scatterplot axes (place task queueing from these selections) -19. [ ] :art: Show some type of progress for metagenome ingestion to postgres DB -20. [x] :art: Dynamically Format scatterplot 2-d axes (i.e. Gc_Content to GC Content, etc.) -21. [ ] :racehorse: Reduce queries to postgres DB -22. [x] :bug::wrench: Fix scatterplot-2d.figure callback (LINENO#685) `ValueError: columns overlap but no suffix specified: Index(['5mers-ilr-umap_x_1', '5mers-ilr-umap_x_2'], dtype='object')` -23. [ ] :bug::wrench: Add `dcc.Interval`? or `interval` argument? to Scatterplot 2D Axes dropdown s.t. disabled is updated appropriately (poll availability of embeddings in pg DB) - --------------------------------------------------------------------------------------------------- - -## Misc. Resources +## TODOs lists +### 🐰:carrot: task-queue + +- [ ] Determine minimal task-queue case for setup/testing +- [ ] simple time.sleep(10) and generation of tasks table displaying progress +- [ ] pre-processing step for marker symbols and marker sizes based on marker counts in `Contig` +- [ ] k-mer freq. analysis steps + +#### Data ingestion, CRUD Operations and UI + +- [ ] create metagenome +- [ ] create contigs +- [ ] create markers +- [ ] create connections +- [ ] update each created to metagenome with sample name + +#### 🐎 Component data pre-processing + +- [ ] component to inform user of currently processing tasks (i.e. [Dash AG Grid](https://dash.plotly.com/dash-ag-grid/ "Dash AG grid component"), [notification](https://www.dash-mantine-components.com/components/notification "Dash mantine notification component"), etc.) +- [ ] marker symbol +- [ ] kmer embedding +- [ ] geometric medians +- [ ] MAG summary + +### Pages + +#### base Layout components + +- [x] `metagenome_id_store = dcc.Store(ids.METAGENOME_ID_STORE, "data")` + - [x] Pattern matching callback to get `metagenome_id` (or `Metagenome.name`) for + selected sample card + - [ ] Pattern matching callback to uncheck any other cards when a sample is selected + +#### Home components + +- [ ] loader "new sample" button disable during db ingestion +- [ ] Loader (or notification or overlay? on sample submit button click) + - [ ] User notified on submit button click (will require task-queue) + - [ ] Notification dismissed when database ingestion finished (will require task-queue) + + Supposedly background callbacks here are not supported with notifications. +- [ ] :bug: new sample card rendered upon stepper sample submit button click +- [x] Synchronize pages using `metagenome_id` in a `dcc.Store(ids.METAGENOME_ID_STORE)` + - [x] 🔗 Add callbacks for sample cards + - [x] :fire: Remove button ~refine button navigates to `MAG-refinement`~ + - [x] :fire: Remove button ~summarize button navigates to `MAG-summary`~ + - [x] :link: "Card selected" option for storage in `ids.METAGENOME_ID_STORE` + +#### MAG refinement components + +- [x] Determine construction of `Refinement(SQLModel)` +- [x] Allow save MAG refinement after scatterplot selections +- [x] Allow table generation from saved refinements +- [x] Allow data download +- [ ] re-implement cytoscape contig connection graph callbacks +- [x] Connect `Input(ids.COVERAGE_RANGE_SLIDER, "value")` to 2D scatterplots + > (End-user ? --> should 3D scatterplot also be updated?) + +Component protocols: + +> Following syntax: `class ComponentDataSource(Protocol)` + +- [x] `class Scatterplot2dDataSource(Protocol)` +- [x] `class MarkerSymbolsLegendDataSource(Protocol)` +- [x] `class SaveSelectionsButtonDataSource(Protocol)` +- [ ] `class SettingsOffcanvasDataSource(Protocol)` + - [x] `class Scatterplot2dAxesOptionsDataSource(Protocol)` + - [x] `class ColorByColumnOptionsDataSource(Protocol)` + - [ ] ~`class KmerNormMethodDropdownOptionsDataSource`~ + - [ ] ~`class KmerEmbedMethodDropdownOptionsDataSource`~ +- [x] `class MagMetricsDataSource(Protocol)` +- [x] `class TaxonomyDistributionDataSource(Protocol)` + - [ ] :fire: remove use of pandas in source method +- [x] `class Scatterplot3dDataSource(Protocol)` +- [x] `class CoverageRangeSliderDataSource(Protocol)` +- [x] `class CoverageBoxplotDataSource(Protocol)` +- [x] `class GcPercentBoxplotDataSource(Protocol)` +- [x] `class LengthBoxplotDataSource(Protocol)` +- [ ] `class ContigCytoscapeDataSource(Protocol)` +- [x] `class RefinementsTableDataSource(Protocol)` + +#### MAG Summary components + +Passed `DataSource` object: + +- [ ] `class SummaryDataSource(BaseModel)` + +Component protocols: + +> Following syntax: `class ComponentDataSource(Protocol)` + +- [x] `class CoverageBoxplotDataSource(Protocol)` +- [x] `class GcPercentBoxplotDataSource(Protocol)` +- [x] `class LengthBoxplotDataSource(Protocol)` +- [x] `class ClusterMetricsBarplotDataSource(Protocol)` +- [x] `class ClusterDropdownDataSource(Protocol)` +- [x] `class ClusterMetricsBoxplotDataSource(Protocol)` +- [x] `class ClusterStatsTableDataSource(Protocol)` +- [x] `class ClusterTaxonomyDistributionDataSource(Protocol)` + +----------------------------------------------------- + +## Development resources + +### Libraries + +- [dash-extensions docs](https://www.dash-extensions.com/ "dash-extensions documentation") - [Dash Extensions Enrich Module](https://www.dash-extensions.com/getting-started/enrich) +- [dash-extensions GitHub](https://github.com/thedirtyfew/dash-extensions "dash-extensions GitHub repository") +- [dash-mantine-components docs](https://www.dash-mantine-components.com/ "dash-mantine-components documentation") +- [dash-iconify icons browser]( "Iconify icon sets") +- [dash-bootstrap-components docs](http://dash-bootstrap-components.opensource.faculty.ai/ "dash-bootstrap-components documentation") +- [plotly Dash docs](https://dash.plotly.com/ "plotly Dash documentation") + +### Services + +#### Networking, backend and task management + - [docker-compose networking docs]() - [live mongoDB dash example]() - [plotly dash `dcc.Store` docs]() -- [dash bootstrap components docs](https://dash-bootstrap-components.opensource.faculty.ai/docs/components/layout/) - [how to access rabbitmq publicly]( "how to access RabbitMQ publicly") - [StackOverflow: how to access rabbitmq publicly](https://stackoverflow.com/a/57612615 "StackOverflow: how to access RabbitMQ publicly") + +### Miscellaneous + +dash logger is not supported with pattern matching callbacks diff --git a/docs/data.md b/docs/data.md new file mode 100644 index 00000000..377c3247 --- /dev/null +++ b/docs/data.md @@ -0,0 +1,54 @@ +# Data + +## Models + +## Schema + +## Loader + +## Source + +>TODO Create specific `DataSource`s according to methods from components +> i.e. `Scatterplot2dDataSource` + +To increase cohesion we may want to separate `DataSource` into (for example): + +- `MetagenomeDataSource` +- `MarkerDataSource` +- `ConnectionDataSource` +- `BinningDataSource` + +Which should implement the methods defined in the ComponentDataSource protocols + +```python +# CRUD metagenome +logger.info("Begin CRUD metagenome") +create_metagenome("data/lasonolide/metagenome.filtered.fna") +metagenomes = read_metagenomes() +logger.info("End CRUD metagenome") + +# CRUD contigs +logger.info("Begin CRUD Contigs") +create_contigs("data/lasonolide/binning.tsv") +contigs = read_contigs( + [ + "NODE_122095_length_595_cov_1.911111", + "NODE_137073_length_558_cov_1.087475", + "NODE_53038_length_961_cov_43.318985", + "NODE_64513_length_857_cov_39.677057", + "NODE_43271_length_1085_cov_2.514563", + ] +) +logger.info("End CRUD Contigs") +# CRUD markers +logger.info("Begin CRUD Markers") +create_markers("data/lasonolide/bacteria.markers.tsv") +markers = read_markers() +logger.info("End CRUD Markers") + +# CRUD cytoscape connections +logger.info("Begin CRUD cytoscape connections") +create_cytoscape_connections("data/lasonolide/cytoscape.connections.tab") +connections = read_cytoscape_connections() +logger.info("End CRUD cytoscape connections") +``` diff --git a/environment.yml b/environment.yml index 69805506..83efb9bc 100644 --- a/environment.yml +++ b/environment.yml @@ -6,23 +6,29 @@ channels: - defaults dependencies: - autometa - - dash + - dash==2.10.2 - dash-bootstrap-components - - dash-daq + - dash_cytoscape==0.2.0 - flask - flower - msgpack-python - - numpy + - numpy==1.20.0 - pandas - plotly - psycopg2 - python-dotenv + - python==3.9.* - pydantic - - scipy==1.8.* - - sqlalchemy + - scipy + - scikit-learn==0.24 + - sqlalchemy==1.4.41 + - sqlmodel==0.0.8 - pip - pip: - - dash-extensions + - dash-extensions==1.* + - dash-mantine-components==0.12 + - dash-ag-grid>=2.2 + - dash-iconify - celery[redis] - geom-median - - dash-uploader + - dash-uploader==0.6 \ No newline at end of file diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 00000000..3652c52a --- /dev/null +++ b/requirements-dev.txt @@ -0,0 +1,3 @@ +black +pytest +mypy \ No newline at end of file diff --git a/scripts/test_environment.py b/scripts/test_environment.py deleted file mode 100644 index 993fc422..00000000 --- a/scripts/test_environment.py +++ /dev/null @@ -1,14 +0,0 @@ -import dash_table -from dash_extensions import Download -from dash_extensions.snippets import send_data_frame -from dash.dependencies import Input, Output, State -from dash.exceptions import PreventUpdate -import dash_daq as daq -from plotly import graph_objs as go -import dash_core_components as dcc -import dash_html_components as html -import pandas as pd -import flask -import dash -import base64 -import io diff --git a/setup.py b/setup.py index 93cb75f4..5516ad57 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,4 @@ -"""Setup for installation of Autometa.""" +"""Setup for installation of Automappa.""" import os From 150929872ab5ce1c44c7d70f096bfae6311d97db Mon Sep 17 00:00:00 2001 From: WiscEvan Date: Wed, 26 Jul 2023 12:11:30 +0200 Subject: [PATCH 37/37] :memo: Update docs for release 2.2 --- README.md | 27 ++-- docs/CONTRIBUTING.md | 57 +++++++- docs/IDEA.md | 6 - docs/TODO.md | 130 ------------------ docs/TROUBLESHOOTING.md | 39 ------ docs/data.md | 54 -------- docs/page-overview.md | 17 +++ docs/static/home-tab.png | Bin 0 -> 82061 bytes docs/static/refinement-tab.png | Bin 0 -> 850694 bytes docs/static/summary-tab.png | Bin 0 -> 463468 bytes .../tasks/README.md => docs/task-queue.md | 0 11 files changed, 88 insertions(+), 242 deletions(-) delete mode 100644 docs/IDEA.md delete mode 100644 docs/TODO.md delete mode 100644 docs/TROUBLESHOOTING.md delete mode 100644 docs/data.md create mode 100644 docs/page-overview.md create mode 100644 docs/static/home-tab.png create mode 100644 docs/static/refinement-tab.png create mode 100644 docs/static/summary-tab.png rename automappa/pages/home/tasks/README.md => docs/task-queue.md (100%) diff --git a/README.md b/README.md index 9ed67759..ad5d0ebf 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,20 @@ # Automappa: An interactive interface for exploration of metagenomes ![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/WiscEvan/Automappa?label=latest) -[![Anaconda-Server Install Badge](https://anaconda.org/bioconda/automappa/badges/installer/conda.svg)](https://conda.anaconda.org/bioconda) -[![Anaconda-Server Platforms Badge](https://anaconda.org/bioconda/automappa/badges/platforms.svg)](https://anaconda.org/bioconda/automappa) -[![Anaconda-Server Downloads Badge](https://anaconda.org/bioconda/automappa/badges/downloads.svg)](https://anaconda.org/bioconda/automappa) | Image Name | Image Tag | Status | |----------------------|-----------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `evanrees/automappa` | `main`,`latest` | [![docker CI/CD](https://github.com/WiscEvan/Automappa/actions/workflows/docker.yml/badge.svg?branch=main "evanrees/automappa:main")](https://github.com/WiscEvan/Automappa/actions/workflows/docker.yml) | | `evanrees/automappa` | `develop` | [![develop docker CI/CD](https://github.com/WiscEvan/Automappa/actions/workflows/docker.yml/badge.svg?branch=develop "evanrees/automappa:develop")](https://github.com/WiscEvan/Automappa/actions/workflows/docker.yml) | -![automappa_demo_920](https://user-images.githubusercontent.com/25933122/158899748-bf21c1fc-6f67-4fd8-af89-4e732fa2edcd.gif) + + +> You may also [see each page as a static view](docs/page-overview.md) ## :deciduous_tree: Automappa testing setup/run commands -- [Clone the Automappa Repo](#clone-the-repository) -- [Run `make build` using Makefile](#build-images-for-automappa-services) +- [Clone the Automappa Repo](#clone-the-automappa-repository) +- [Run `make build` using Makefile](#build-images-for-services-used-by-automappa) - [Run `make up` using Makefile](#build-and-run-automappa-services) - [Open the Automappa url](#navigate-to-automappa-page) - [Download test data](#download-test-data) @@ -23,10 +22,10 @@ ### clone the Automappa Repository ```bash -git clone -b develop https://github.com/WiscEvan/Automappa +git clone https://github.com/WiscEvan/Automappa ``` -### build images for automappa services +### build images for services used by Automappa ```bash make build @@ -49,8 +48,16 @@ Once you see `automappa_web_1` running from the terminal logs, you should be abl ### Download Test Data -Test data to try out Automappa may be downloaded from here: +Test data to try out Automappa may be downloaded from the google drive in the [Automappa test data folder]() + +Try out different settings and perform your own refinements on some of this sponge data! -This data is not yet binned, so you can easily try out different settings and perform your own refinements on some example data. +>NOTE: This dataset was retrieved from: +> +> Uppal, Siddharth, Jackie L. Metz, René K. M. Xavier, Keshav Kumar Nepal, Dongbo Xu, Guojun Wang, and Jason C. Kwan. 2022. “Uncovering Lasonolide A Biosynthesis Using Genome-Resolved Metagenomics.” mBio 13 (5): e0152422. Happy binning! + +## Contributors + +![Automappa's Contributors](https://contrib.rocks/image?repo=WiscEvan/Automappa) diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index a6973503..66814f97 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -24,8 +24,9 @@ - [Celery](#celery-task-queue) - [Redis](#redis) - [Flower](#flower) -- [dev resources](#development-resources) - - [component libraries](#libraries) +- [Dev Resources](#development-resources) + - [Component libraries](#libraries) + - [Monitoring services](#monitoring-and-task-queue-services) ## Getting started with development @@ -1235,4 +1236,54 @@ WARNING:flower.inspector:Inspect method revoked failed - [dash-mantine-components docs](https://www.dash-mantine-components.com/ "dash-mantine-components documentation") - [dash-iconify icons browser]( "Iconify icon sets") -[Back to top](#contributing) \ No newline at end of file +### Monitoring and Task-queue Services + +#### Networking, backend and task management + +- [docker-compose networking docs]() +- [live mongoDB dash example]() +- [plotly dash `dcc.Store` docs]() +- [how to access rabbitmq publicly]( "how to access RabbitMQ publicly") +- [StackOverflow: how to access rabbitmq publicly](https://stackoverflow.com/a/57612615 "StackOverflow: how to access RabbitMQ publicly") +- [celery rabbitmq tutorial](https://suzannewang.com/celery-rabbitmq-tutorial/) + +### Miscellaneous + +dash logger is not supported with pattern matching callbacks + + +#### docker-compose services configuration + +>NOTE: The Prometheus and Grafana services are disabled by default. You may enable them by removing the comments in the docker compose +file. + +***NOTE: All of this assumes you have all docker services running via `make up` or `docker-compose up`*** + +> ~Provision grafana from `docker-compose.yml`. See: [Grafana provisioning example data source config file](https://grafana.com/docs/grafana/latest/administration/provisioning/#example-data-source-config-file)~ +> Found a nice blog post and accompanying GitHub repo to follow: +> +> - [Medium blog post](https://medium.com/swlh/easy-grafana-and-docker-compose-setup-d0f6f9fcec13) +> - [github.com/annea-ai/grafana-infrastructure]() +> - [Grafana docs on Provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/) + +- RabbitMQ management - [http://localhost:15672](http://localhost:15672) +- Flower link - [http://localhost:5555](http://localhost:5555) +- Prometheus link - [http://localhost:9090](http://localhost:9090) +- Grafana link - [http://localhost:3000](http://localhost:3000) + +#### Grafana configuration + + +- flower+prometheus+grafana [add prometheus as a data source in grafana]( "flower+prometheus+grafana add prometheus as a data source in grafana") +- grafana link - [http://localhost:3000](http://localhost:3000) + +Add the prometheus url as: + +```bash +http://prometheus:9090 +``` + +Notice the tutorial mentions `http://localhost:9090`, but since this is running as a service using `docker-compose` the hostname changes to the +`prometheus` alias (this is the name of the service in the `docker-compose.yml` file) + +[Back to top](#contributing) diff --git a/docs/IDEA.md b/docs/IDEA.md deleted file mode 100644 index ed439c04..00000000 --- a/docs/IDEA.md +++ /dev/null @@ -1,6 +0,0 @@ -## Random Ideas for potential incorporation to Automappa - -1. ~[Dash Mantine Components](https://www.dash-mantine-components.com/)~ -2. ~[Multipage web app using dash /pages](https://community.plotly.com/t/introducing-dash-pages-a-dash-2-x-feature-preview/57775#introducing-dash-pages-2)~ -3. ~[dashlabs.pythonanywhere.com](https://dashlabs.pythonanywhere.com/)~ -4. [pyarrow](https://arrow.apache.org/docs/python/pandas.html) \ No newline at end of file diff --git a/docs/TODO.md b/docs/TODO.md deleted file mode 100644 index 75a12554..00000000 --- a/docs/TODO.md +++ /dev/null @@ -1,130 +0,0 @@ -# TODO (WIP) - -## TODOs lists - -### 🐰:carrot: task-queue - -- [ ] Determine minimal task-queue case for setup/testing -- [ ] simple time.sleep(10) and generation of tasks table displaying progress -- [ ] pre-processing step for marker symbols and marker sizes based on marker counts in `Contig` -- [ ] k-mer freq. analysis steps - -#### Data ingestion, CRUD Operations and UI - -- [ ] create metagenome -- [ ] create contigs -- [ ] create markers -- [ ] create connections -- [ ] update each created to metagenome with sample name - -#### 🐎 Component data pre-processing - -- [ ] component to inform user of currently processing tasks (i.e. [Dash AG Grid](https://dash.plotly.com/dash-ag-grid/ "Dash AG grid component"), [notification](https://www.dash-mantine-components.com/components/notification "Dash mantine notification component"), etc.) -- [ ] marker symbol -- [ ] kmer embedding -- [ ] geometric medians -- [ ] MAG summary - -### Pages - -#### base Layout components - -- [x] `metagenome_id_store = dcc.Store(ids.METAGENOME_ID_STORE, "data")` - - [x] Pattern matching callback to get `metagenome_id` (or `Metagenome.name`) for - selected sample card - - [ ] Pattern matching callback to uncheck any other cards when a sample is selected - -#### Home components - -- [ ] loader "new sample" button disable during db ingestion -- [ ] Loader (or notification or overlay? on sample submit button click) - - [ ] User notified on submit button click (will require task-queue) - - [ ] Notification dismissed when database ingestion finished (will require task-queue) - - Supposedly background callbacks here are not supported with notifications. -- [ ] :bug: new sample card rendered upon stepper sample submit button click -- [x] Synchronize pages using `metagenome_id` in a `dcc.Store(ids.METAGENOME_ID_STORE)` - - [x] 🔗 Add callbacks for sample cards - - [x] :fire: Remove button ~refine button navigates to `MAG-refinement`~ - - [x] :fire: Remove button ~summarize button navigates to `MAG-summary`~ - - [x] :link: "Card selected" option for storage in `ids.METAGENOME_ID_STORE` - -#### MAG refinement components - -- [x] Determine construction of `Refinement(SQLModel)` -- [x] Allow save MAG refinement after scatterplot selections -- [x] Allow table generation from saved refinements -- [x] Allow data download -- [ ] re-implement cytoscape contig connection graph callbacks -- [x] Connect `Input(ids.COVERAGE_RANGE_SLIDER, "value")` to 2D scatterplots - > (End-user ? --> should 3D scatterplot also be updated?) - -Component protocols: - -> Following syntax: `class ComponentDataSource(Protocol)` - -- [x] `class Scatterplot2dDataSource(Protocol)` -- [x] `class MarkerSymbolsLegendDataSource(Protocol)` -- [x] `class SaveSelectionsButtonDataSource(Protocol)` -- [ ] `class SettingsOffcanvasDataSource(Protocol)` - - [x] `class Scatterplot2dAxesOptionsDataSource(Protocol)` - - [x] `class ColorByColumnOptionsDataSource(Protocol)` - - [ ] ~`class KmerNormMethodDropdownOptionsDataSource`~ - - [ ] ~`class KmerEmbedMethodDropdownOptionsDataSource`~ -- [x] `class MagMetricsDataSource(Protocol)` -- [x] `class TaxonomyDistributionDataSource(Protocol)` - - [ ] :fire: remove use of pandas in source method -- [x] `class Scatterplot3dDataSource(Protocol)` -- [x] `class CoverageRangeSliderDataSource(Protocol)` -- [x] `class CoverageBoxplotDataSource(Protocol)` -- [x] `class GcPercentBoxplotDataSource(Protocol)` -- [x] `class LengthBoxplotDataSource(Protocol)` -- [ ] `class ContigCytoscapeDataSource(Protocol)` -- [x] `class RefinementsTableDataSource(Protocol)` - -#### MAG Summary components - -Passed `DataSource` object: - -- [ ] `class SummaryDataSource(BaseModel)` - -Component protocols: - -> Following syntax: `class ComponentDataSource(Protocol)` - -- [x] `class CoverageBoxplotDataSource(Protocol)` -- [x] `class GcPercentBoxplotDataSource(Protocol)` -- [x] `class LengthBoxplotDataSource(Protocol)` -- [x] `class ClusterMetricsBarplotDataSource(Protocol)` -- [x] `class ClusterDropdownDataSource(Protocol)` -- [x] `class ClusterMetricsBoxplotDataSource(Protocol)` -- [x] `class ClusterStatsTableDataSource(Protocol)` -- [x] `class ClusterTaxonomyDistributionDataSource(Protocol)` - ------------------------------------------------------ - -## Development resources - -### Libraries - -- [dash-extensions docs](https://www.dash-extensions.com/ "dash-extensions documentation") -- [Dash Extensions Enrich Module](https://www.dash-extensions.com/getting-started/enrich) -- [dash-extensions GitHub](https://github.com/thedirtyfew/dash-extensions "dash-extensions GitHub repository") -- [dash-mantine-components docs](https://www.dash-mantine-components.com/ "dash-mantine-components documentation") -- [dash-iconify icons browser]( "Iconify icon sets") -- [dash-bootstrap-components docs](http://dash-bootstrap-components.opensource.faculty.ai/ "dash-bootstrap-components documentation") -- [plotly Dash docs](https://dash.plotly.com/ "plotly Dash documentation") - -### Services - -#### Networking, backend and task management - -- [docker-compose networking docs]() -- [live mongoDB dash example]() -- [plotly dash `dcc.Store` docs]() -- [how to access rabbitmq publicly]( "how to access RabbitMQ publicly") -- [StackOverflow: how to access rabbitmq publicly](https://stackoverflow.com/a/57612615 "StackOverflow: how to access RabbitMQ publicly") - -### Miscellaneous - -dash logger is not supported with pattern matching callbacks diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md deleted file mode 100644 index 4865de63..00000000 --- a/docs/TROUBLESHOOTING.md +++ /dev/null @@ -1,39 +0,0 @@ -# Troubleshooting - -## Celery, RabbitMQ task-queue - -### Resources - -- [celery rabbitmq tutorial](https://suzannewang.com/celery-rabbitmq-tutorial/) - -## docker-compose services configuration - -***NOTE: All of this assumes you have all docker services running via `make up` or `docker-compose up`*** - -> ~Provision grafana from `docker-compose.yml`. See: [Grafana provisioning example data source config file](https://grafana.com/docs/grafana/latest/administration/provisioning/#example-data-source-config-file)~ -> Found a nice blog post and accompanying GitHub repo to follow: -> -> - [Medium blog post](https://medium.com/swlh/easy-grafana-and-docker-compose-setup-d0f6f9fcec13) -> - [github.com/annea-ai/grafana-infrastructure]() -> - [Grafana docs on Provisioning](https://grafana.com/docs/grafana/latest/administration/provisioning/) - -### Monitoring Services - -- RabbitMQ management - [http://localhost:15672](http://localhost:15672) -- Flower link - [http://localhost:5555](http://localhost:5555) -- Prometheus link - [http://localhost:9090](http://localhost:9090) -- Grafana link - [http://localhost:3000](http://localhost:3000) - -### Grafana configuration - -- flower+prometheus+grafana [add prometheus as a data source in grafana]( "flower+prometheus+grafana add prometheus as a data source in grafana") -- grafana link - [http://localhost:3000](http://localhost:3000) - -Add the prometheus url as: - -```bash -http://prometheus:9090 -``` - -Notice the tutorial mentions `http://localhost:9090`, but since this is running as a service using `docker-compose` the hostname changes to the -`prometheus` alias (this is the name of the service in the `docker-compose.yml` file) diff --git a/docs/data.md b/docs/data.md deleted file mode 100644 index 377c3247..00000000 --- a/docs/data.md +++ /dev/null @@ -1,54 +0,0 @@ -# Data - -## Models - -## Schema - -## Loader - -## Source - ->TODO Create specific `DataSource`s according to methods from components -> i.e. `Scatterplot2dDataSource` - -To increase cohesion we may want to separate `DataSource` into (for example): - -- `MetagenomeDataSource` -- `MarkerDataSource` -- `ConnectionDataSource` -- `BinningDataSource` - -Which should implement the methods defined in the ComponentDataSource protocols - -```python -# CRUD metagenome -logger.info("Begin CRUD metagenome") -create_metagenome("data/lasonolide/metagenome.filtered.fna") -metagenomes = read_metagenomes() -logger.info("End CRUD metagenome") - -# CRUD contigs -logger.info("Begin CRUD Contigs") -create_contigs("data/lasonolide/binning.tsv") -contigs = read_contigs( - [ - "NODE_122095_length_595_cov_1.911111", - "NODE_137073_length_558_cov_1.087475", - "NODE_53038_length_961_cov_43.318985", - "NODE_64513_length_857_cov_39.677057", - "NODE_43271_length_1085_cov_2.514563", - ] -) -logger.info("End CRUD Contigs") -# CRUD markers -logger.info("Begin CRUD Markers") -create_markers("data/lasonolide/bacteria.markers.tsv") -markers = read_markers() -logger.info("End CRUD Markers") - -# CRUD cytoscape connections -logger.info("Begin CRUD cytoscape connections") -create_cytoscape_connections("data/lasonolide/cytoscape.connections.tab") -connections = read_cytoscape_connections() -logger.info("End CRUD cytoscape connections") -``` diff --git a/docs/page-overview.md b/docs/page-overview.md new file mode 100644 index 00000000..f78bca48 --- /dev/null +++ b/docs/page-overview.md @@ -0,0 +1,17 @@ +# Automappa overview + +- [home](#home-page) +- [mag-refinement page](#mag-refinement-page) +- [mag-summary page](#mag-summary-page) + +## Home page + +![HOME page](static/home-tab.png) + +## MAG-refinement page + +![refinement page](static/refinement-tab.png) + +## MAG-summary page + +![summary page](static/summary-tab.png) diff --git a/docs/static/home-tab.png b/docs/static/home-tab.png new file mode 100644 index 0000000000000000000000000000000000000000..97cedb7041c3e1c1dfcdc7f571da9a9a2e22548e GIT binary patch literal 82061 zcmZ6z1z1#F+cpd$ASmE~2n^lb-O?#t(hbtxjdToylt_nkDqWI8cSTY?0pTU~(?7(?<$yZ`1Tq9UNihwtjDuxVuQ!@_D8D~vA>>}J_{vBnmF7jts=s@u z@mi`k9=U)rDnuF>o%~&{?&pcO*dPQo}FjpzptM|LyR>bwEy=em!kvPzuKgz z-5$~K_;*X@VHcD}-)e`1*xA{!-+D-ii;Hhe7QRM9YhnUHLD_=wN8>UwAisS3MpV>Dd70>6O(lg^_fp{GKz9xnTx+e4MVg!*LiF1_-kX}4X_gT` zz|LxF*h9pOy-zowzkeIktB`dhF{?|1xr$i}6;iQ9MMWWt|DL8K*J{wf2#bCr-J<6~ z5)vwADGH^`c-k90JXT)Em4v^HCfIM@P=%r6L5ET$Hv1>cCaX>SKTbA6->~Sz^B8g;NNHXJAQ zDx?hTPFYcrlatHXqvmBGz?Q7C{l7iX{@+yQ6|mu>Op(`s1~Asvam*hdLTgVOxLr;r zsUt8%MMOkwNT_027(Uc~dPA?KbNU=P3ewPU!(M6F#(;`Nl5n>DsEMH#_;?RtGCC>C z;B!_JR+c=fv7KZ2xYSr9^munRSs;f4=l$;x?olyhyWBhoB2cpOWgvsiaKeqfnPT&y zmoHuE>vuiS8LNUm>GB5T|?$~Ui+kvi3Y9R9E|Sh z=vZoXR?nlLEHv$n-t(7C3OG4A;jkVj^S(W7n33(kBo^rZRcEV-^tUq<)uJ;585;y5 zw_mKsb#%lWr}}4sV0{o$*5kIzETQvp3AJyE1P;^`6sJ1%ZXSg5kx7Vvsn-VFqwss5 zCh2gF=3+N%RisZ=F5j!Q`|+Q^;h#TUJ@4FM6@Jb{!y=|d`U+ZNt2A^f@n_Ab`iGmf z``0_ytxpFl_o@Hc4R4rB4G9Sds2>6XY(CuFOKuRw?(EOk6(1F-XGuccMr%M)Pmw_41$UXKt{N?_k#Us;h{%^}-1F5Rz?S56qM*X6LdVYLJXsE@X zt}qVEVf^pxZy!5uk6LV~u%*%0+`aCzgnh3TU;oE)p&w^THBT3;vIpV_d8Z|0AtDY- zd~i_^2wEh*h)M|6PfAE2gF#4nUt;M(yTUe(TE5CX{5Fn?i%YHh?m2Y#k~yL%f{2)xfmc%@>WM@2>Pq{e9I(JE&t zx>`*9{$YKh1~jJGK&_BsvE%!2d)#rlbjCXH6VJ%~l7?zf!*y#|^^7=E#9y~myNX4x z!68{GQ-J@je>79DY`aXejHXB_GqL5US)nbPj<>4`Ntp5QPiw?>IWaA;CniJAq~*8Y zGS_XNJnZaP!_%jK3%PIfgRa*hqEs9l90~!c_qUg2=oSSkvjJCsP`NK@+L1t;cFMx= zqzpk%B?HzjLdVwJhf1}==b&6yr7Js0 z7Z4aSnsrMnXYmWZKUb(xqNdiW!w$cUvz@5*Uo^QE+)MmKBRLq?Xw6@D9HI}2O}Qdu zU&DJ#EdrYBy|nys)zho+>{)f>lJ|DzWV8D|RVO(3`e31hnuZ1*Qv__FvU7tpGqe;Y zgKsOgVeYFPk3mGjK1>7aR%4m!kccqq(POt=wa9BQm(rgmzz@*g4o?-0^;dN~d~;}Y zJRVGTg0FB|jh5m}k7foAmTKBgubT?8+ppAWYE>BK(-gZsr@!|&SfUf3y1zJBU@X(B(E5l*+-a+n!AH*+s7DQK93^LB zdf%Vz(cwA;rQPh;zeBMGFAf(Afi)|sG+J(Uw}8bHPgWWT!bMt1aS<+HSkR*GVqqDK zO3{a_{PZ%%h1ytmB{GVE@829i482L#9tV;H)qkh7K0KQ?s$DfVpjw z);#pu{hBl?bTw^e%S$Q?(cjP+d(bp%j$dPD$3{xE%fyqc=wpt$kjGP|QtYeZzL=Puwu z1Y4g6qQ8Pm@4fbnYJ622J$abc`|NsXSrdJ5R`Ue=?N>51v4FMpb>{bj&lL*uys4E* z0(WQ1ikLps3Jo-eg@+9QHq^7)VH~|5D^0m`=EcfQffh$E=y8zr?fo&EQD}Is!?JG) zN)^Vw_7_Ot-_D5AKNj=jou@rGkRfH!5$JAqfMq~OCFDFo(4phK<;abeu*aF@k5?od z8d~L7+yeqX99NZANFTGFqtLwlHvW#&?uzcChn0F%Y;@vNEQh^(%S1ONaxJ?;ATUYv z5GT|%RrEgD_vqR&z4H3y+kyF-_0Mm*7_=*?jE3(t29p_!9#&dBvxTlT0!!2iYp2UU zNO1FftkA1Vf`~r!|GDYug_RxVyYF8_whCGnTobN&1{}h9HP$m`Y^&Os#zZg6zg{v7 z7XSV0v^$X>ad)xkXd}nk`ZvVybbxm2_{-ZjJ}R%ld`>y?>VG>?NsFgFid%m^$V+xX zWhEt(SZ-RKAMU(0M{AFZgPIuBLs9VgoPGz{WYjn+-rQfV76R+7Rv-(y{@aaJ=yCX~ z6lvlU8jeP~sX3$(ce625(6e+u*|GgvI!SP!?KY?Vw;gdx!!t$ zVrJnr#;?3Rp>F> zz&|u2rvEe{9PkjJHq3*L_U!Cxh*G&Of#;GpNzEcgw#_oRz+jaok(ckCz|sf9l>^_1 zAJ$rO;29R?dp)hB&cibKle?%kgaoZJ^O8il+5N3JLTnNe=vwnitG7D$vrZ(eAIbHX z%VkHbhON_u$Mbo;%acXQj_#y!f}w_inm$6a%YF~}^rU4dRBtkFM3=Rfok$^n_EYnhr(^MDDvTVnR8ui;>^1lmBQ2Kc({dE^~!mg9c>e zE3Xl^?#Jx0J8^EZnPNkOa16pc@bd9=}G3=K7kOp6nju(KVmFjw9O8H;ROaC>!p1SYZC zwzXZ%?o`p&Rm8bYFOpT{<6i&R9okb#YofiCwidr@i%9`weOBFE3Dcnz<`F3I`>#C7 z^2rP(w~-c&-+CVL-3L&pnv)MM4SJm1ykya`vUwe|1Z0dwWpuTCj|48GnZLhQ>s;gO zh8OrZAq>@ii$Wf@7q%FDbLOaB$*+nRfBE}1*Y9NB3^Kr2)tWa*!&d}9T-46gt6@I) zBY?Xf?cNtDsVKH)-K68mgtw0xih`lGI)mPUY-Hf3ZzOF7S0miZ*w#RfgdMMG2SX*cU4n(uR{`Wv+{3(S)^97Ndt4)e0Zt zeAiTmy<;GAFX6>`mJzZPZOn zOh|W2dlqlMWzsPh5ea+{hwJ3`y^l*v!-vmj@Sjbac`h&Owqd0(efUOF_MxmDNV#%J zx;?wi8hiWtN?{a~lyQLPApq(P4$*bi#hV6)saZd}p_Ga(U*5a+2|vHJf`I|UvN_aj ze;^k;J4rS2HV|IfjtbZ^i@+jP%_b!waS)k3-bLJVKf`0%2>v|s)5J#>bxjW;dcXIpy# zQ{Udttl~0guwi3k4`03qa%5q{GA-tOV2kcO;3x}0H@E>meSbJ#Bhl-WH1+rK3#Zkv zdlsK_-kXWz?VaydW9e1idnHBFw+ve4)?Bu;!*rUZNjJV-pV0QMfdr`d zlzsI2`fOYKQoBO8Hy?o;Nspwo%e3e)SZ+oPJ>Y&|zXsd&bHBy{<# z66EX$KU!WC(y|;*GZ9%WQKxmUtnI4#v@bW3Y^#(+d+zIGXg6P-dqen$ii%3;x$K^* zn)413J`W_9!((7Uk8+<^Fn+_(TfjkaX3q`vayTDV>NQl>xNOS9m+~PZrCHrP(~B>w zY?m6@vCs#Pq@9(h@Oo)`XbC#Qw5-k(w3uCa(268BZqj?Y zZ?T|2cFS0kc)SBibi93ivV2+7bNGdZ-F7wMikURZ)6J`Pso6*EEQv`kh0oh;S)gvl zPxRp-fZ_3@hDMT|&SQ&S$79$_yb-O_S$o|t5FiQkdfb~S+vC=%vzg)Y-ArOJkiUmC zeFaLlsv|ua!(DId>Ic9C6YK-+QX5dQlDCb#3@9!@_3 z^}A#3++4A$L8*3`<-_uy+>5=ySj*aWl>Fgu%8!bM-tGxP_vZ-QlXCtK@0H@eE|(j$ zXu-qFwX3r9X3NDwQ5dwVl^`-ndgVqPhJ{qsD{OOV!slD!%FG3vKYbJ0k*@!+qmKt9 zol!!B^3?8)9bT!r&Jk->t=8Lr-P;U<<~Cc+17p(|+d(DqdiP!}eltG;Pb~zf^TTh+ zL8_RaCM1E#%IPb)E)-1C%qL1VQco#s5n&rl^FFG@@qXv9f{y_b@*IFba{U-xHDtKIci$kmA zW|Ku0tNYV>1&9Iz3FH#fo>TeW8P|#!a|S!!acA^CQf6OdcVj!?r{#;UwjK0gx!PRP zOF)(`l{r*{c_9rUsmNMwaLjII>E5A#=Yf_4Vb?|(Ty6|p^}jr+rhNLvf|WOq#qfJ3{9N#Z3w8nCdke z`5rI#W@1p0EqqrLEw;g;MxrMd4^^EzgUM{%&Y2DyQCKYuc%u3&k1UJK+M^Mr*P&~a z`P3edQcXPGaXnQ6WYRlwQHzhCWxa~==k6s<+j=8olIGPFsQ=s<$$<-gBn2>YnO5j^ zh%IMxURr(h4fOr!`zT=0_|nLainRC58G(836u})Hu}V&TS3gByAj%Ew0woJQmXX zc^-J}UY;Xd|A8%(nqb0W%4j(UeX80n(gi9H9P!l@B_G8kRK8n|BrdUiEEU%9%QeOF zu0l)>YMlSsi$H_lum_+cH)V=nI2hoJD-lG$atH(9{4cM6^rkSpP1cn)3X*59R!)_X zl;lTZFmsou(Pm>HdUh6qTdTu~t4$=}9hK!>LW=W8v+@pho_;I=s7!bt-JFECcUp2dFrC^fRrAA} z0wkM|2dj$TT*Bd+XPfN+YAn~zA5Wh2RrLN`{fvEDdxwAg^=iO9T-$v(7gX=EIk@Dx zlOo>yP?Q~@TWHYhjf_EOeI;|FE~C;AiGrEoI?+V9jqip~!$Z+@wiFZoA*0)Eke^6F zmOLV9t5SB1yC@vq+l1{d(QKla%1_jWO3Xd}B4WA$s5~^|j7`6a4(_e^9>4b(raUB_ zMJ;g^cx#J2FXQKFK;z)h;ZLq3hEIu1m{X=`FzC>sp@i0^Xv^TV|jj2$fsk}@P6 zE9uoMAFJJJQ!E!53EeTeh;crOq|%R1yc#Eydx7=3X^YjcM#Qv7QdazG*XBC=fE*||UwdW3>w07Gc#lbioW@%_ zR$W$az}mSdj9g5LM@>a_ckL!|cYnJ*Pk)uM7B&7*NEP2$YU&v2@H~=lo$q=<&BgYC zSip^HAj%jP-}AC)vQVMyksUUj!5`J6ygi&n9rJedb5%!H$(BQ22HKeudA49kc6)9F zT0Anv4VI5cAbdt!&LN)R8o+k5%1w+QEK;xPCh-WtTlG$EMy%R)$sc!^!wCgwa%io% zhhC^rPz-)xQ^xY4tVaI7k^ zkN@o=X4@d##kre~LZ&0sUC#*>=<7@NMBMxYC#BvO046^FiAls-wriiw1(TS691+p=DC+B28(%|? z8O7FoZzTD!TmfNn`2p&?0xmKN;kk@-_}#bte)tW4x*6eOS}(lsz0w@e z_)SWJq@EG{dE7X8GSV*ZpeMB>+%_7Qxzxvw{B;a-P_7hdchXP_J>wjUJ~Sb+4BMA!zVI(6zDMO;GRvWT(Hu96YOC7bNkBvd#zf14DFTNDb)OOr) zIGc8AG3!y?-mSxwi%Fsw1Fkh6ZfrfnJMchI+G4}D<~@SA5Ki=g$S*tG@mRh9*4&CQ-pEEV}gfr5d zlRl+nB$;mo-FANZf2w^|Im9N8dP^-ZZkmK8peq={RqKVB>FB=wuE30t-=#o+6^oE( zxMJHdLt;EK^o@u5H$^B=F3QHdW!7kJ+|CX(Y(vS8!0hy5K3e~UGZ!Wc@-EAz9A0X2 z-Hf8mV(Va>TmS;*+%C6Rv#wh~=cQEs*ZdzRk_(`1Nf%WBw@=)t#d!7N4JoP7hicVfZc-mjs3OnS865^(MX%%pi?6&*Mp#5%-aE8wu z%N0+vR6|Z!zHI4hy5iBo0+d`hp%u zi5^%YOh7TEz(FxcqC~-0(|R>o#<7w4$0pM5WUY4tD9`0CegWCzKom~V#sVUe#oaBA zW^fgd=}vY88o`CYXe}OxPgmT_N^27MrY0AWwu{K$Qm}ytpvvBW6Hd1m9-JMrrreA!~RuM7y@RPr46633gMqz%YD;mxg zHR68001;PI0E#}%bxvtUn%OatL|mgVx@A7>0jU?@WCl%@Zte-jawjN-P2<+dI?Q~( zzG5;@SAt%nCXMRgQSYut&+irMiK_5UYtPV_-rZNtw^eJ@R5|-E*VIdYP<=XjQzcXb zSWo!Xw713@I$6Y#Ns$(&1euY~E;6^WdWA0cXW>V$V_1PoZclE(&P+sjp(iXhi6)M4 zV8#EInvLuoiT@9PxARtuYy3E09eizgF002DSbulAFVF9J;aH-Q+j_0PP-|g6uy$SR z^v9Inhs(JVoom=_canafRm^>VR&`&yL`~|9J>&IX9rB^q+8TUo>@}}AD|8E^?}UzN zxhMD3WFv0^0Oyv_bhagUzz-D-tOiJ>OAf0GA^75NSGJo{>xm7@mhpKwT$@vKP=JXL zLw1?uu=KRe#!MRWA6)hcNPj?ln5)({5HHeK70u(fV=>0X)XL-u{ z{KP?l=4l_p@sfk_ag#75zqDi2&r@b1y27i7YJYMlyLTq?N3`Y(B0y zKZr$e3X`HUK*uLK1*}5z&k8sLi_RBD1HQrA=;LUB`Y73H2O(rL?-@`I#O{e#9Z%1~ zCd`V-WWoft+TRuCl88xdM5&~P{BRc-d3)h+;2nEyny`jm^3&~3T;gw|^LitzN=h|k zfzOjdI)9y8Ej1=|E{FVuTYhmkf2Wf{I8?F4^H_^AdfEM`w>d}kEe3;ZS?GNS-T!I< zqFvPh-eH}WT6nea)x~C#0~$y{ZPW^RbAI|+$om;}j;1CW_%6pQxdwUWN(VZ>Zc^^&P^uUv8`O1M6y0wospuV(kJ|G~wzDoUA*>gi@GI6RNWIGzCasc*@C z4-0M%Vpy6*O2o+zNFUXIwEN$dcDRqR{baLOq_Sf!AY-OBv>iE7kffK;CDM@1<&=_^ zCgTs8Xe*YKIOZK)2LSLAF&IigBVg3xwfwhBh?$p866>jDyQ`Co-$igrMxe7V@}r1G z%92RMk)!Xaepahlzb;4 zzWiOh@gO=;%tG!2awyt4|8%mg1`wU@G|IHAQhblwc-3d2{Ug$@RB9-17_~Iwwmx*2 z9kUwdf0Wlfp&$ESem!Fv&`Pd1OkwQ2Ou1-Tt{W``Yx=xJdvsNh2pEuf5IP#D| zizLzomV-V`$l^yCV^PwvQG7hx8e!n(But=daGGnv%a2hv8}Uk-QMrs!ir)>pE@bn0 zN&IEPs|ST@Q0|FlrlTT*FE({NLPJrpH6bkLU4S4Y!J?xwiRL|6ZoLwt&^S0` zZGW5b#E?SpVV>Og@-VqC4lEan0@p;$;`k;V2j=kq^`?lW`l$Vu1^z4VV@A&ZXg=sX zyYEbbCS>Em30%`4@=PEtp-e+U+g*VE;w@|{iL52e9F32YCHRCeUMuCwT|y{LX3z$v z4h|Meq*YD;kG9&*8QEmzs!=NdeUqB09ySPEt68rNd_*j%%MS;jz9&w!a&iW0i;l-a zWxu$xJ?+bmhwbpcijNWiVv7f)3*sX$B_uaWf$fs+S_0a9BVa+P$GntdK7j2KxOht? z!eXeDAynd%y@Nu&G0^Hlt-ss~p;FolpOB{rw{iPb{l{~m-9KW^b@Da2Gh2gt7HaBT+K5|_Dtgc%0NIC z)SQ`_+-=|{87HUe=_+k)o0)R*xk`i2F9V&wI9@%AC3W*jl!nxsU9i(gi;WVJLo&O^{+Pb z?1qQfVpY>ZvqS-r#(%Gc*o)b-^M5n~|8qCWo`K5oTP@|kIz-{SvPAK}*7o$>91;gO zqyJt>uLz<4H@CnK{hzKA*oYGyrFrG->@s@#dYy;<{aOp0fcEya?QLDo*WiRtl>hIe z&{q&iamJJ~<`4gEA8;0JJ8m9CQUAUrN%QUd|I^C|HgMGcuV2rn7XCX}K$|NotWYs)$2&Iwh?ELKi%HGyEH5UCj|s41i-G*Z7)sR4L6Y4Lik zi?-!W^tn{pLv|Q9#aP(-5;%~}>VKsPCUs!ee8uz5Eb z#tE#SUA44i(igXb30Qlhmula!#C@6vc06VZ~=v11@Fct)5#Ipci+%qpGS!sMgMxk9z{$&0x zWlSTTKgs_{7VyjCLa~9zC;#)Fr#?oX_h16Elb127xS2V#ip1FEweyw6S1N=9H{RsXSeU#P^moaL(IQWYN z=cX?J3>&rI?I?8+jnhgF|WT1O{*uCvU^Ac@H7&-{eL~r|IhPa)^0ZGsQ!LEc-wg|{)4ittV$R&DJkiZj^vla8~3Qk z9SONB+lEEa{uWh<6L0$j*(@OfLoGkQ)@ZDim6a{v8LI=bL5lyg87WN2e=H!Yk1ck> zMv>Qd0p#62+>DRJgf~P>G1mQmivsioSQ87@9lCrl)Ik$Mz@r7Sz$ggqFOOlz7=mq^ zqx50sm3IQ4XCd}|YJRW=2K)k8FSEtyLE`SukhiKd3bHEKJ&wq5{xk$$B%6T0GANr zpisM_MXGCtXrLir83h3O-sG>YB7k3{%ztlsET7zC)&CmWCH3IT#-y8(+|YH0+a4=e$y6qk0k+qc;iKq zi=53W(yg+&dBJm3)Utu8KY+$035bgF4|mt!jqSgS_F;}C`M&lf`>L zzsP9h4^MuQ>fPPl6#(iI8(&;{wVo(XbtPj-19u4Sr#($SAPKUtQL?se&`5j`(ZP~uo#Hi7yo0q(OKe26Y(Uz8Xs4g zd2tSiC=>u8Po`mC1z#n=UNYg(C?)_jp)4SqSgKX@OjQ6Br50;FQG0<&%Lp}vKq~0+ zcz>y&qLQFDyJ#p6tisTEuEge)vZyBRAF zKH`*#RaI3>&M;z^gL!s({l;p&lxrX#DV0A01Y~D5Po$hQwZ@zEkQ5gJJ=XoJCfDKP zxR)F!NF1yw$NcimPhF=+8f+y-=M41icC0a*FM7Lc5_0CfT7s*f(NW^s;mm@H5@ zPt~yy;F6NER<+-hDt_f*9!OO2jt@@+_)<9ldKrO{di_S6aV5pw=La!d`<(BPd#-pF z{X*sFy7_AAZ5_l2fZ^Bp^`LrYh6~k+Y_rYUdGh-E4L}-`)E$9E>wP!G+L2V0N%5{B`zV<8^Z)i)Esc%{0k2qmtkYpG05I}^)(&|Nu|h3;Ud?>B@)5$>R)ZXo-;w8 zK;GGYr%_xh6ep8v~y;Ly@F~@^KIaaC3su zh`DOZhWWNKPkL~ai&3cD?mC$Pxtrx!R>c}~<%e-^DJ#|fQul)4-hX^F_j$1`#72@q zHSZHZMl;RK<#Zi&V~HjKgkDK>7mz9!|3I20DFlEcZr{v2DDrNl%11Upm;x7K$e0lH z2Z&!A0{ogYK%GK!Yv=b??fdWV2_Ct?JTX*gHo0sL&pn|%%#VOVRCAXgfd;wCe(GDC zH?-i7qyD@55nX?Nk|3f36V?fh(rgOF+h=ic6KiY9w+E zC~l@b#UaMAIqx1G0ScP{kaGP(k(~;PF)Zllw)%5BT{}d5ILuSeg41@!euLK5tO^yd znWm%vP-~uOk%xT??0I$EDOrrLvEGY!j_(UzPF0WY+_wQ$44feCJ3@BmYyeb`;Ljh^ zgGE|H&n0rS7`%K&uR%Quws)<99@u8bhVXX1&URia%ZsC>?^cjkq$2u-e)8`D0mRG+ zuj`iLnPw#Ne1z}C!Iy*iZh>`vqJ8UPDIa`|xBTe}NU)a|fn38m>}7zivNS|eJ6&2z zszfK)4801c#Vbr2OQSSr4Hq^O38GB&!<zbbQCt&9c^>(Pn|g`*C0 z2kCqo8_!ROC*seWWn8KcH1-8#qEg&SF%_`CfM$9#S=&%`*RL8Vw*0kw$?pStKJ?IP z{G_yOhtKNCNt5cBZ2>!_G9}_z*u4KWZp?U2o%6c1yU07cec4s#kikg zoZAxN7U+!A4xo1q!ao7Eec4f+ropKE$hhDwVu2K8ux_nvayMjd+d8lqle( z!G}oy7ujJTrs7wh?MTL4d=GxIe?rLzWSB#Ma)eqrOK8uzfT_nZY84$r&KjY!iM!4v zEPox$W`jX#GVTbVNN70LdB5@qY2aP%g#?lBP95Zo zYqV+>q+xB$s{5lD%p5V71;}lA99PP-T&6O(`_^6$$a>}AvM7SCHZQZ=|FLDElQSbRVt|eG|@EQN;SFix(Z+uC=ck6XqiXZsf zhJI}Cq|LEEyA@0cHN%7n4{BUFXmvc?l*!}9LdE$So-nh_IW@t+wfU}-7`{j|681M) zfUrb!P7SMM(GKZTWki z+rfS;0iwL#@YR6Z_sK}0?&^4-bHTyvEC0V=WV54=qwX;mkvlfi*v%5{VM}x^u%}F% zLfOJ&gA0&JK?i`eBXJo>L1F>Xk^4|F;GKV?kL@frIF>3zM@F(e0R)D?p#j5ufIZ%8 zhd>}Tz=k<<2X1LWfaqy>Px|JKx!c~foNxr({@0HZpQY8+)ncQLcJDwS3T<)Z<>keu zbK90~N55sZ(J0qZU1kMbjq}kG|CxKfbetLl&`@;yF+ac#fykp+`v7=2Rmf_ibGKi) z=kdy3&E54Gq34owgf7ZuLnwbH$ut~e)=3w$3uN>MeUKO);Wd@X1nS_#$Z~n!L z7b*~3I+dS%KIhiW4K;?AKzy3M=&e2594aEQ1*9w>Kn2u(0r+$72Duv3-f7Qr;AFr} z3y^@x%{D)Y^PJ1A0G;v3m|?TqN6X`_1+BXbe(7OK1NGKk<;*-S;iKN*l6ID(LcE#% zwIaWyo71h~f$?wS&WONr2j4SungXs;IQEIdyaikudv?c@O2;fWH+RcyzAEMoeWE!( zr{%Cp6`=LeuZ5KYZ$FrCKdfJjouAh^t9v>@0NFQ#-TdNR@#^+l23XY@GeC~OOFDH@ z3Zy}Mk3fF-1-1=LMTWUBh!Spl+;S`eH~4!A$dS~Rm7b1t_z;^Wz`Yt8fK(zM&|hse z1KQ=IW8@#<Cew7YH z8Uo;e4(}5rv|5tPNJKohJ(kUT83$x?gmGYQj@7Cm1QQdJJ@$;-ij{y{d-xS#6eBXL zKKm8sklm{YfAnoNBHOFmU{n&>TA{YUwX_~vRfqAV4=iF$XjK4$i?xU_W+J#}uhr;e z!kM+(7|IdGZTNTsBel$sC@)sY4f(Lub)m59`}=Cf8UNAjnvSs(pu4Bn+kD_0tH*W= z;|um#Q|ulEOog^@GkpK@#cSG+WeM-qI4n0)dspP2#eBEeJZJj;;k)HtO+v!!GjUL( z3pP_Rvi&16tC^Zf;D8l{|`@=pG z5D$#DpC{nhQ?W6Y^KVj)D(C4^#~im!3ge+~+eX+MP`B<7&Q)Ys)y_#I90P$bkPW~^ z%(%XnQ1(1elR9=kr;*;HJ*VpSKbNkL$O_&;jEpj_8QsZ@K4;#s$i_aml)w`5L-E-# zZ9Cs*2a|L6??N<$t{isY@rKbmVv!n}I+c|3hG`mLfGA&Mmw)#8{kpG(IpMlz*QLzy zidvz32}#&aE@M4EHkU;!k&!(x z`?lJ)(@k~W+uJ-}3u$S~RBlhg;mnICVQsGoRX?D_U#hnwy4@LwHzT~9ID31>Y9bjY zd~OJ{{j1xG+NR2`hL|5d8G;{#AF}>J_=0^9g_5w!@O?%BK5UYA)i2*?m9me_f!y#n zpLErqY2)Y5;u%}9+F~Zu@hH)p{r=3)rgMGF^Le?y^0QIYYBa-Kp-+HFG(f+XG*%^O z&M43zuK%5%q+Ku1Psd=hm5DnL5XO%Efb^3a`$f%*OXq|2aCmn!dAyA*?k82{v|EhT z&7LS68#n59(KXE9{=gQ|zSR3+o!cIo<%+c*YxoQQL&!X2r9aX+QV;(gx*~VM?ju*}CrxrY?L_fEd6_yi8i2;cK zonOaae17t5an6T`qS83?wf1X;*QD%u*cPvdjOaTZ3uGK}4nj65wF-I`YE69Vn`F$I zBo0aWlt_FJ*tye@ZwW?`W+x&?<&{71ek<4h3V>DEYDz*p-Q zRhCFweB3Bo!X-BO7qA9@u)A+0=RAt6Z}ckiB7y~b(#W|`Bz{;J?C%0Ta@0OulCKkU zq%B?T6V^mQ(Y)z)ub#q1VKLj2U;Iu=A`%{+#13Ye?42FYoeC!ld!?3>1L_WNvD4t8 zXiHG~iXc1oOt!N?J@+Z;+B1Ez3*yQ|LB``RE|)~ksSFy&4c3yCbDcBrERj*k4T{03 zDgRE{IR8!xXOf&;^BDu5yTA*pE$yFDge>0xez}N2d?GD$5LY8|?b#p+L$^vv$n^;{*d`ev_H4TU)RNP zFw7fOq%1I~tt6=pjlCCG8vU|-mHcP}sYKqVQ$2_pzgs&)#?Iz-=tTY?&;BfM|7(lM zs>G(yMdo&v@2I_kOyO&qRn&sWiDVRk6rJ43cSX=n-}9Q_bB~W=p%L-qve10>@*VWI z1u%&)?08xYwH!5YoLF(cbfKb+3N+aLoqpG+)4+RpYH4e4O}I3s?G3RNSu&=Y)5l^J znS3;`RkZ`dTxnGm|5##tkybIA^d4T1kj7v%^U59ylZHkovI2=;4@Hk+!HhUHHiN4h zH6i$TGH(uP`)xt#vx3n{`rsvgED5DZt0sProzb@fahczVfaoznzfe7v8%%hMV2T>A zP1JA{B>&;yd)oRd7#jl%{KPC!aosm>0%tRR+Wd$9GwU81DFJZ6cyY{e42tEjZ^&uX z{MP5r?cZtAQrz(t!cija5%UbOsw|RU#9+APF9&VB^kEpDPW5(aS@uT*I!Cg0M7`el zaZ%0PgrgPlgD4;6p6oei9X(#VrWeuQ@57j;>}K>R^NwPuo6Gz*NKXYtw(JZOJPobH z%A6iY2-V>O4nAtqh#|FbR=-#AK(J#ZRvv$c$=9)#{vu9dhsg@7X~(Gr59{aot!E&;G0_@>`49YqbCs!aF)VE(&QrMBO{J-h z0{G3^*@q!a4@IO>t!+KN{en?}k5~DJ-ivpjl5_E~T@_HU8J6pcu?UXmxGRZu^9dp> zL)d@G_NHxAam^u+LS-#_4$MZL0p~nh7>i54??~vPaPB)yb@vdazA8{blCBe zsvLe?X!O$gPazJin|Vq7y3v@RF-Zx~T8_U^mn)dTtfx|ZRUkVfTxX`q)rxx?-4x4I z*Croyu!|phY#-KS-!sBaL)-IxiAeDLA)+ty+)1O->G>u>?48tyXUScBmetw2P}`#* zGdv99NL)Uc>0@m?xLO?Po5<*7BGe^n9vt|jca`SPuK@>k+?@{D1h8RjzOlm_jtHNR z80E!(Z(L|r?yoPV*eURVu$FW1AGb+s)vJQ?+Y!`1!aC&8l^= z6pMHz{FpEgPVA{P^}oG1-1GYR08tG~L**8ZD{AEF?3Z3Xk@URkd_gB%OUE54H>;Cl z1_p<7$ylJ)^!g=zDL?fFb8$v@GQbv*>MEssy2iv$GmOYdI8zc+RqyL5JV; zsF!QvD%`My&X>M_>yjwN_iaW*Z>u|O;D4CIZA(f5nd}!9(Gn=L^;F+qgu9czP+Gdg z5#r;1&Va=xjun@WZ;(eAdbq%Ao%4*2`XWy1hR?;#ZV`riZ^-4-h$LMnCt&!$T7d1} zXQ0%C5M))>Iq=P&4cvSP48qT!gaU z3P7N6fA%I`X|w|Pa)ldZ^1aXAszxA|XFu8r6E6&N89!rkhqHi9g7tcu0Z%~6R zEI6N4Vk%CMGQ*X&Gxhz=>Gn(ReJbzd;`lZ?P?UbD8F8gZ5lpn5DNP1jgu1|iv*6Qb z&4_&>GlbID9}0`g4-Pn%F`Uuk)^%vz%qQrH4+S&%Lpa_JlHKUXxUx$djis@7+sO`E z^HVWfAJiy2Zx7d{$4l%W7z?K!1LrL6t7Ism?Wxk0SYx-R2nAh{09FT}amsT!d^f(? zLktaqxER5wOZhqRFR^bY-o@u!ijaBMg|8eIt8^j9g=<{OGVVrg6)b{TM+(pf3jcinB_zA+h_T~32M(_`T6>{_?;o^J->W;>fBsq z6}}N_sy5}zU>mRgh*-L8e)i&6>=#~5PiE}93~}WEuB@D3;*Y6rzvMN{zoC3QsBXW= z_fQ#NtatO&YF1sCux14QbU_&OZE;ZDC{hH9aP`H;*o%T{afn$#YFDvBkG;&<&+OmI9zt7r1t=9nkW=;j~Mqu7GxVp z;{J9JozHM0=oEmU%Nm2P{d;&wfnCp1Et(J29`g^s2OYnExkh;5?*{HHCXJ03tMa)` z`o5bCxSeomssfKL42;=X)n{OESr3mI*2nKE7uCvgQBb5v#z_C!)fX7sbP)2olomd9 zp5b!)EXtZC=&K7XVcu*9ukl^2iJ-h86uwyiv`E}I+@)s}Y*F--VBn3d7L!*l81KCk zmz7FYvm$0B;^lk%lUBo%Xm$^Gb~M6t=Izn)Uq+HmjXw@Oj0ZvKCu%x2wkwL!hC*MuC9r zc7CD6I47C|BXlt}o3zju%29pZW~_?F=6B_E=H3`7v5Bo#oip)*-VRXk7W@3>Pyd8V zADw_ih^H+(5cE<`UtjpNpOU`vNq08$So}f^(_TM_@9Q3lD`_q*8AH?Gh2o8$x9kg| zK1%Crn7DjERUDL!y>vF(Z@D#Z5Tb-XXifd zI0%H-=+s>=BPFCZSHOL&Q2CaC-mrce>!dzLo03TpTkDaRHwBMD!c`6;!d1D}=)uK{ z7_UxrydxYx%`gu;V1!mAc)U6mgLo6o?U=-(qiR(DlF+3*C`%I}#i+N|=xoo{HLLxu zLLYX`r$QfR_I79*Rvk}GFSaDH{+XmRG+Nn@b-CEwNHt6zmy65<6M-l-GPFcY0(}i> zwhSO&7)Hq~clrp1b2l)rveFtbl1G8Rbpra(3~NZvqD4%$OPfMJ!DcVC7)mJ>d&v&| zY;H#S^Hvq~$tw!3#G1MCY7sX(bUH}KBR#O6cSleh-RE{7Z zAYIbk-O_A2_NKeLJ5^G;yOiE^H%O--4bsxxxrsY?&Ue1=oO|#50h|3BX6Ak8&9&CE z9wlJXne_Q|CS$fXeyYAQpEflwK`j*NIrQ|C;j3OE@l_x&%XMdR2IXmd_)O$zHjy#> z{ErX*cO3L)AB05^XiS+U&?C&d{K6uB_DeR9@xLJ*jq+zfix3H;nFv2fVJ}^F>p-%9 zk?z8@(~<5-!u@9pHC#V&fal5F0+-L2I`QxVgBwyLw!VL$g42xIbZPp}!wTJ5&Y`@& zMnITA>C*&8-O^T0+$y_fE>;J$@GdU+UtAJ?>VU2SA|txjd5vwpainG<+}z!qd?llW zvV9T@gf5ZuaOfdxI0*<_y`P0!4;1jeNjmIEbL8^PN(%%BQK5WQ#3k@K!dG9*n)&GQ zWF13f5Uqg^JHkcvpPju4T{FCN;c9md0=b z{s`-j1Fpc$mFkzpa-rTKj9O`5$A~a~-qbw>9}v^O$t7VCZnl~mFJCjQTT~s9@KCWi zp12TUD>v}1m6r%Ge2rAbWuid+K_{2%p4p?{V}9HCs}K&XEMo)?gIe;`D1BU_Q<4mS z@TO+VIh0GUEo183`9iJZ_+ubK7sC)MNo|~YfnN9X(Ly1pG+W%46~t39b2mX6$>@}R z1p8M=0|oO!huRClaT-=)0o*icsEE$^qG=tK3ZwC3wuxgW_}-Wap>7{u^30ceA2+zo z-|%!{hN%d5e{Xg_1qa|6Ap7Wo!P(DVn;)MQD1i+~O?WKG67w@9kCH0m&NC~=$i%@x0xF3u?t>pxA1fIy z;M3qr!bgCJd4*Fl{pU0`RE~yhcsyneN-A<$^ZnOE=C}5o!WF*?Mwf_^MQR zS$noyOX)r+IMuW}RjBS@$zLshpf}-~W_hyZM5O zxcP*QF888g>rJpD_W;rj-$(0@YF{?(MR0|?*y7loBt*L8<5=yG-@{|TxLF-*w6qQ$ zcfk%%wQ+wrPfv5+Bs>lRd zXC^!?q_8{o*BP#V({b~qj!YaUw|alRCj4pqgk;S}>)cI(_htP6ldTqh@ZK<;Uu&6| z{SSdT0>{ENBgx}8g06DVrEizYiWoI__)3I~tdJ`fDB=uu3DmoEhSP{PX@WZWyy>K2wc@XtrL-^k8rVZiB zplJVK_QGxkiSmVL2KWjTB*`RELJVqZp>=u#C68Zdzi@sbP9e^5u#VNP)O~XWS;7o- zJ6+$uu2_;w;zULYQwxlc#`z*RIC?Pzb?cc~x!wFK1lRjQiRO#qw{)auTy3O;ISDMU z;(M-armor30?vDU)`z)USTq>yF$Z39*^EgU2b{M{BC-uPrWpKCAC5y}HB2?73?n!Q zT73nRk%E=c8n0I;B&*v+NWim76w-`;I%W7p?}OsRHk$3uitghYh9I`X*M*h?6;)@b zIf*3YI#9gtFk@1cDD-Gd!{xQhsEQ=Ol^}Tp3WKoIX{F1lhH~?xDaeP{mgEji8w$|hcB%oEGol!h57hMd z@`;}RG^Jr|`J-Fjc)u~GsV`zzMI6m%1)TK7v$Rql6_sw*9^}pCwBVPML%)U^*lJ$p zRU7qak>JoLat+RkV;7@{Kc7b!wi+4AXd^FTcB$9Zu>TfcS=ltOy}fR=i6`+x;Xul; zq3K;hjx$mm8vbR5&z|YqKess->w{V?>WJ~VNvUMCjDY`3h+Z+NIJNPMp#}epKuOww z3FH}-E%ncA@=5raN}G@hrIa#e5)v|(9M6|zJg4qJfbyfF36NOX@F&Ex%CGi>v$i-Q zl0Pb4fNqolhAG+rTBEokGBU||N4S_Zgb_NO0L0Kk(@fq2oUNR+nBj=N)xRzQsI<7* zT$p}~1KoK5`A?H?n}}@JfK<|AotS2i!;YER%1wz7tILzTO9Q#7&ag< z;U%e~^Xo4oC?49WA+1+{>#9rDGyo1*n5 zxDA3gn#jj-C!v5?Z9(|&XB0yYC5YHiW))0yd~9xt$fmR-R!1xV8xleS(gjAg6#>Uv+x&J8-%;D`WNBysm6PU?k6q51k zL639#e~vzc?C2Tby8m?G+2jli0(G`Gngerl(zkcFWK7?dR$K85C8qTD`UtRj`xdZy9pHPA z9XNX;=G$X`cZSiDHT!VMXw@>1+ZyEf{5blF?}Mg`{`=1&bEQl5)L&FdAVMvY?YBG6 z=vkMa(XsI*f6&KRoW*z;Jbarv?uVEA!op)6MwX>ywSElHr+XXW(8a zz`ZDed-+xP&3~>Sy*zr9MT&|#6@9ra#*Mb4%*Qer^*mJC!Z_EK!lW(sz*n65X8Xqd zyz$nP8mt|+{vCa5>b&0NF0T=r_Dz~<2o`l8Vg;RlMpgZjHXl+A;O`s{*g9Jn!7RL? z!~xJ04!UNdJ(*mhc$r<+2s7UNM))cb1}dO~sg!Tc?3p(6?UCc@7%q~VmVFV5EhVTvO|8oyF;Xj8g zDZ*&Zr`zv-#1>0_c?P@|EiAw<5Q{-4#^3*W9X%@vi4ubjg8eQ_`ugT3ZFk7qn5Z9i z=P;wU2_KCyXq6P`d0PO>6b54AM1odY3JURieqSM*#jxQx>yOTDPSm2nD_FT|h#Ex0 z!`nSOIr7wLb^-wv9>X7W_yz|j{^G`8%Vo|-F-O5i({|X>Mt!lQVY$@Hp$jhc1<4s3AvYNz5`x9D>3UQ`zHPS=QWbKyDKZsZ4d3vH9zcPFbdT$S2+lN(lj!YON_&2*2Kr8 z2-CHHuBRSGB~etn6e2+qj{?V~*;JPJ`X^D1TG04aV@ z?@p$689}?4 zuDn`|T5;NcG{j^(gtNLZx#W=7%p9-RRT+)OtjFkKTL^@jX8I@*Hb3Y9HW)21kY8hs z@l_P(#1dokP2NPArbNc>12~~OLQGn8a+&~woQqC zxq^gO*S98bQcxeV&tb8psg&&j3EJ(Ri4LyltTG6?*gR<2!4EMcTn}a-AY$=dYJnm5 z!HDGIkAl0z@$oMYo3B%zKffB}Tza4y^wp+k&6A5W5DV=a2~8Nd6da1~J~iog6&y<7 z7G#t<;SR@}N~|DVQiS~xaaaf6QjA+BaJ7X#ThDFZgh>NdSad&y6e<_xSv>!CALz&n zE>nRl5-_WMI$f7OZc5|!lnac+VN4Y11YfV#U}eU7YSc+yG{zZxADG-V0$S4d>B&-c zZHjQBY+R%spxw^Z9)cq|i*@CD!mx`s5TuX{B}f3xZ5RO{wDOv%v<|vyGWz^$M@yqs zOZ~hqKTfFlhZ_6I!Yha6ANuE8EhUTpEaR!jBor%+dyQP03wBEx2j8nb z7G73K%%>pXS(mX`J>V+ks~|zLdiNJON+!h43-oRKY2n}trk6j-VO$MTVqQ%@w5mUY zgEQ{Tqu(%1q?sYd(D?)U=&f~Vkj z%>ss=jO{szp4Yyb*4Zc&zAV#?-D?8wOS6Z z>7oB@UJoA2+59e{AWrRmm&V-nfuF_v*3238;y$O)0=D|=?6j;k_Qu(A(7%Fo?SS_% zDszJ&>JA>AQRfJch~G!pt&ED^3V{>Q8b)b6WF*HUT z*;`u2X9efl@sd!z+^E6B=At>^(NXr=;6`GH#P?t%bjUg&BPhaS()+R#pH3@J)<+j2 ze%EMG@>2pBTncfcbbPnnuPu7O+}=0E#S7)sv#|V;TX86)q*zC}!plARVm5K-7z>_{ z9=#_5%&9dWPpc`;9}&u`-5!sN@7F4@Xe0X~CK;O-o0kO*kj>smW76s5(I`+h^9oz2 zlu?We!j#w#EHvv5!5HpeFG3#@QZXvvvN7APmm*KhrXxR^?R_7+}`j)K3WA>`$_eKR>cRa?lPjTLlX+!Ug>(lTB^+_wOUiTWr4S9 z1S}M&?*+mLJ_Bn5xxF80*RuA=^x*OjVR)=Egu7E>8;lw>P4_=bGDKFDGWgt$_JUo( z#=Ztx>NiW9fH5tR%stn!YZd<36~KYuP889Kg}ha{%-mWaJ!Yk6yj`zjhdwP-y)4~1 zMP;RsO$KCq1FCHA!m&^Ce!}11T{c_XV$7L64YMl`>VX5l^S$h5n=~^{2&YpDWmi;G zRDv44f-H7eFMr6NT=l#lP&md6*E8drX>rZOuXVBjy?X3T5ouVw%b7(mqwzM@iHC?R zI7Hm2A2XNtO{87D(-QHB-(m;esRRqvXI!s!Ugkb1M-HZ(4)&;-kK_*fXK0PobPs1hTlP0arT^+ChRkc9qMA z`%PWQtILk6yS4FT-6FyQq@8I9*5HT5r2?4(#S*B~{#DdkKl<6yCU%c+COWnm=CHl* zNpI#rSYd2TF;>Ft*#!kO`l-jcn`oX4Oe>dm>;!%@*163#uJ*DzAzZT-Dix8Vd(bxW z`j6Lk*S*MRm$6YvXZezqSOn-r9_O>tyLF;|3K?1N-0k1{p2PImz7Gpt7U)>|&U)~F z?KNoia)*@l();d3@q1ntg`z29#koTwmwJ%!$kc5*Z=Aow%0B5ndRZ2Nf z8>J^mdG55va!R2ky1lnoQyc#ayz%&4N?&XXk#gj!aKmQo2o#a(4$lG}nLu%u!?qpm z_~JNW6Ltw1gb8|HJ|z&V>3Mo9m@0?xiue_%;7elg8O5_tY%eNK!EfJscIqfRMZD0- zEC|LZ{PZ=r0K6@m61k}P$1Ccb!{*b4?=~F$6nkW;m9r0rY5cQ1-mOFFo`J{kh$fbJ-cN-Xl*2 zk-~0m0P|i7WmM|T^e%GVZXi2Wv_aE|wB_c|WU~yh6?ni7ip1}33{Q!njYDjcK;xl?5p@_*F_Z{BqXF=I$A!et}oTk?fUy4tM&| zdsnYM$6DxmtY4m}A75HH9ZuosdS66Ck1)zw&s%rSZ&&8ZFj9UcDRLIj+-=`ORD(+MG92X#35?W`Iwk zw(pxP>u8E5N(sJtw1CQFJ_pR5;@yB!^!pRu%}II~i-}LvSks!;EC1dLK#}KL(fX~X zCIbo;VM+-zKKc*M1y2*tx-+E{vVE*xdpPZDQDOMs?BB!fbU@}kPLZ~77Hhi9?`o_9 zXv&=r+`dR*RXTOkK;9`+`+IqT^<|X2)jR|HWWCIZDOAeTBdzCLp_@B_+Vs-xb`wa` z`WMZa#~YqRm??>C1qmwVDoPC>ZV?Z%ZcAZ_XD!&F4;n9Zbp@SQ%U$-Dg~y|u$ful} z3})VRiSw#vF-jrem-g8*jF)5T?4{f44kY1!`BGzTj+gtM#cJ#QxmM3f6dv`I^{@T? zk_<|6$~m8DgNS{&)} zyifT}zTdsl^@hQvaF~a2>bUCw#=gd8`G|~E9EmxspdE_#KFd-iPG2&l$uo0hFl}2w zfuz-qDeZloXQY(o>bjmMXl3jt+E0!5iS~aygQK5&@4*8SsxZ87=B;kaQV?oob6^1Xg4}F_buuBv9F$%b$8kcRn34Tw5Z;P|_zMAw>{~wXU_^e6T88 z7r%}Yf@9j>Jsi#U?1wdwQOcz$ajHZw6}{Iu4}EBTpzyfdiHOa&QGh*+QJgbSA#tdqWUjfV#)a6wdkAFRuc`#X4M%V9pv_7R=f9wh5id=4XpWY^nk1f^k`LOs`3 z@7V%HIQFZubCZZ1ndr282i9!G&X*RNnvS;8gV$?oRkdEL!UxuT_do6|G^GXwNppUX z4&IG$3?8;#t?{!J5cx&jPBpRUniq1Orj9@uPFr9odbU1abR+md%F5t{#uezNO(-ox zCq^QQxQ0ff#b?Uig10{fyosZsw4^3HfPH)syw!$jQq8O4kl;u4sA8dsr!#C<(wWn?j@n}1{-N`}Xo5I&E?TZsrl&gvly zRNfr;4)hTjrQYwM=oyvgqET)qL+B_)hTT6?>OtA!o7&bqCYvm_ADEFL8ohY;ySFt`=*6yKU++Rc7XT{ zC$O&1%||KXTCa5)cKC+lrBNYL%vsE`w!5b)WeXn?DI)4hbQQ_Fakr0;e%!el0`|$+ z_SbJFrKNjJqS=yS2$g#*Z*F59(gdoR1OoH z<$)I;?AFL>e;$;35|w&$@xra&(w4wZ#pY;E)j<-`9%tyR38xBi%C?JXa!mT=Sa?Xt zx1Z;!Ou%@21;aS|8m;2b;&ua+VS=T2uD541W-?8#$TbQ(2$6W3 z_*ux=bDpJE{2!MF&W^vL^D{bMSZfy9`x&KzcCQY!d5WG*=OpT=&cQOR=OBo~tyq^d zY~fK+dhF%9SDzViG)&Bs8U4OX?=*C=f}k%BKKcbQ(+JiKjTzHcSZ}t~=g8Z}^GGWx zcd^^#oPczF$c)Rw(2_t@3qe;OlBxO$~G@1U?YGGG@#oMY+oH8OJ0boHCSf zH|YJ~<9)Pi8+yDojy}PK9x&O(`Z#pUw)SQw=ZuX=?hJ4) z-Th%bSFHpWJIICWxcTtagL)u*Cu5)_*ccPBGRySCikx#N}4zQ5im zJltTLKhkl~0)yNzfoxs7*F_LzJ7c6ypRzOEH=O-y@sn~~KN^If#K4Grlpu2S51!!` zvy7{_={(-JvAVvR3vTf}WN5s_=9Ei54~BpVja0b1;bCZ5Yj?~#Ed-&s;nXLPP1*gV z!k)|fhOHH*U%O*YvJI6AVRwkV9<ZkK_2oWp89toau2a8vrD zdH0Oshl);bgP=2>n71xta3GqfkPBg9A4dHKIp5{c{875n&(O6Nx`&h&U}ZC;c~k`L zYPC9g{}?+90S}({_Q$@W5i{IHGx(VEa)06r|Heb5!uhwD)q~3RM4pz2gFl6m&=__{ z|7k#Sy^c_}W@3%erxCkeKps=o+|m*i7XxJ&3zzAYJ{6Kt;60Dp6zg{YwW}cTGLN4B zSo!2OX(_g7J6Rdf2x1&r|J5v)&U9`zU;a_s)f1_;RiHjgThkeRrK>*{8s)rA?hPJb z+a6h1qY`M3 zaLB96!-TU?LH&hLJ4Fo}JJTd*g3V!Lk{=9*jjS*whfjk@GUn%0CHTfxC^*k`YBTpj z7x|lto(AQ9Ae#eHN7yKH3Pt(iOG;}}CrQ)Lnj165Gmky{ArALzuBIk8tinbY^okS) zLU1~1ny3a(yh2nqM3D4&Gn%c5<>u~5dOU=~E->2SI-gftN0&$^pARggoir}UCbf)= zwDEa;(2n;tiar*Sm(}e#s^3>pQ`4OLRd>a8Bu7bA5-v1lDf-=5X!bQq55PCLlQnD_ z^OJ|ipOx4Ds{CSsvuFNeIo6j?vrblVjD?+?9&SZ{oi<8c(`JB0ok)xsfaS0iQ7c3e zq%|B&O*aehP?g+CI4z9cIPBKOSfgX$d>OXprZC$3V^;hHy!3iEGPh|C=chg1>kKbU zboB7RbHmBy+e$BoPVq5}{^bo8g2MN)6Rh5zmo(KwSk>0q3F=e1VxcBGCfQvQg;r2> z6LaUq&0jiw-}}2895*Mv4~%BtD;wWUlc}RlNc7|=H-1g4&B)C zb#daC#*ebsGis8u#W!DPjF*mKK)7zn%FU0;xsD0houW@6C8;d^smhTO__CtHOn=%F zq+Q1rsyru$n{>7}3dv24J1zEYTdY6dz?<}mb8J(u;)>50n(Kw1*5Q`v<|?|m#z4Cu zt7WrN(}n*eLp9&h{xdPvk+`U=;uUw z>|#`~T2^C~nBs(Vav+iU z6);n#5sAmtxiUtP&BF?@0LK{IYQ01>E9^FMUp4^3pfsqs)%TloHjW_w`>}lBA+7 zd=l%uU+~@}oIuIUaIat%n-{w1kEJU%JxnkWUK9dQH>F)Oiq|GX89{JC$b}Pa@c;s- z^|>P3Ld$-E%Ij>{656g~Z121mH6X9@o4S;ZU&^wHnY$GIB!#nFgkUs=R;2QwZ7?Bn zfRd6DD_<^CNYoA6t9dXJS2zO09Ea*fvj!9+e-33ELQRGtT1KdC4TquX-<9)ra)*U) z$exj$l%`0G33x7msMrxwlI^au&ep(GeG9Nm!!pqG4qQ(yn6%8DUwF!|N6Oz=IxezjPOOk$BP%?zsuD4g9g3ZhKNDZMG25ka_ zu&Tk{5k3YE?+p4QQ)4RJTh+*sQnA!XIfwTbFT zTJtUYqoKhas*n&LkL2sCJ82PxwH;5%I8v-Jy0MXrii@Up;PmE#24)h0VJI=F58UF< z;$;A|8c_e8j$rXUJ@QA&(EUS->SrS$=qk4x2nf#}AbnB(q-{WY?%fN&*X&!~gqIHF zF;kvn4z(mF&8m~%@o9ltYoA%c0jR-Apd@fWNpKB=Obfzh+NFN;#7|;rY_j}lD6m5C z`>Ho$Kj|uz3=Fo*vl9VlJ(kyS@8~t~Fcx7186SVc!NK$K7EVEE9*BQ)pumS{)0o9c zDB9uFWP||>K)<8za7%;v4?{P7aB>Ate#Qtdpgo@Gm`$2zS;PD4d zVEjKEa*RkgMX((nwuT3Bc1%>iwfWwZbbpStr(hbEe>A3oUXbDGi<&<*}80<4>7owz(Lu`^IYa zwZtig$#TDqj9@*s+z%?KIZqOj(moXzTV26m9ldjN3E9y!I&yM^V`HpQsixv)lJA5+ zJzMZ8Vs!x%0Q3Mrn*tqsg&SxD@jxSxkOkOu;E0zk&4iH0@Y!F6s++gC(lCQcq+5cS zCVvG(1NZ#msvWspXcRF9o>^WuIdPAT7(+YM_Ltv_1_o8WQwhjfw=z&d7&bN{X5o{m zxp3qRBbbE$pFZ+e1J0)S)AMk!xali z@to>IWTf+`#}nMknN(`lv|=6QhWWYS7f^w%IM9Uu^Ys7ej0LZLH^%~+$>8Q!G04Ii zF#$mVV87f#u6*>7cdV>~+uMU2-0@K#is?3em2WTe^*K@p&pd(XS5f~U5@Kd`*`j76 zTo(G|JefI&l9F5h4r z5;Y)q@>!bak8A+x_yCI4?VThIT&dNx0k_=)4Cpa1;TW9BKD0Zl>V8?|jg9kHtXuH= z_Dv&6z#2h@LV96tu&m8*-M36-#Bvhh+qe5WJ6MupUw?ml@UCr7x}k*O)eoVht1-)& zx`!tMZo7~)Sx~z+3ZI4ow%zziFo1^ZlQ-wcfTXhrYjBA_X9scSn<^>>pd5N^mh!fny%aoKF_??> z(VlXGx64R61F}t0NYDm9q(O!I_-PcFH2e+S4#7qo7NWGfk1qVrVW1KMf@J? ztm{``u^Z-lOI&R;`z#2VNU?W%p)Rt${EMF*11a3}Jkv{gJ}<$Wvx5L#AI-4Hd>P1T zg0auesQuRbv9QRTGJ-KBTFgAnG?!tJVI@AxcK`*48hTtA`-OTRCNBB(_A0l6du3#t z_?wUBV}nao%O+-6O&3S541U(_q=l4X1z=I~mE7}mJYo*%@$4>KEa$t2Ope1|&aa*K z=M}wIzh4S;5c$$~HrdLEpXS-`w6rCZeA~74{Qzu^RvYpW3_k7+I$mQiuQ+$u?SALX z(#>3~H5C6Px*zq1?`TVTN6q{C+LZY+Uzce;w9IO}{GPGSS69PQ_stTfyue9Th4BPGbvfCI8C$IJ0(|Q^yy{2H!nAgn2~Z0pWhOR=Z`X z?XcTzt=)6t6vMZyXZO&npp~>@eG-eZ;M@W8ap&j^s06k4+)idUk4I_=YNs9c`kBqn ze`+4;_n)+l=DW)C9UsB%Xme-ed&B%187#)?iFE*;+dm+Hr-dH}01ba@ZI}xMSsFZF z#N;4EJvArD_3Puv>yj0!r z18iXU^|)v4^VVcGp3m(z)~)Sy#wQeKf~D_C;o+$d?N^k9;`={elpss!c*Zwg0dDRK zc5^U8@r4ihM0i0!o(qE=)uqDAjk{Y~78cgyQQoqZjJaOD{z?okueGEbnS_2t+TvM9 z-;U3*l*c`Tu@kZf7)4Xi!=JIAvfpy_EVyFncpr10moFAvAB`89Ez%)p`rb0&a#{tW z2v8SLNy!TaF}B^yVd$ly;)A7;bToBX!u3c48W)=MGFUZ%RVo6p5_W8=Mp^R_esgon z(J*{wfoKL?U7t$?sU&v>o|S^1FZu7y#6g3sq)sOuZakj_)!jk#3IuHy-Vn}iIEL>)j=kg||GJM>`_+sE5wCLPiBI;S_IK<^Q}w`)g! zezX@GS+FW>(=pq|I1N`9-ueoU!@7N>jX~+q`*_mTf@siLs7(N?FttWSseeDXnUT}I zWEwm1;xN1QeC}6Uxibt|#_fig?`FAWrpi_jnTQ)I09$U$%(hNdaodf@vZJ5|5-B~H zT~X=n0gKKEY~HYL75ryPj{M4#8izKTI5GYL>Bl$c+yNpA*&of&=9;>aq0`rGM2Y_ez~)VL7&pC z-#x${AP}qb+*+efc*G{5L)JngWxonyIkh^sT?G#1ZOE$2-!+jILgE4Jx2!VREYyhj z8=Zv!?=Nc&2U1m=4yliYLVjvP$pT17JG})X;!Eyr7Tzio!`e-#OSR!U%x8zC3-yLW zRL?QYt(`f@A$Rc|AKnT8s zw6!@Q5R>4hKD#bo@@_oJoZI-4VJ(a(vKg?j@g=|*sNJ9W40wnZsElG&TV`v>=!{J} z_(|K?fOa_w^+Ii@Uq*&CNkHnYZ%kA7FDnlPulJ;D7D5mZUW0qT$DrB73|1RGz^<>L zi@2$$`LHd&%PiTV1h=b?P4=*)RLG0>TCqLQ0CP!6rXBx)!cD-+l8WQ|_XjJnI)-#R z00;WfU-dQ;{U;4cjt{0)MkVrVsS6}+-nJA~$=7U~Y z

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 literal 0 HcmV?d00001 diff --git a/docs/static/refinement-tab.png b/docs/static/refinement-tab.png new file mode 100644 index 0000000000000000000000000000000000000000..fec6039913b020cf92916a2c6b5af791352c9ec7 GIT binary patch literal 850694 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

+II5~f`+vl|otx+rO&?V>O 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�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

$lc-)HG#kM(4SN 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 z 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 h%$r7+! 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

tN3>%Gk{V6?4X7o|B~G_fFZwl=jh$f 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⁡$En!sd;$3pLdp# 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 z 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᷺l{I4h3WjAZ?wd<;J&~nVME^IL=mHx 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

wHG}>XlgDcm|?Sv&i)D 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�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

<8y7gepWF%MkN;uI3 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