Skip to content

Commit

Permalink
Fix analysis started at assignment (#3195)
Browse files Browse the repository at this point in the history
- Analysis started_at assignment. Now set at the creation date of the slurm jobs YAML file.
  • Loading branch information
ivadym authored May 2, 2024
1 parent 5a03f76 commit b6ad74c
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 18 deletions.
20 changes: 12 additions & 8 deletions cg/meta/workflow/analysis.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import datetime as dt
import logging
import os
import shutil
from datetime import datetime
from pathlib import Path
from subprocess import CalledProcessError
from typing import Any, Iterator
Expand Down Expand Up @@ -233,17 +233,21 @@ def upload_bundle_housekeeper(self, case_id: str, dry_run: bool = False) -> None
f"Analysis successfully stored in Housekeeper: {case_id} : {bundle_version.created_at}"
)

def get_analysis_started_date(self, case_id: str) -> datetime.date:
"""Return the start date of the analysis for a given case."""
return self.get_date_from_file_path(self.get_job_ids_path(case_id))

def upload_bundle_statusdb(self, case_id: str, dry_run: bool = False) -> None:
"""Storing analysis bundle in StatusDB for CASE_ID"""

LOG.info(f"Storing analysis in StatusDB for {case_id}")
case_obj: Case = self.status_db.get_case_by_internal_id(internal_id=case_id)
analysis_start: dt.datetime = self.get_bundle_created_date(case_id=case_id)
analysis_start: datetime.date = self.get_analysis_started_date(case_id)
workflow_version: str = self.get_workflow_version(case_id=case_id)
new_analysis: Case = self.status_db.add_analysis(
workflow=self.workflow,
version=workflow_version,
completed_at=dt.datetime.now(),
completed_at=datetime.now(),
primary=(len(case_obj.analyses) == 0),
started_at=analysis_start,
)
Expand Down Expand Up @@ -300,7 +304,7 @@ def get_hermes_transformed_deliverables(self, case_id: str) -> dict:
created=self.get_bundle_created_date(case_id),
).model_dump()

def get_bundle_created_date(self, case_id: str) -> dt.datetime:
def get_bundle_created_date(self, case_id: str) -> datetime.date:
return self.get_date_from_file_path(self.get_deliverables_file_path(case_id=case_id))

def get_workflow_version(self, case_id: str) -> str:
Expand Down Expand Up @@ -331,7 +335,7 @@ def set_statusdb_action(self, case_id: str, action: str | None, dry_run: bool =
f"Action '{action}' not permitted by StatusDB and will not be set for case {case_id}"
)

def get_analyses_to_clean(self, before: dt.datetime) -> list[Analysis]:
def get_analyses_to_clean(self, before: datetime) -> list[Analysis]:
analyses_to_clean = self.status_db.get_analyses_to_clean(
before=before, workflow=self.workflow
)
Expand Down Expand Up @@ -500,11 +504,11 @@ def resolve_decompression(self, case_id: str, dry_run: bool) -> None:
self.prepare_fastq_api.add_decompressed_fastq_files_to_housekeeper(case_id)

@staticmethod
def get_date_from_file_path(file_path: Path) -> dt.datetime.date:
def get_date_from_file_path(file_path: Path) -> datetime.date:
"""
Get date from deliverables path using date created metadata.
"""
return dt.datetime.fromtimestamp(int(os.path.getctime(file_path)))
return datetime.fromtimestamp(int(os.path.getctime(file_path)))

def get_lims_naming_metadata(self, sample: Sample) -> str | None:
return None
Expand Down Expand Up @@ -532,7 +536,7 @@ def clean_analyses(self, case_id: str) -> None:
analyses: list = self.status_db.get_case_by_internal_id(internal_id=case_id).analyses
LOG.info(f"Adding a cleaned at date for case {case_id}")
for analysis_obj in analyses:
analysis_obj.cleaned_at = analysis_obj.cleaned_at or dt.datetime.now()
analysis_obj.cleaned_at = analysis_obj.cleaned_at or datetime.now()
self.status_db.session.commit()

def clean_run_dir(self, case_id: str, yes: bool, case_path: list[Path] | Path) -> int:
Expand Down
21 changes: 11 additions & 10 deletions tests/cli/workflow/balsamic/test_compound_commands.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from datetime import datetime
from pathlib import Path

from click.testing import CliRunner
Expand All @@ -12,6 +13,7 @@
store,
store_available,
)
from cg.meta.workflow.analysis import AnalysisAPI
from cg.meta.workflow.balsamic import BalsamicAnalysisAPI
from cg.models.cg_config import CGConfig

