Skip to content

Commit 4f49459

Browse files
authored
Merge pull request #243 from Clinical-Genomics/crud-separation
Continuation crud separation
2 parents 8dcda7c + 70f0391 commit 4f49459

File tree

5 files changed

+66
-56
lines changed

5 files changed

+66
-56
lines changed

microSALT/cli.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -315,14 +315,6 @@ def finish(
315315
if os.path.isdir(f"{input}/{subfolder}"):
316316
pool.append(subfolder)
317317

318-
run_settings = {
319-
"input": input,
320-
"track": track,
321-
"dry": dry,
322-
"email": config.regex.mail_recipient,
323-
"skip_update": skip_update,
324-
}
325-
326318
sampleinfo = review_sampleinfo(sampleinfo_file)
327319
ext_refs = Referencer(
328320
log=logger,

microSALT/store/db_manipulator.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,18 @@ def delete_sample(self, cg_id: str) -> None:
279279
self.session.commit()
280280
self.logger.info(f"Removed sample {cg_id} and its child rows")
281281

282+
def delete_sample_results(self, cg_id: str) -> None:
283+
"""Delete only the analysis result rows for a sample (seq_types, resistances, expacs)
284+
without removing the Samples row itself."""
285+
for obj in self.session.query(Expacs).filter(Expacs.CG_ID_sample == cg_id).all():
286+
self.session.delete(obj)
287+
for obj in self.session.query(Seq_types).filter(Seq_types.CG_ID_sample == cg_id).all():
288+
self.session.delete(obj)
289+
for obj in self.session.query(Resistances).filter(Resistances.CG_ID_sample == cg_id).all():
290+
self.session.delete(obj)
291+
self.session.commit()
292+
self.logger.info(f"Cleared analysis results for sample {cg_id}")
293+
282294
def delete_project(self, name: str) -> None:
283295
"""Delete all samples (and their child rows) belonging to a project."""
284296
for obj in self.session.query(Expacs).filter(Expacs.CG_ID_sample.like(f"{name}%")).all():
@@ -413,16 +425,29 @@ def read_columns(self, tablename: str):
413425
table = _resolve_orm_table(tablename)
414426
return dict.fromkeys(table.__table__.columns.keys())
415427

416-
def read_exists(self, table: str, item: dict[str, str]):
417-
"""Takes a k-v pair and checks for the entrys existence in the given table"""
418-
orm_table = _resolve_orm_table(table)
419-
filter_clauses = [getattr(orm_table, k) == v for k, v in item.items()]
420-
entry = self.session.query(orm_table).filter(and_(*filter_clauses)).scalar()
421-
return entry is not None
428+
def get_projects_by_cg_id_project(self, cg_id_project_name: str) -> Projects | None:
429+
"""Fetch a Projects record by CG_ID_project."""
430+
return (
431+
self.session.query(Projects)
432+
.filter(Projects.CG_ID_project == cg_id_project_name)
433+
.scalar()
434+
)
435+
436+
def get_collection_by_id(self, collection_id: str) -> Collections | None:
437+
return (
438+
self.session.query(Collections)
439+
.filter(Collections.ID_collection == collection_id)
440+
.scalar()
441+
)
442+
443+
def get_sample_by_cg_id_sample(self, cg_id_sample: str) -> Samples | None:
444+
return self.session.query(Samples).filter(Samples.CG_ID_sample == cg_id_sample).scalar()
422445

423446
def read_version(self, name: str):
424447
"""Gets the version from a given name. Should be generalized to return any value for any input"""
425-
version = self.session.query(Versions).filter(Versions.name == name).scalar()
448+
version: Versions | None = (
449+
self.session.query(Versions).filter(Versions.name == name).scalar()
450+
)
426451
if version is None:
427452
return "0"
428453
else:

microSALT/utils/job_creator.py

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -526,12 +526,10 @@ def create_snpsection(self):
526526

527527
def create_collection(self):
528528
"""Creates collection entry in database"""
529-
if self.db_pusher.read_exists("Collections", {"ID_collection": self.name}):
529+
if self.db_pusher.get_collection_by_id(self.name):
530530
self.db_pusher.delete_collection(self.name)
531531
for sample in self.pool:
532-
self.db_pusher.add_rec(
533-
{"ID_collection": self.name, "CG_ID_sample": sample}, "Collections"
534-
)
532+
self.db_pusher.add_collection(ID_collectiuon=self.name, CG_ID_sample=sample)
535533

536534
addedprojs = []
537535
for sample in self.pool:
@@ -541,8 +539,8 @@ def create_collection(self):
541539
self.create_project(lims_project)
542540
addedprojs.append(lims_project)
543541

544-
def create_project(self, name: str):
545-
"""Creates project in database"""
542+
def create_project(self, name: str) -> None:
543+
"""Creates or updates a project in the database."""
546544
if not self.sample:
547545
raise JobCreationError(
548546
"No sample information provided. Cannot create project in database."
@@ -552,12 +550,15 @@ def create_project(self, name: str):
552550
"Customer_ID_project": self.sample["Customer_ID_project"],
553551
"Customer_ID": self.sample["Customer_ID"],
554552
}
555-
self.db_pusher.add_to_session(self.db_pusher.add_project(**project_data))
556-
self.db_pusher.commit_session()
557-
558-
def create_sample(self):
559-
"""Creates sample in database"""
553+
if self.db_pusher.get_projects_by_cg_id_project(name):
554+
update_data = {k: v for k, v in project_data.items() if k != "CG_ID_project"}
555+
self.db_pusher.update_project({"CG_ID_project": name}, update_data)
556+
else:
557+
self.db_pusher.add_to_session(self.db_pusher.add_project(**project_data))
558+
self.db_pusher.commit_session()
560559

560+
def create_sample(self) -> None:
561+
"""Creates or updates a sample in the database."""
561562
try:
562563
if not self.sample:
563564
raise JobCreationError(
@@ -581,8 +582,13 @@ def create_sample(self):
581582
"method_libprep": self.sample["method_libprep"],
582583
"method_sequencing": self.sample["method_sequencing"],
583584
}
584-
self.db_pusher.add_to_session(self.db_pusher.add_sample(**sample_data))
585-
self.db_pusher.commit_session()
585+
cg_id = self.sample["CG_ID_sample"]
586+
if self.db_pusher.get_sample_by_cg_id_sample(cg_id):
587+
update_data = {k: v for k, v in sample_data.items() if k != "CG_ID_sample"}
588+
self.db_pusher.update_sample({"CG_ID_sample": cg_id}, update_data)
589+
else:
590+
self.db_pusher.add_to_session(self.db_pusher.add_sample(**sample_data))
591+
self.db_pusher.commit_session()
586592
except JobCreationError as e:
587593
self.logger.error(f"Unable to add sample {self.name} to database: {e}")
588594
except KeyError as e:

microSALT/utils/scraper.py

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,7 @@ def scrape_project(self, project=None):
108108
if project is None:
109109
project = self.name
110110
self.db_pusher.delete_project(project)
111-
if not self.db_pusher.read_exists("Projects", {"CG_ID_project": project}):
112-
self.logger.warning(f"Replacing project {project}")
113-
self.job_fallback.create_project(project)
111+
self.job_fallback.create_project(project)
114112

115113
# Scrape order matters a lot!
116114
for item in os.listdir(self.infolder):
@@ -140,17 +138,9 @@ def scrape_sample(self, sample=None):
140138
"""Scrapes a sample folder for information"""
141139
if sample is None:
142140
sample = self.name
143-
self.db_pusher.delete_sample(sample)
144-
145-
if not self.db_pusher.read_exists(
146-
"Projects", {"CG_ID_project": self.sample.get("CG_ID_project")}
147-
):
148-
self.logger.warning(f"Replacing project {self.sample.get('CG_ID_project')}")
149-
self.job_fallback.create_project(self.sample.get("CG_ID_project"))
150-
151-
if not self.db_pusher.read_exists("Samples", {"CG_ID_sample": sample}):
152-
self.logger.info(f"Replacing sample {sample}")
153-
self.job_fallback.create_sample(sample)
141+
self.db_pusher.delete_sample_results(sample)
142+
self.job_fallback.create_project(self.sample.get("CG_ID_project"))
143+
self.job_fallback.create_sample()
154144

155145
# Scrape order matters a lot!
156146
self.sampledir = self.infolder

tests/store/test_database.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -267,17 +267,6 @@ def test_purge_rec(sysexit, caplog, dbm):
267267
dbm.delete_collection("UPD1234A1")
268268

269269

270-
def test_top_index(dbm):
271-
dbm.add_to_session(dbm.add_sample(CG_ID_sample="Uniq_ID_123", total_reads=100))
272-
dbm.add_to_session(dbm.add_sample(CG_ID_sample="Uniq_ID_321", total_reads=100))
273-
dbm.commit_session()
274-
ti_returned = dbm.read_top_index("Samples", {"total_reads": "100"}, "total_reads")
275-
assert ti_returned == 100
276-
277-
ti_missing = dbm.read_top_index("Samples", {"total_reads": "99999"}, "total_reads")
278-
assert ti_missing == -1
279-
280-
281270
def test_query_rec(dbm):
282271
dbm.add_to_session(dbm.add_sample(CG_ID_sample="QRY_001"))
283272
dbm.add_to_session(dbm.add_sample(CG_ID_sample="QRY_002"))
@@ -301,12 +290,20 @@ def test_get_columns(dbm):
301290
assert "organism" in cols
302291

303292

304-
def test_exists(dbm):
293+
def test_get_sample_by_cg_id(dbm: DB_Manipulator):
305294
dbm.add_to_session(dbm.add_sample(CG_ID_sample="EXS_001"))
306295
dbm.commit_session()
307296

308-
assert dbm.read_exists("Samples", {"CG_ID_sample": "EXS_001"}) is True
309-
assert dbm.read_exists("Samples", {"CG_ID_sample": "DOES_NOT_EXIST"}) is False
297+
assert dbm.get_sample_by_cg_id_sample("EXS_001")
298+
assert dbm.get_sample_by_cg_id_sample("DOES_NOT_EXIST") is None
299+
300+
301+
def test_get_collection_by_id(dbm: DB_Manipulator):
302+
dbm.add_to_session(dbm.add_collection(CG_ID_sample="EXS_001", ID_collection="COLL_001"))
303+
dbm.commit_session()
304+
305+
assert dbm.get_collection_by_id("COLL_001")
306+
assert dbm.get_collection_by_id("DOES_NOT_EXIST") is None
310307

311308

312309
def test_resolve_orm_table_unknown():

0 commit comments

Comments
 (0)