diff --git a/backend/analysis/api.py b/backend/analysis/api.py index ce4129ea5a67338cba95ac54298a93b72f0c25b3..ca66b6195943b77b05303d931901e8c9234ecc3e 100644 --- a/backend/analysis/api.py +++ b/backend/analysis/api.py @@ -1,13 +1,11 @@ import os -import tarfile from tempfile import mkstemp from django.conf import settings -from ninja import Router, File, Form +from ninja import Router, File from ninja.files import UploadedFile from analysis.models import ( Analysis, - AnalysisHistory, AnalysisWorkflow, Gene, Hmmer, @@ -101,7 +99,9 @@ def get_analysis(request, analysis_id: int): analysis = Analysis.objects.get( pk=analysis_id, analysis_history__session=session ) + analysis.synchronize() + analysis.set_stderr() analysis.load_genes() analysis.load_systems() analysis.load_hmmers() diff --git a/backend/analysis/migrations/0008_analysis_stderr.py b/backend/analysis/migrations/0008_analysis_stderr.py new file mode 100644 index 0000000000000000000000000000000000000000..ee93bb9b9d71f2e381f38075bd47a682e89baeb1 --- /dev/null +++ b/backend/analysis/migrations/0008_analysis_stderr.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.3 on 2024-03-05 13:46 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('analysis', '0007_protein'), + ] + + operations = [ + migrations.AddField( + model_name='analysis', + name='stderr', + field=models.TextField(blank=True), + ), + ] diff --git a/backend/analysis/model_schemas.py b/backend/analysis/model_schemas.py index 1b63c716a4145fbf480537cf20e1f9eb8f140ede..7df65d0ca7459690aacffb89c290dca4c2b22083 100644 --- a/backend/analysis/model_schemas.py +++ b/backend/analysis/model_schemas.py @@ -51,6 +51,7 @@ class AnalysisOutSchema(ModelSchema): analysis_workflow: AnalysisWorkflowSchema history: AnalysisHistorySchema percentage_done: float = 0.0 + stderr: str class Config: model = Analysis @@ -90,4 +91,4 @@ class ProteinOutSchema(ModelSchema): class Config: model = Protein - model_fields = ["analysis", "proteins"] \ No newline at end of file + model_fields = ["analysis", "proteins"] diff --git a/backend/analysis/models.py b/backend/analysis/models.py index 65db950209fa1c38c8f789af5daabb5399e50f7d..3d42eb62957479fe821df4d14fc2a69c28f8a6c4 100644 --- a/backend/analysis/models.py +++ b/backend/analysis/models.py @@ -2,11 +2,16 @@ import csv import tarfile from typing import Any from django.db import models +from django_to_galaxy.utils import enabled_cache from django.contrib.sessions.models import Session from tempfile import mkstemp from django.conf import settings from Bio import SeqIO from pathlib import Path +from rich.console import Console +from typing import List, Dict + +console = Console() # from galaxy.models import WorkflowInvocation @@ -16,7 +21,7 @@ from django_to_galaxy.models import ( GalaxyUser, History as DgHistory, ) -from django_to_galaxy.models.invocation import DONE +from django_to_galaxy.models.invocation import DONE, ERROR from django_to_galaxy.utils import ( load_galaxy_invocation_time_to_datetime, load_galaxy_history_time_to_datetime, @@ -124,10 +129,33 @@ class Analysis(Invocation): analysis_history = models.ForeignKey(AnalysisHistory, on_delete=models.CASCADE) params = models.JSONField() datamap = models.JSONField() + stderr = models.TextField(blank=True) class Meta: ordering = ["-create_time"] + def _build_job_id_to_tools(self) -> Dict[str, dict]: + step_jobs_summary = self.galaxy_invocation.step_jobs_summary() + job_id_to_tools = {} + for step in step_jobs_summary: + job_id = step["id"] + job = self.workflow.galaxy_owner.obj_gi.jobs.get(job_id) + wrapped_tool = self.workflow.galaxy_owner.obj_gi.tools.get( + job.wrapped["tool_id"] + ).wrapped + job_id_to_tools[job_id] = wrapped_tool + return job_id_to_tools + + """Retrive `step_jobs_summary` with details of tool used.""" + step_jobs_summary = self.galaxy_invocation.step_jobs_summary() + detailed_jobs_summary = [] + for step in step_jobs_summary: + detailed_step = step + job_id = step["id"] + detailed_step["tool"] = self.job_id_to_tools_perso.get(job_id, {}) + detailed_jobs_summary.append(detailed_step) + return detailed_jobs_summary + def download_results( self, ): @@ -243,6 +271,18 @@ class Analysis(Invocation): prots = Protein(analysis=self, proteins=json_prots) prots.save() + def set_stderr(self): + + if self.status == ERROR and self.stderr == "": + steps = self.detailed_step_jobs_summary + for step in steps: + job_id = step["id"] + job = self.analysis_history.analysis_owner.obj_gi.jobs.get( + job_id, full_details=True + ) + self.stderr = job.wrapped["tool_stderr"] + self.save() + def read_fasta_file(self, file_path): sequences = [] if file_path is not None: @@ -251,19 +291,16 @@ class Analysis(Invocation): for record in SeqIO.parse(handle, "fasta"): prot = {"id": record.id, "length": len(record), "strand": None} description_list = record.description.split(" # ") - print(description_list) if len(description_list) == 5: start = description_list[1] end = description_list[2] strand = description_list[3] - print(strand) if strand == "1" or strand == "-1": prot["strand"] = int(strand) prot["start"] = int(start) prot["end"] = int(end) else: strand = None - print(prot) sequences.append(prot) return sequences diff --git a/backend/defense_finder_api/settings.py b/backend/defense_finder_api/settings.py index 878ff3ed4bf38a7154acc01cba7f3e4143b35561..fcfc47007df775718e14b7b5fb89d5484d367327 100644 --- a/backend/defense_finder_api/settings.py +++ b/backend/defense_finder_api/settings.py @@ -18,7 +18,7 @@ env = environ.Env( # set casting, default value DEBUG=(bool, False), DF_HOSTNAME=(str, "localhost"), - STATIC_URL=(str, "/static/") + STATIC_URL=(str, "/static/"), ) environ.Env.read_env() @@ -52,7 +52,7 @@ INSTALLED_APPS = [ "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", - "galaxy", + # "galaxy", "analysis", "django_to_galaxy", ] @@ -93,12 +93,12 @@ WSGI_APPLICATION = "defense_finder_api.wsgi.application" DATABASES = { "default": env.db(), - # { + # "default": { # "ENGINE": "django.db.backends.postgresql", - # "NAME": "df-db", - # "USER": "df", - # "PASSWORD": "password", - # "HOST": "postgresql", + # "NAME": env("POSTGRES_DB"), + # "USER": env("POSTGRES_USER"), + # "PASSWORD": env("POSTGRES_PASSWORD"), + # "HOST": env("POSTGRES_HOST"), # "PORT": "5432", # } } diff --git a/backend/galaxy/__init__.py b/backend/galaxy/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/backend/galaxy/admin.py b/backend/galaxy/admin.py deleted file mode 100644 index 8c38f3f3dad51e4585f3984282c2a4bec5349c1e..0000000000000000000000000000000000000000 --- a/backend/galaxy/admin.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.contrib import admin - -# Register your models here. diff --git a/backend/galaxy/apps.py b/backend/galaxy/apps.py deleted file mode 100644 index 042bab42cd5991225fb3aaa16c3feec1242d09f1..0000000000000000000000000000000000000000 --- a/backend/galaxy/apps.py +++ /dev/null @@ -1,6 +0,0 @@ -from django.apps import AppConfig - - -class GalaxyConfig(AppConfig): - default_auto_field = "django.db.models.BigAutoField" - name = "galaxy" diff --git a/backend/galaxy/managers.py b/backend/galaxy/managers.py deleted file mode 100644 index 2e550c346761450cac8d6226c33ece2bea264113..0000000000000000000000000000000000000000 --- a/backend/galaxy/managers.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.db import models - - -class InstanceManager(models.Manager): - def get_by_natural_key(self, name): - return self.get(name=name) - - -class UserManager(models.Manager): - def get_by_natural_key(self, username, instance): - return self.get(username=username, instance=instance) diff --git a/backend/galaxy/migrations/0001_initial.py b/backend/galaxy/migrations/0001_initial.py deleted file mode 100644 index 8e078bb5d6e678c800455c69f9292fc70ec7e275..0000000000000000000000000000000000000000 --- a/backend/galaxy/migrations/0001_initial.py +++ /dev/null @@ -1,80 +0,0 @@ -# Generated by Django 4.2.3 on 2023-07-25 11:56 - -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - initial = True - - dependencies = [] - - operations = [ - migrations.CreateModel( - name="Instance", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=200, unique=True)), - ("url", models.URLField(unique=True)), - ("description", models.TextField(blank=True)), - ], - options={ - "ordering": ["name"], - }, - ), - migrations.CreateModel( - name="WorkflowInvocation", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("name", models.CharField(max_length=20)), - ( - "creation_date", - models.DateTimeField(default=django.utils.timezone.now), - ), - ], - ), - migrations.CreateModel( - name="User", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), - ("username", models.EmailField(max_length=254)), - ("api_key", models.CharField(blank=True, max_length=254)), - ( - "instance", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="galaxy.instance", - ), - ), - ], - options={ - "ordering": ["username", "instance"], - "unique_together": {("username", "instance")}, - }, - ), - ] diff --git a/backend/galaxy/migrations/__init__.py b/backend/galaxy/migrations/__init__.py deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/backend/galaxy/models.py b/backend/galaxy/models.py deleted file mode 100644 index 46899499191eaf641beee8a7c615cb559636e4a4..0000000000000000000000000000000000000000 --- a/backend/galaxy/models.py +++ /dev/null @@ -1,43 +0,0 @@ -from django.utils import timezone -from django.db import models - -from galaxy.managers import InstanceManager, UserManager - -# Create your models here. - - -class Instance(models.Model): - name = models.CharField(max_length=200, unique=True) - url = models.URLField(unique=True) - description = models.TextField(blank=True) - - objects = InstanceManager() - - class Meta: - ordering = ["name"] - - def natural_key(self): - return (self.name,) - - def __str__(self): - return self.name - - -class User(models.Model): - username = models.EmailField(max_length=254) - api_key = models.CharField(max_length=254, blank=True) - instance = models.ForeignKey(Instance, on_delete=models.CASCADE) - - objects = UserManager() - - class Meta: - ordering = ["username", "instance"] - unique_together = [["username", "instance"]] - - def natural_key(self): - return (self.username, self.instance) - - -class WorkflowInvocation(models.Model): - name = models.CharField(max_length=20) - creation_date = models.DateTimeField(default=timezone.now) diff --git a/backend/galaxy/tests.py b/backend/galaxy/tests.py deleted file mode 100644 index 7ce503c2dd97ba78597f6ff6e4393132753573f6..0000000000000000000000000000000000000000 --- a/backend/galaxy/tests.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.test import TestCase - -# Create your tests here. diff --git a/backend/galaxy/views.py b/backend/galaxy/views.py deleted file mode 100644 index 91ea44a218fbd2f408430959283f0419c921093e..0000000000000000000000000000000000000000 --- a/backend/galaxy/views.py +++ /dev/null @@ -1,3 +0,0 @@ -from django.shortcuts import render - -# Create your views here. diff --git a/backend/poetry.lock b/backend/poetry.lock index 3b67d8e5d85f1bcc73026b3e0ab183ed7b309a57..cadcff4180946868fdc6f8cbb36ec8361a40db33 100644 --- a/backend/poetry.lock +++ b/backend/poetry.lock @@ -450,6 +450,41 @@ files = [ [package.extras] testing = ["coverage", "pyyaml"] +[[package]] +name = "markdown-it-py" +version = "3.0.0" +description = "Python port of markdown-it. Markdown parsing, done right!" +optional = false +python-versions = ">=3.8" +files = [ + {file = "markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb"}, + {file = "markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1"}, +] + +[package.dependencies] +mdurl = ">=0.1,<1.0" + +[package.extras] +benchmarking = ["psutil", "pytest", "pytest-benchmark"] +code-style = ["pre-commit (>=3.0,<4.0)"] +compare = ["commonmark (>=0.9,<1.0)", "markdown (>=3.4,<4.0)", "mistletoe (>=1.0,<2.0)", "mistune (>=2.0,<3.0)", "panflute (>=2.3,<3.0)"] +linkify = ["linkify-it-py (>=1,<3)"] +plugins = ["mdit-py-plugins"] +profiling = ["gprof2dot"] +rtd = ["jupyter_sphinx", "mdit-py-plugins", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"] +testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] + +[[package]] +name = "mdurl" +version = "0.1.2" +description = "Markdown URL utilities" +optional = false +python-versions = ">=3.7" +files = [ + {file = "mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8"}, + {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, +] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -682,6 +717,21 @@ typing-extensions = ">=4.2.0" dotenv = ["python-dotenv (>=0.10.4)"] email = ["email-validator (>=1.0.3)"] +[[package]] +name = "pygments" +version = "2.17.2" +description = "Pygments is a syntax highlighting package written in Python." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pygments-2.17.2-py3-none-any.whl", hash = "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c"}, + {file = "pygments-2.17.2.tar.gz", hash = "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"}, +] + +[package.extras] +plugins = ["importlib-metadata"] +windows-terminal = ["colorama (>=0.4.6)"] + [[package]] name = "pytz" version = "2023.3" @@ -817,6 +867,24 @@ files = [ [package.dependencies] requests = ">=2.0.1,<3.0.0" +[[package]] +name = "rich" +version = "13.7.1" +description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" +optional = false +python-versions = ">=3.7.0" +files = [ + {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, + {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, +] + +[package.dependencies] +markdown-it-py = ">=2.2.0" +pygments = ">=2.13.0,<3.0.0" + +[package.extras] +jupyter = ["ipywidgets (>=7.5.1,<9)"] + [[package]] name = "six" version = "1.16.0" @@ -911,4 +979,4 @@ zstd = ["zstandard (>=0.18.0)"] [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "89a87025d377cb3257ccd2e8e2d4eafab8ac82c7714d350dd7d50f09226ca9d8" +content-hash = "0178257066e14753b45a43635af1b8fb9ab5f4773148e50f2ae63573f08e98b7" diff --git a/backend/pyproject.toml b/backend/pyproject.toml index 31d51f3ffe5e3365401639a4051a144a0ec7a4b2..684756c6a271798c4c052f1fac5f97f9a05bf089 100644 --- a/backend/pyproject.toml +++ b/backend/pyproject.toml @@ -13,6 +13,7 @@ django-to-galaxy = "^0.6.4" django-environ = "^0.10.0" gunicorn = "^21.2.0" biopython = "^1.83" +rich = "^13.7.1" [tool.poetry.group.dev.dependencies] diff --git a/deploy/charts/djangoninja/templates/deployment.yaml b/deploy/charts/djangoninja/templates/deployment.yaml index 7eb4b594be90f95b4d14745949f0fbe111fb2756..78458349d5b0c4555d1a3ee06cc522291e0c0e05 100644 --- a/deploy/charts/djangoninja/templates/deployment.yaml +++ b/deploy/charts/djangoninja/templates/deployment.yaml @@ -74,18 +74,20 @@ spec: secretKeyRef: name: "{{ .Values.postgresql.user }}.{{ .Values.postgresql.teamId }}-postgresql.credentials.postgresql.acid.zalan.do" key: username + - name: POSTGRES_HOST + value: "{{ .Values.postgresql.teamId }}-postgresql" - name: POSTGRES_DB value: {{ .Values.postgresql.name }} - name: DATABASE_URL value: "psql://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@{{ .Values.postgresql.teamId }}-postgresql:5432/$(POSTGRES_DB)" - # livenessProbe: - # httpGet: - # path: / - # port: 8000 - # readinessProbe: - # httpGet: - # path: / - # port: 8000 + livenessProbe: + httpGet: + path: /api/docs + port: 8000 + readinessProbe: + httpGet: + path: /api/docs + port: 8000 resources: {{- toYaml .Values.resources | nindent 12 }} volumeMounts: @@ -93,6 +95,57 @@ spec: # name: server-static - mountPath: /uploaded-files name: uploaded-files + - name: "{{ .Chart.Name }}-sidecar" + securityContext: + {{- toYaml .Values.securityContext | nindent 12 }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + command: [ "/bin/bash", "-c", "--" ] + args: [ "while true; do python manage.py clear-sessions-history; python manage.py clearsessions; find /uploaded-files -type f -mmin +360 -delete; sleep 3600; done;" ] + ports: + - name: http + containerPort: {{ .Values.service.port }} + protocol: TCP + envFrom: + - configMapRef: + name: galaxy-config-map + env: + - name: DEBUG + value: "True" + - name: SECRET_KEY + value: {{ .Values.django.secret }} + - name: GALAXY_API_KEY + valueFrom: + secretKeyRef: + name: galaxy-secret + key: galaxy_key + - name: DF_HOSTNAME + value: {{ .Release.Name }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: "{{ .Values.postgresql.user }}.{{ .Values.postgresql.teamId }}-postgresql.credentials.postgresql.acid.zalan.do" + key: password + - name: POSTGRES_USER + valueFrom: + secretKeyRef: + name: "{{ .Values.postgresql.user }}.{{ .Values.postgresql.teamId }}-postgresql.credentials.postgresql.acid.zalan.do" + key: username + - name: POSTGRES_DB + value: {{ .Values.postgresql.name }} + - name: POSTGRES_HOST + value: "{{ .Values.postgresql.teamId }}-postgresql" + - name: DATABASE_URL + value: "psql://$(POSTGRES_USER):$(POSTGRES_PASSWORD)@{{ .Values.postgresql.teamId }}-postgresql:5432/$(POSTGRES_DB)" + resources: + {{- toYaml .Values.resources | nindent 12 }} + volumeMounts: + # - mountPath: /code/public/static + # name: server-static + - mountPath: /uploaded-files + name: uploaded-files + + {{- with .Values.nodeSelector }} nodeSelector: {{- toYaml . | nindent 8 }} diff --git a/deploy/charts/djangoninja/values.yaml b/deploy/charts/djangoninja/values.yaml index 858f109edc2dd62afae1342e25ced6ba7204dfdf..8d45c08398dd67a9055d4ed67e1c997c285b102c 100644 --- a/deploy/charts/djangoninja/values.yaml +++ b/deploy/charts/djangoninja/values.yaml @@ -38,7 +38,7 @@ securityContext: # readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 1001 - fsGroup: 1001 + # fsGroup: 1001 service: type: ClusterIP diff --git a/deploy/charts/nuxt/templates/deployment.yaml b/deploy/charts/nuxt/templates/deployment.yaml index d85e96f17b71ceebde9131e500f8b0f16e631709..257e257959c73710e04b865bf0ec38d27219be06 100644 --- a/deploy/charts/nuxt/templates/deployment.yaml +++ b/deploy/charts/nuxt/templates/deployment.yaml @@ -39,14 +39,14 @@ spec: - name: http containerPort: {{ .Values.service.port }} protocol: TCP - # livenessProbe: - # httpGet: - # path: / - # port: http - # readinessProbe: - # httpGet: - # path: / - # port: http + livenessProbe: + httpGet: + path: / + port: 3000 + readinessProbe: + httpGet: + path: / + port: 3000 resources: {{- toYaml .Values.resources | nindent 12 }} {{- with .Values.nodeSelector }} diff --git a/deploy/templates/postgres-operator.yaml b/deploy/templates/postgres-operator.yaml index 7ea69dde834430ca2d3dc9440fe95624bc85aaaf..83e5fab5d8b8e9d027bdf6b1081ae7aa1dba6bc5 100644 --- a/deploy/templates/postgres-operator.yaml +++ b/deploy/templates/postgres-operator.yaml @@ -3,7 +3,7 @@ kind: postgresql metadata: name: {{ .Values.postgresql.teamId }}-postgresql spec: - dockerImage: ghcr.io/zalando/spilo-15:3.0-p1 + dockerImage: harbor.pasteur.fr/dockerhub/spilo:15.13-latest teamId: {{ .Values.postgresql.teamId }} numberOfInstances: 1 users: diff --git a/deploy/values.development.yaml b/deploy/values.development.yaml index 1cabaaed1163ba82d3d102211991c01dcdde32e8..dbeea52f0959cd6e6f78cad18f22855dca28b664 100644 --- a/deploy/values.development.yaml +++ b/deploy/values.development.yaml @@ -39,6 +39,7 @@ postgresql: cpu: 1000m memory: 3Gi logical_backup: false + shared_buffers: "250MB" django: secret: "not-secret" diff --git a/deploy/values.production.yaml b/deploy/values.production.yaml index 545daaba5f6d3b17a453e2985f932fe81580f6a7..681280676c1547816914fc929ee78178e3d8f317 100644 --- a/deploy/values.production.yaml +++ b/deploy/values.production.yaml @@ -76,6 +76,7 @@ postgresql: memory: "2Gi" ephemeral-storage: "1Gi" logical_backup: true + shared_buffers: "300MB" wiki: diff --git a/frontend/pages/analyses/[analysisId].vue b/frontend/pages/analyses/[analysisId].vue index a78b0b5b22c3f0b4a6f5620737a6832d4c2ce7a3..cf4510d81836bfa1b2d19aa0221d7f886e087e2c 100644 --- a/frontend/pages/analyses/[analysisId].vue +++ b/frontend/pages/analyses/[analysisId].vue @@ -415,6 +415,7 @@ watchEffect(() => { const selectedResult = ref(null); </script> + <template> <v-card v-if="analysis" flat color="transparent"> @@ -425,10 +426,11 @@ const selectedResult = ref(null); <v-card> <v-toolbar density="compact" class="pr-2"> <v-toolbar-title>{{ analysis.name }}</v-toolbar-title> - <v-btn color="primary" prepend-icon="mdi-download" :href="`/api/analysis/${analysis.id}/results-archive`">Download + <v-btn color="primary" prepend-icon="mdi-download" + :href="`/api/analysis/${analysis.id}/results-archive`">Download all results</v-btn> <v-chip color="primary" rounded>{{ new Date(analysis.create_time).toLocaleString() }}</v-chip> - <template v-if="analysis.percentage_done !== 100" #extension> + <template v-if="analysis.percentage_done !== 100 && analysis.stderr === ''" #extension> <v-row> <v-col cols="12"> <v-card flat color="transparent"> @@ -440,36 +442,48 @@ const selectedResult = ref(null); </v-row> </template> </v-toolbar> - <v-card-text> - <div ref="gbContainer"> - <v-card flat color="transparent"> - <v-toolbar variant="flat" density="compact" color="transparent"> - <v-spacer></v-spacer><v-toolbar-items> - <v-switch v-model="displayHmmerHits" color="primary" label="Display HMM-only hits + + <template v-if="analysis.stderr !== ''"> + <v-card color="error" variant="tonal" class="my-2"> + <v-card-title>Standard error</v-card-title> + <v-card-text> + <pre> {{ analysis.stderr }} </pre> + </v-card-text> + </v-card> + </template> + + <template v-else> + <v-card-text> + <div ref="gbContainer"> + <v-card flat color="transparent"> + <v-toolbar variant="flat" density="compact" color="transparent"> + <v-spacer></v-spacer><v-toolbar-items> + <v-switch v-model="displayHmmerHits" color="primary" label="Display HMM-only hits " class="mr-2"></v-switch> - </v-toolbar-items></v-toolbar> - - <svg ref="svgRef" :width="computedWidth" :height="height"> - <g class="x-axis" /> - </svg> - </v-card> - </div> - </v-card-text> - <v-card-text> - <v-btn-toggle v-model="selectedResult" rounded="0" color="primary" group> - <v-btn value="systems" :to="`/analyses/${analysis.id}/systems`"> - - Systems - </v-btn> - <v-btn value="genes" exact :to="`/analyses/${analysis.id}/genes`"> - Genes - </v-btn> - <v-btn value="hmmer" :to="`/analyses/${analysis.id}/hmmer`"> - Hmmer - </v-btn> - </v-btn-toggle> - <!-- <NuxtLink :to="`/analyses/${analysis.id}/genes`">Genes</NuxtLink> --> - </v-card-text> + </v-toolbar-items></v-toolbar> + + <svg ref="svgRef" :width="computedWidth" :height="height"> + <g class="x-axis" /> + </svg> + </v-card> + </div> + </v-card-text> + <v-card-text> + <v-btn-toggle v-model="selectedResult" rounded="0" color="primary" group> + <v-btn value="systems" :to="`/analyses/${analysis.id}/systems`"> + + Systems + </v-btn> + <v-btn value="genes" exact :to="`/analyses/${analysis.id}/genes`"> + Genes + </v-btn> + <v-btn value="hmmer" :to="`/analyses/${analysis.id}/hmmer`"> + Hmmer + </v-btn> + </v-btn-toggle> + <!-- <NuxtLink :to="`/analyses/${analysis.id}/genes`">Genes</NuxtLink> --> + </v-card-text> + </template> <NuxtPage /> </v-card> </v-card>