Expand Down Expand Up @@ -48,7 +50,7 @@ def test_start(
case_id = "balsamic_case_wgs_single"

# WHEN dry running and config case already exists
mocker.patch("cg.meta.workflow.balsamic.BalsamicAnalysisAPI.config_case", return_value=None)
mocker.patch.object(BalsamicAnalysisAPI, "config_case", return_value=None)
result = cli_runner.invoke(start, [case_id, "--dry-run"], obj=balsamic_context)

# THEN command should execute successfully
Expand Down Expand Up @@ -82,7 +84,8 @@ def test_store(
# Make sure analysis not already stored in ClinicalDB
assert not balsamic_context.status_db.get_case_by_internal_id(internal_id=case_id).analyses

# GIVEN that HermesAPI returns a deliverables output
# GIVEN a started analysis and a HermesAPI returning a deliverables output
mocker.patch.object(AnalysisAPI, "get_analysis_started_date", return_value=datetime.now())
mocker.patch.object(HermesApi, "convert_deliverables")
HermesApi.convert_deliverables.return_value = CGDeliverables(**hermes_deliverables)

Expand Down Expand Up @@ -122,10 +125,8 @@ def test_start_available(
)

# GIVEN decompression is not needed and config case performed
mocker.patch(
"cg.meta.workflow.balsamic.BalsamicAnalysisAPI.resolve_decompression", return_value=None
)
mocker.patch("cg.meta.workflow.balsamic.BalsamicAnalysisAPI.config_case", return_value=None)
mocker.patch.object(BalsamicAnalysisAPI, "resolve_decompression", return_value=None)
mocker.patch.object(BalsamicAnalysisAPI, "config_case", return_value=None)

# WHEN running command
result = cli_runner.invoke(start_available, ["--dry-run"], obj=balsamic_context)
Expand Down Expand Up @@ -172,11 +173,11 @@ def test_store_available(

# GIVEN that HermesAPI returns a deliverables output and config case performed
hermes_deliverables["bundle_id"] = case_id_success
mocker.patch(
"cg.apps.hermes.hermes_api.HermesApi.convert_deliverables",
return_value=CGDeliverables(**hermes_deliverables),
mocker.patch.object(AnalysisAPI, "get_analysis_started_date", return_value=datetime.now())
mocker.patch.object(
HermesApi, "convert_deliverables", return_value=CGDeliverables(**hermes_deliverables)
)
mocker.patch("cg.meta.workflow.balsamic.BalsamicAnalysisAPI.config_case", return_value=None)
mocker.patch.object(BalsamicAnalysisAPI, "config_case", return_value=None)

# Ensure case was successfully picked up by start-available and status set to running
result = cli_runner.invoke(start_available, ["--dry-run"], obj=balsamic_context)
Expand Down
3 changes: 3 additions & 0 deletions tests/cli/workflow/balsamic/test_store_housekeeper.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from datetime import datetime
from pathlib import Path

import pytest
Expand All @@ -12,6 +13,7 @@
from cg.constants import EXIT_SUCCESS
from cg.constants.constants import FileFormat
from cg.io.controller import WriteStream
from cg.meta.workflow.analysis import AnalysisAPI
from cg.meta.workflow.balsamic import BalsamicAnalysisAPI
from cg.models.cg_config import CGConfig
from cg.utils import Process
Expand Down Expand Up @@ -141,6 +143,7 @@ def test_valid_case(
assert not balsamic_context.status_db.get_case_by_internal_id(internal_id=case_id).analyses

# GIVEN that HermesAPI returns a deliverables output
mocker.patch.object(AnalysisAPI, "get_analysis_started_date", return_value=datetime.now())
mocker.patch.object(HermesApi, "convert_deliverables")
HermesApi.convert_deliverables.return_value = CGDeliverables(**hermes_deliverables)

Expand Down
5 changes: 5 additions & 0 deletions tests/cli/workflow/nf_analysis/test_cli_store_housekeeper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Tests CLI common methods to store deliverable files into Housekeeper for NF analyses."""

import logging
from datetime import datetime

import pytest
from _pytest.fixtures import FixtureRequest
Expand All @@ -15,6 +16,7 @@
from cg.constants import EXIT_SUCCESS, Workflow
from cg.constants.constants import FileFormat
from cg.io.controller import WriteStream
from cg.meta.workflow.analysis import AnalysisAPI
from cg.models.cg_config import CGConfig
from cg.store.store import Store
from cg.utils import Process
Expand Down Expand Up @@ -161,6 +163,7 @@ def test_store_housekeeper_valid_case(
assert not store.get_case_by_internal_id(internal_id=case_id).analyses

# GIVEN that HermesAPI returns a deliverables output
mocker.patch.object(AnalysisAPI, "get_analysis_started_date", return_value=datetime.now())
mocker.patch.object(HermesApi, "convert_deliverables")
HermesApi.convert_deliverables.return_value = CGDeliverables(**hermes_deliverables)

Expand Down Expand Up @@ -210,6 +213,7 @@ def test_valid_case_already_added(
assert not context.status_db.get_case_by_internal_id(internal_id=case_id).analyses

# GIVEN that HermesAPI returns a deliverables output
mocker.patch.object(AnalysisAPI, "get_analysis_started_date", return_value=datetime.now())
mocker.patch.object(HermesApi, "convert_deliverables")
HermesApi.convert_deliverables.return_value = CGDeliverables(**hermes_deliverables)

Expand Down Expand Up @@ -257,6 +261,7 @@ def test_dry_run(
context.meta_apis["analysis_api"].housekeeper_api = real_housekeeper_api

# GIVEN that HermesAPI returns a deliverables output
mocker.patch.object(AnalysisAPI, "get_analysis_started_date", return_value=datetime.now())
mocker.patch.object(HermesApi, "convert_deliverables")
HermesApi.convert_deliverables.return_value = CGDeliverables(**hermes_deliverables)

Expand Down

0 comments on commit b6ad74c

Please sign in to comment.