cg 80.1.0__py3-none-any.whl → 83.14.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. cg/__init__.py +1 -1
  2. cg/apps/housekeeper/hk.py +1 -1
  3. cg/apps/tb/api.py +1 -1
  4. cg/cli/upload/mutacc.py +16 -3
  5. cg/cli/upload/scout.py +2 -2
  6. cg/cli/upload/utils.py +10 -1
  7. cg/cli/workflow/balsamic/base.py +29 -4
  8. cg/cli/workflow/microsalt/base.py +3 -1
  9. cg/cli/workflow/nallo/base.py +18 -38
  10. cg/cli/workflow/nf_analysis.py +2 -203
  11. cg/cli/workflow/raredisease/base.py +33 -51
  12. cg/cli/workflow/rnafusion/base.py +28 -3
  13. cg/cli/workflow/taxprofiler/base.py +21 -13
  14. cg/cli/workflow/tomte/base.py +17 -19
  15. cg/constants/constants.py +3 -3
  16. cg/constants/devices.py +6 -1
  17. cg/constants/gene_panel.py +3 -1
  18. cg/constants/lims.py +4 -0
  19. cg/constants/orderforms.py +1 -1
  20. cg/constants/pacbio.py +1 -0
  21. cg/constants/scout.py +6 -4
  22. cg/exc.py +12 -4
  23. cg/meta/compress/compress.py +7 -2
  24. cg/meta/delivery_report/nallo.py +1 -1
  25. cg/meta/delivery_report/templates/macros/ticket_system.html +1 -1
  26. cg/meta/observations/balsamic_observations_api.py +1 -1
  27. cg/meta/observations/mip_dna_observations_api.py +1 -1
  28. cg/meta/observations/nallo_observations_api.py +1 -1
  29. cg/meta/observations/observations_api.py +1 -1
  30. cg/meta/observations/raredisease_observations_api.py +1 -1
  31. cg/meta/tar/tar.py +5 -2
  32. cg/meta/upload/coverage.py +5 -5
  33. cg/meta/upload/raredisease/raredisease.py +3 -0
  34. cg/meta/upload/scout/nallo_config_builder.py +14 -0
  35. cg/meta/workflow/nallo.py +22 -95
  36. cg/meta/workflow/nf_analysis.py +11 -262
  37. cg/meta/workflow/raredisease.py +3 -112
  38. cg/meta/workflow/rnafusion.py +2 -34
  39. cg/meta/workflow/taxprofiler.py +2 -38
  40. cg/meta/workflow/tomte.py +2 -42
  41. cg/models/deliverables/metric_deliverables.py +1 -1
  42. cg/models/nallo/nallo.py +14 -64
  43. cg/models/nf_analysis.py +1 -41
  44. cg/models/raredisease/raredisease.py +0 -62
  45. cg/models/rnafusion/rnafusion.py +0 -26
  46. cg/models/scout/scout_load_config.py +1 -0
  47. cg/models/taxprofiler/taxprofiler.py +0 -42
  48. cg/models/tomte/tomte.py +0 -69
  49. cg/resources/nallo_bundle_filenames.yaml +282 -22
  50. cg/resources/raredisease_bundle_filenames.yaml +11 -1
  51. cg/resources/taxprofiler_bundle_filenames.yaml +20 -0
  52. cg/server/admin.py +51 -24
  53. cg/server/app.py +15 -4
  54. cg/server/endpoints/sequencing_run/dtos.py +21 -3
  55. cg/server/endpoints/sequencing_run/pacbio_sequencing_run.py +29 -10
  56. cg/server/endpoints/sequencing_run/pacbio_smrt_cell_metrics.py +20 -0
  57. cg/services/analysis_starter/configurator/configurator.py +1 -1
  58. cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/models.py +40 -1
  59. cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/nallo.py +3 -1
  60. cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/tomte_params_file_creator.py +3 -1
  61. cg/services/analysis_starter/factories/configurator_factory.py +4 -4
  62. cg/services/analysis_starter/tracker/implementations/balsamic.py +4 -1
  63. cg/services/analysis_starter/tracker/implementations/microsalt.py +4 -4
  64. cg/services/analysis_starter/tracker/implementations/mip_dna.py +4 -1
  65. cg/services/analysis_starter/tracker/implementations/nextflow_tracker.py +6 -4
  66. cg/services/analysis_starter/tracker/tracker.py +10 -6
  67. cg/services/illumina/backup/backup_service.py +29 -7
  68. cg/services/orders/validation/constants.py +3 -0
  69. cg/services/orders/validation/index_sequences.py +558 -0
  70. cg/services/run_devices/pacbio/data_storage_service/pacbio_store_service.py +39 -18
  71. cg/services/run_devices/pacbio/data_transfer_service/data_transfer_service.py +8 -2
  72. cg/services/run_devices/pacbio/data_transfer_service/dto.py +9 -3
  73. cg/services/run_devices/pacbio/data_transfer_service/utils.py +14 -7
  74. cg/services/run_devices/pacbio/metrics_parser/models.py +1 -0
  75. cg/services/run_devices/pacbio/sequencing_runs_service.py +35 -7
  76. cg/services/sequencing_qc_service/quality_checks/checks.py +18 -16
  77. cg/services/sequencing_qc_service/quality_checks/utils.py +82 -18
  78. cg/services/sequencing_qc_service/sequencing_qc_service.py +12 -10
  79. cg/store/crud/create.py +73 -42
  80. cg/store/crud/read.py +50 -2
  81. cg/store/crud/update.py +14 -3
  82. cg/store/models.py +88 -31
  83. cg/store/store.py +8 -1
  84. {cg-80.1.0.dist-info → cg-83.14.0.dist-info}/METADATA +1 -1
  85. {cg-80.1.0.dist-info → cg-83.14.0.dist-info}/RECORD +91 -90
  86. /cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/{nallo.py → nallo_sample_sheet_creator.py} +0 -0
  87. /cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/{raredisease.py → raredisease_sample_sheet_creator.py} +0 -0
  88. /cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/{rnafusion.py → rnafusion_sample_sheet_creator.py} +0 -0
  89. /cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/{taxprofiler.py → taxprofiler_sample_sheet_creator.py} +0 -0
  90. {cg-80.1.0.dist-info → cg-83.14.0.dist-info}/WHEEL +0 -0
  91. {cg-80.1.0.dist-info → cg-83.14.0.dist-info}/entry_points.txt +0 -0
cg/store/crud/create.py CHANGED
@@ -9,6 +9,7 @@ from sqlalchemy.orm import Session
9
9
 
10
10
  from cg.constants import DataDelivery, Priority, Workflow
11
11
  from cg.constants.archiving import PDC_ARCHIVE_LOCATION
12
+ from cg.exc import PacbioSequencingRunAlreadyExistsError
12
13
  from cg.models.orders.constants import OrderType
13
14
  from cg.services.illumina.data_transfer.models import (
14
15
  IlluminaFlowCellDTO,
@@ -19,6 +20,7 @@ from cg.services.run_devices.pacbio.data_transfer_service.dto import (
19
20
  PacBioSampleSequencingMetricsDTO,
20
21
  PacBioSequencingRunDTO,
21
22
  PacBioSMRTCellDTO,
23
+ PacBioSMRTCellMetricsDTO,
22
24
  )
23
25
  from cg.store.crud.read import ReadHandler
24
26
  from cg.store.database import get_session
@@ -44,6 +46,7 @@ from cg.store.models import (
44
46
  PacbioSampleSequencingMetrics,
45
47
  PacbioSequencingRun,
46
48
  PacbioSMRTCell,
49
+ PacbioSMRTCellMetrics,
47
50
  Panel,
48
51
  Pool,
49
52
  Sample,
@@ -500,47 +503,75 @@ class CreateMixin(ReadHandler):
500
503
  self.add_item_to_store(new_smrt_cell)
501
504
  return new_smrt_cell
502
505
 
503
- def create_pac_bio_sequencing_run(
504
- self, sequencing_run_dto: PacBioSequencingRunDTO, smrt_cell: PacbioSMRTCell
506
+ def create_pacbio_sequencing_run(
507
+ self, pacbio_sequencing_run_dto: PacBioSequencingRunDTO
505
508
  ) -> PacbioSequencingRun:
506
- LOG.debug(f"Creating Pacbio sequencing run for SMRT cell {smrt_cell.internal_id}")
507
- new_sequencing_run = PacbioSequencingRun(
508
- type=sequencing_run_dto.type,
509
- well=sequencing_run_dto.well,
510
- plate=sequencing_run_dto.plate,
511
- run_name=sequencing_run_dto.run_name,
512
- movie_name=sequencing_run_dto.movie_name,
513
- started_at=sequencing_run_dto.started_at,
514
- completed_at=sequencing_run_dto.completed_at,
515
- hifi_reads=sequencing_run_dto.hifi_reads,
516
- hifi_yield=sequencing_run_dto.hifi_yield,
517
- hifi_mean_read_length=sequencing_run_dto.hifi_mean_read_length,
518
- hifi_median_read_quality=sequencing_run_dto.hifi_median_read_quality,
519
- percent_reads_passing_q30=sequencing_run_dto.percent_reads_passing_q30,
520
- productive_zmws=sequencing_run_dto.productive_zmws,
521
- p0_percent=sequencing_run_dto.p0_percent,
522
- p1_percent=sequencing_run_dto.p1_percent,
523
- p2_percent=sequencing_run_dto.p2_percent,
524
- polymerase_mean_read_length=sequencing_run_dto.polymerase_mean_read_length,
525
- polymerase_read_length_n50=sequencing_run_dto.polymerase_read_length_n50,
526
- polymerase_mean_longest_subread=sequencing_run_dto.polymerase_mean_longest_subread,
527
- polymerase_longest_subread_n50=sequencing_run_dto.polymerase_longest_subread_n50,
528
- control_reads=sequencing_run_dto.control_reads,
529
- control_mean_read_length=sequencing_run_dto.control_mean_read_length,
530
- control_mean_read_concordance=sequencing_run_dto.control_mean_read_concordance,
531
- control_mode_read_concordance=sequencing_run_dto.control_mode_read_concordance,
532
- failed_reads=sequencing_run_dto.failed_reads,
533
- failed_yield=sequencing_run_dto.failed_yield,
534
- failed_mean_read_length=sequencing_run_dto.failed_mean_read_length,
535
- barcoded_hifi_reads=sequencing_run_dto.barcoded_hifi_reads,
536
- barcoded_hifi_reads_percentage=sequencing_run_dto.barcoded_hifi_reads_percentage,
537
- barcoded_hifi_yield=sequencing_run_dto.barcoded_hifi_yield,
538
- barcoded_hifi_yield_percentage=sequencing_run_dto.barcoded_hifi_yield_percentage,
539
- barcoded_hifi_mean_read_length=sequencing_run_dto.barcoded_hifi_mean_read_length,
540
- unbarcoded_hifi_reads=sequencing_run_dto.unbarcoded_hifi_reads,
541
- unbarcoded_hifi_yield=sequencing_run_dto.unbarcoded_hifi_yield,
542
- unbarcoded_hifi_mean_read_length=sequencing_run_dto.unbarcoded_hifi_mean_read_length,
509
+ """Create a new PacBio sequencing run
510
+
511
+ Raises PacbioSequencingRunAlreadyExistsError:
512
+ - When run name already exists in the database
513
+ """
514
+ if (
515
+ self._get_query(table=PacbioSequencingRun)
516
+ .filter(PacbioSequencingRun.run_name == pacbio_sequencing_run_dto.run_name)
517
+ .first()
518
+ ):
519
+ raise PacbioSequencingRunAlreadyExistsError(
520
+ message=f"{pacbio_sequencing_run_dto.run_name} already exists."
521
+ )
522
+ else:
523
+ LOG.debug(f"Creating Pacbio Sequencing Run for {pacbio_sequencing_run_dto.run_name}")
524
+ sequencing_run = PacbioSequencingRun(
525
+ instrument_name=pacbio_sequencing_run_dto.instrument_name,
526
+ run_name=pacbio_sequencing_run_dto.run_name,
527
+ )
528
+ self.add_item_to_store(sequencing_run)
529
+ return sequencing_run
530
+
531
+ def create_pacbio_smrt_cell_metrics(
532
+ self,
533
+ sequencing_run: PacbioSequencingRun,
534
+ smrt_cell_metrics_dto: PacBioSMRTCellMetricsDTO,
535
+ smrt_cell: PacbioSMRTCell,
536
+ ) -> PacbioSMRTCellMetrics:
537
+ LOG.debug(f"Creating Pacbio SMRT cell metrics for SMRT cell {smrt_cell.internal_id}")
538
+ new_sequencing_run = PacbioSMRTCellMetrics(
539
+ barcoded_hifi_mean_read_length=smrt_cell_metrics_dto.barcoded_hifi_mean_read_length,
540
+ barcoded_hifi_reads=smrt_cell_metrics_dto.barcoded_hifi_reads,
541
+ barcoded_hifi_reads_percentage=smrt_cell_metrics_dto.barcoded_hifi_reads_percentage,
542
+ barcoded_hifi_yield=smrt_cell_metrics_dto.barcoded_hifi_yield,
543
+ barcoded_hifi_yield_percentage=smrt_cell_metrics_dto.barcoded_hifi_yield_percentage,
544
+ completed_at=smrt_cell_metrics_dto.completed_at,
545
+ control_mean_read_concordance=smrt_cell_metrics_dto.control_mean_read_concordance,
546
+ control_mean_read_length=smrt_cell_metrics_dto.control_mean_read_length,
547
+ control_mode_read_concordance=smrt_cell_metrics_dto.control_mode_read_concordance,
548
+ control_reads=smrt_cell_metrics_dto.control_reads,
543
549
  device=smrt_cell,
550
+ failed_mean_read_length=smrt_cell_metrics_dto.failed_mean_read_length,
551
+ failed_reads=smrt_cell_metrics_dto.failed_reads,
552
+ failed_yield=smrt_cell_metrics_dto.failed_yield,
553
+ hifi_mean_read_length=smrt_cell_metrics_dto.hifi_mean_read_length,
554
+ hifi_median_read_quality=smrt_cell_metrics_dto.hifi_median_read_quality,
555
+ hifi_reads=smrt_cell_metrics_dto.hifi_reads,
556
+ hifi_yield=smrt_cell_metrics_dto.hifi_yield,
557
+ movie_name=smrt_cell_metrics_dto.movie_name,
558
+ p0_percent=smrt_cell_metrics_dto.p0_percent,
559
+ p1_percent=smrt_cell_metrics_dto.p1_percent,
560
+ p2_percent=smrt_cell_metrics_dto.p2_percent,
561
+ percent_reads_passing_q30=smrt_cell_metrics_dto.percent_reads_passing_q30,
562
+ plate=smrt_cell_metrics_dto.plate,
563
+ polymerase_longest_subread_n50=smrt_cell_metrics_dto.polymerase_longest_subread_n50,
564
+ polymerase_mean_longest_subread=smrt_cell_metrics_dto.polymerase_mean_longest_subread,
565
+ polymerase_mean_read_length=smrt_cell_metrics_dto.polymerase_mean_read_length,
566
+ polymerase_read_length_n50=smrt_cell_metrics_dto.polymerase_read_length_n50,
567
+ productive_zmws=smrt_cell_metrics_dto.productive_zmws,
568
+ sequencing_run=sequencing_run,
569
+ started_at=smrt_cell_metrics_dto.started_at,
570
+ type=smrt_cell_metrics_dto.type,
571
+ unbarcoded_hifi_mean_read_length=smrt_cell_metrics_dto.unbarcoded_hifi_mean_read_length,
572
+ unbarcoded_hifi_reads=smrt_cell_metrics_dto.unbarcoded_hifi_reads,
573
+ unbarcoded_hifi_yield=smrt_cell_metrics_dto.unbarcoded_hifi_yield,
574
+ well=smrt_cell_metrics_dto.well,
544
575
  )
545
576
  self.add_item_to_store(new_sequencing_run)
546
577
  return new_sequencing_run
@@ -548,11 +579,11 @@ class CreateMixin(ReadHandler):
548
579
  def create_pac_bio_sample_sequencing_run(
549
580
  self,
550
581
  sample_run_metrics_dto: PacBioSampleSequencingMetricsDTO,
551
- sequencing_run: PacbioSequencingRun,
582
+ smrt_cell_metrics: PacbioSMRTCellMetrics,
552
583
  ) -> PacbioSampleSequencingMetrics:
553
584
  sample_id: str = sample_run_metrics_dto.sample_internal_id
554
585
  LOG.debug(f"Creating Pacbio sample sequencing metric for sample {sample_id}")
555
- sample: Sample = self.get_sample_by_internal_id(sample_id)
586
+ sample: Sample = self.get_sample_by_internal_id_strict(sample_id)
556
587
  if not sample:
557
588
  self.rollback()
558
589
  raise EntryNotFoundError(f"Sample not found: {sample_id}")
@@ -562,7 +593,7 @@ class CreateMixin(ReadHandler):
562
593
  hifi_yield=sample_run_metrics_dto.hifi_yield,
563
594
  hifi_mean_read_length=sample_run_metrics_dto.hifi_mean_read_length,
564
595
  hifi_median_read_quality=sample_run_metrics_dto.hifi_median_read_quality,
565
- instrument_run=sequencing_run,
596
+ instrument_run=smrt_cell_metrics,
566
597
  polymerase_mean_read_length=sample_run_metrics_dto.polymerase_mean_read_length,
567
598
  )
568
599
  self.add_item_to_store(new_sample_sequencing_run)
cg/store/crud/read.py CHANGED
@@ -20,6 +20,7 @@ from cg.exc import (
20
20
  CgDataError,
21
21
  CgError,
22
22
  OrderNotFoundError,
23
+ PacbioSequencingRunNotFoundError,
23
24
  SampleNotFoundError,
24
25
  )
25
26
  from cg.models.orders.constants import OrderType
@@ -97,6 +98,7 @@ from cg.store.models import (
97
98
  PacbioSampleSequencingMetrics,
98
99
  PacbioSequencingRun,
99
100
  PacbioSMRTCell,
101
+ PacbioSMRTCellMetrics,
100
102
  Panel,
101
103
  Pool,
102
104
  RunDevice,
@@ -1806,18 +1808,34 @@ class ReadHandler(BaseHandler):
1806
1808
  sequencing_metrics = sequencing_metrics.filter(RunDevice.internal_id.in_(smrt_cell_ids))
1807
1809
  return sequencing_metrics.all()
1808
1810
 
1809
- def get_pacbio_sequencing_runs_by_run_name(self, run_name: str) -> list[PacbioSequencingRun]:
1811
+ def get_pacbio_smrt_cell_metrics_by_run_name(
1812
+ self, run_name: str
1813
+ ) -> list[PacbioSMRTCellMetrics]:
1810
1814
  """
1811
1815
  Fetches data from PacbioSequencingRunDTO filtered on run name.
1812
1816
  Raises:
1813
1817
  EntryNotFoundError if no sequencing runs are found for the run name
1814
1818
  """
1815
- runs: Query = self._get_query(table=PacbioSequencingRun)
1819
+ runs: Query = self._get_query(table=PacbioSMRTCellMetrics).join(
1820
+ PacbioSMRTCellMetrics.sequencing_run
1821
+ )
1816
1822
  runs = runs.filter(PacbioSequencingRun.run_name == run_name)
1817
1823
  if runs.count() == 0:
1818
1824
  raise EntryNotFoundError(f"Could not find any sequencing runs for {run_name}")
1819
1825
  return runs.all()
1820
1826
 
1827
+ def get_pacbio_sequencing_runs(
1828
+ self, page: int = 0, page_size: int = 0
1829
+ ) -> tuple[list[PacbioSequencingRun], int]:
1830
+ query = self._get_query(PacbioSequencingRun).order_by(PacbioSequencingRun.id.desc())
1831
+
1832
+ if page and page_size:
1833
+ query = query.limit(page_size).offset((page - 1) * page_size)
1834
+
1835
+ total_count: int = self._get_query(table=PacbioSequencingRun).count()
1836
+
1837
+ return query.all(), total_count
1838
+
1821
1839
  def get_case_priority(self, case_id: str) -> SlurmQos:
1822
1840
  """Get case priority."""
1823
1841
  case: Case = self.get_case_by_internal_id(case_id)
@@ -1835,3 +1853,33 @@ class ReadHandler(BaseHandler):
1835
1853
  ):
1836
1854
  return True
1837
1855
  return False
1856
+
1857
+ def get_pacbio_sequencing_run_by_id(self, id: int):
1858
+ """
1859
+ Get Pacbio Sequencing run by id.
1860
+ Raises:
1861
+ PacbioSequencingRunNotFoundError: If no Pacbio sequencing run is found with the given id.
1862
+ """
1863
+ try:
1864
+ return (
1865
+ self._get_query(table=PacbioSequencingRun)
1866
+ .filter(PacbioSequencingRun.id == id)
1867
+ .one()
1868
+ )
1869
+ except sqlalchemy.orm.exc.NoResultFound:
1870
+ raise PacbioSequencingRunNotFoundError(
1871
+ f"Pacbio Sequencing run with id {id} was not found in the database."
1872
+ )
1873
+
1874
+ def get_pacbio_sequencing_run_by_run_name(self, run_name: str) -> PacbioSequencingRun:
1875
+ """
1876
+ Get Pacbio Sequencing run by run name.
1877
+ Raises:
1878
+ PacbioSequencingRunNotFoundError: If no Pacbio sequencing run is found with the given run name.
1879
+ """
1880
+ try:
1881
+ return self._get_query(table=PacbioSequencingRun).filter_by(run_name=run_name).one()
1882
+ except sqlalchemy.orm.exc.NoResultFound:
1883
+ raise PacbioSequencingRunNotFoundError(
1884
+ f"Pacbio Sequencing run with run_name {run_name} was not found in the database."
1885
+ )
cg/store/crud/update.py CHANGED
@@ -13,6 +13,7 @@ from cg.store.models import (
13
13
  IlluminaSampleSequencingMetrics,
14
14
  IlluminaSequencingRun,
15
15
  Order,
16
+ PacbioSequencingRun,
16
17
  Sample,
17
18
  )
18
19
 
@@ -78,10 +79,10 @@ class UpdateMixin(ReadHandler):
78
79
  sample.reads = total_reads_for_sample
79
80
  self.commit_to_store()
80
81
 
81
- def update_sample_reads(self, internal_id: str, reads: int):
82
+ def update_sample_reads_pacbio(self, internal_id: str, reads: int):
82
83
  """Add reads to the current reads for a sample."""
83
- sample: Sample = self.get_sample_by_internal_id(internal_id)
84
- sample.reads += reads
84
+ sample: Sample = self.get_sample_by_internal_id_strict(internal_id)
85
+ sample.reads = reads
85
86
  self.commit_to_store()
86
87
 
87
88
  def update_sample_sequenced_at(self, internal_id: str, date: datetime):
@@ -132,3 +133,13 @@ class UpdateMixin(ReadHandler):
132
133
  analysis: Analysis = self.get_analysis_by_entry_id(analysis_id)
133
134
  analysis.delivery_report_created_at = delivery_report_date
134
135
  self.commit_to_store()
136
+
137
+ def update_pacbio_sequencing_run_comment(self, id: int, comment: str):
138
+ sequencing_run: PacbioSequencingRun = self.get_pacbio_sequencing_run_by_id(id)
139
+ sequencing_run.comment = comment
140
+ self.commit_to_store()
141
+
142
+ def update_pacbio_sequencing_run_processed(self, id: int, processed: bool):
143
+ sequencing_run: PacbioSequencingRun = self.get_pacbio_sequencing_run_by_id(id)
144
+ sequencing_run.processed = processed
145
+ self.commit_to_store()
cg/store/models.py CHANGED
@@ -28,7 +28,7 @@ from cg.constants.constants import (
28
28
  SexOptions,
29
29
  StatusOptions,
30
30
  )
31
- from cg.constants.devices import DeviceType
31
+ from cg.constants.devices import DeviceType, RevioNames
32
32
  from cg.constants.priority import SlurmQos
33
33
  from cg.constants.sequencing import SeqLibraryPrepCategory
34
34
  from cg.constants.symbols import EMPTY_STRING
@@ -147,6 +147,8 @@ class Application(Base):
147
147
  min_sequencing_depth: Mapped[int] = mapped_column(default=0)
148
148
  target_reads: Mapped[BigInt | None] = mapped_column(default=0)
149
149
  percent_reads_guaranteed: Mapped[int]
150
+ target_hifi_yield: Mapped[BigInt | None] = mapped_column(default=None)
151
+ percent_hifi_yield_guaranteed: Mapped[int | None] = mapped_column(default=None)
150
152
  sample_amount: Mapped[int | None]
151
153
  sample_volume: Mapped[Text | None]
152
154
  sample_concentration: Mapped[Text | None]
@@ -191,9 +193,20 @@ class Application(Base):
191
193
  return self.tag
192
194
 
193
195
  @property
194
- def expected_reads(self):
196
+ def expected_reads(self) -> float:
195
197
  return self.target_reads * self.percent_reads_guaranteed / 100
196
198
 
199
+ @property
200
+ def expected_hifi_yield(self) -> int | None:
201
+ if self.target_hifi_yield and self.percent_hifi_yield_guaranteed:
202
+ return round(self.target_hifi_yield * self.percent_hifi_yield_guaranteed / 100)
203
+ else:
204
+ return None
205
+
206
+ @property
207
+ def expected_express_hifi_yield(self) -> int | None:
208
+ return round(self.target_hifi_yield * 0.5) if self.target_hifi_yield else None
209
+
197
210
  @property
198
211
  def analysis_type(self) -> str:
199
212
  if self.prep_category == SeqLibraryPrepCategory.WHOLE_TRANSCRIPTOME_SEQUENCING.value:
@@ -729,7 +742,7 @@ class Sample(Base, PriorityMixin):
729
742
  prepared_at: Mapped[datetime | None]
730
743
 
731
744
  priority: Mapped[Priority] = mapped_column(default=Priority.standard)
732
- reads: Mapped[BigInt | None] = mapped_column(default=0)
745
+ reads: Mapped[BigInt] = mapped_column(default=0)
733
746
  last_sequenced_at: Mapped[datetime | None]
734
747
  received_at: Mapped[datetime | None]
735
748
  reference_genome: Mapped[Str255 | None]
@@ -759,16 +772,28 @@ class Sample(Base, PriorityMixin):
759
772
  def __str__(self) -> str:
760
773
  return f"{self.internal_id} ({self.name})"
761
774
 
775
+ @property
776
+ def hifi_yield(self) -> int | None:
777
+ if self._sample_run_metrics and getattr(self._sample_run_metrics[0], "hifi_yield", False):
778
+ return sum(metric.hifi_yield for metric in self._sample_run_metrics) # type: ignore
779
+ else:
780
+ return None
781
+
762
782
  @property
763
783
  def archive_location(self) -> str:
764
784
  """Returns the data_archive_location if the customer linked to the sample."""
765
785
  return self.customer.data_archive_location
766
786
 
767
787
  @property
768
- def expected_reads_for_sample(self) -> int:
788
+ def expected_reads_for_sample(self) -> float | None:
769
789
  """Return the expected reads of the sample."""
770
790
  return self.application_version.application.expected_reads
771
791
 
792
+ @property
793
+ def expected_hifi_yield(self) -> int | None:
794
+ """Return the expected HiFi yield of the sample."""
795
+ return self.application_version.application.expected_hifi_yield
796
+
772
797
  @property
773
798
  def has_reads(self) -> bool:
774
799
  return bool(self.reads)
@@ -1023,49 +1048,63 @@ class IlluminaSequencingRun(InstrumentRun):
1023
1048
  return data
1024
1049
 
1025
1050
 
1026
- class PacbioSequencingRun(InstrumentRun):
1027
- __tablename__ = "pacbio_sequencing_run"
1051
+ class PacbioSMRTCellMetrics(InstrumentRun):
1052
+ __tablename__ = "pacbio_smrt_cell_metrics"
1028
1053
 
1029
1054
  id: Mapped[int] = mapped_column(
1030
1055
  ForeignKey("instrument_run.id", ondelete="CASCADE"), primary_key=True
1031
1056
  )
1032
- well: Mapped[Str32]
1033
- plate: Mapped[int]
1034
- run_name: Mapped[Str32]
1035
- movie_name: Mapped[Str32]
1036
- started_at: Mapped[datetime]
1057
+ barcoded_hifi_mean_read_length: Mapped[BigInt]
1058
+ barcoded_hifi_reads_percentage: Mapped[Num_6_2]
1059
+ barcoded_hifi_reads: Mapped[BigInt]
1060
+ barcoded_hifi_yield_percentage: Mapped[Num_6_2]
1061
+ barcoded_hifi_yield: Mapped[BigInt]
1037
1062
  completed_at: Mapped[datetime]
1038
- hifi_reads: Mapped[BigInt]
1039
- hifi_yield: Mapped[BigInt]
1063
+ control_mean_read_concordance: Mapped[Num_6_2]
1064
+ control_mean_read_length: Mapped[BigInt]
1065
+ control_mode_read_concordance: Mapped[Num_6_2]
1066
+ control_reads: Mapped[BigInt]
1067
+ failed_mean_read_length: Mapped[BigInt]
1068
+ failed_reads: Mapped[BigInt]
1069
+ failed_yield: Mapped[BigInt]
1040
1070
  hifi_mean_read_length: Mapped[BigInt]
1041
1071
  hifi_median_read_quality: Mapped[Str32]
1042
- percent_reads_passing_q30: Mapped[Num_6_2]
1072
+ hifi_reads: Mapped[BigInt]
1073
+ hifi_yield: Mapped[BigInt]
1074
+ movie_name: Mapped[Str32]
1043
1075
  p0_percent: Mapped[Num_6_2]
1044
1076
  p1_percent: Mapped[Num_6_2]
1045
1077
  p2_percent: Mapped[Num_6_2]
1046
- productive_zmws: Mapped[BigInt]
1078
+ pacbio_sequencing_run_id: Mapped[int] = mapped_column(
1079
+ ForeignKey(
1080
+ "pacbio_sequencing_run.id",
1081
+ ondelete="CASCADE",
1082
+ name="pacbio_smrt_cell_metrics_pacbio_sequencing_run_fk",
1083
+ )
1084
+ )
1085
+ percent_reads_passing_q30: Mapped[Num_6_2]
1086
+ plate: Mapped[int]
1087
+ polymerase_longest_subread_n50: Mapped[BigInt]
1088
+ polymerase_mean_longest_subread: Mapped[BigInt]
1047
1089
  polymerase_mean_read_length: Mapped[BigInt]
1048
1090
  polymerase_read_length_n50: Mapped[BigInt]
1049
- polymerase_mean_longest_subread: Mapped[BigInt]
1050
- polymerase_longest_subread_n50: Mapped[BigInt]
1051
- control_reads: Mapped[BigInt]
1052
- control_mean_read_length: Mapped[BigInt]
1053
- control_mean_read_concordance: Mapped[Num_6_2]
1054
- control_mode_read_concordance: Mapped[Num_6_2]
1055
- failed_reads: Mapped[BigInt]
1056
- failed_yield: Mapped[BigInt]
1057
- failed_mean_read_length: Mapped[BigInt]
1058
- barcoded_hifi_reads: Mapped[BigInt]
1059
- barcoded_hifi_reads_percentage: Mapped[Num_6_2]
1060
- barcoded_hifi_yield: Mapped[BigInt]
1061
- barcoded_hifi_yield_percentage: Mapped[Num_6_2]
1062
- barcoded_hifi_mean_read_length: Mapped[BigInt]
1091
+ productive_zmws: Mapped[BigInt]
1092
+ started_at: Mapped[datetime]
1093
+ unbarcoded_hifi_mean_read_length: Mapped[BigInt]
1063
1094
  unbarcoded_hifi_reads: Mapped[BigInt]
1064
1095
  unbarcoded_hifi_yield: Mapped[BigInt]
1065
- unbarcoded_hifi_mean_read_length: Mapped[BigInt]
1096
+ well: Mapped[Str32]
1097
+
1098
+ sequencing_run: Mapped["PacbioSequencingRun"] = orm.relationship(
1099
+ back_populates="smrt_cell_metrics"
1100
+ )
1066
1101
 
1067
1102
  __mapper_args__ = {"polymorphic_identity": DeviceType.PACBIO}
1068
1103
 
1104
+ @property
1105
+ def run_name(self) -> str:
1106
+ return self.sequencing_run.run_name
1107
+
1069
1108
  def to_dict(self):
1070
1109
  return to_dict(self)
1071
1110
 
@@ -1122,13 +1161,31 @@ class PacbioSampleSequencingMetrics(SampleRunMetrics):
1122
1161
  polymerase_mean_read_length: Mapped[BigInt]
1123
1162
 
1124
1163
  __mapper_args__ = {"polymorphic_identity": DeviceType.PACBIO}
1125
- instrument_run = orm.relationship(PacbioSequencingRun, back_populates="sample_metrics")
1164
+ instrument_run = orm.relationship(PacbioSMRTCellMetrics, back_populates="sample_metrics")
1126
1165
 
1127
1166
  def to_dict(self) -> dict:
1128
1167
  """Represent as dictionary"""
1129
1168
  return to_dict(self)
1130
1169
 
1131
1170
 
1171
+ class PacbioSequencingRun(Base):
1172
+ """PacBio sequencing run, consisting of a set of SMRT-cells sequenced simultaneously."""
1173
+
1174
+ __tablename__ = "pacbio_sequencing_run"
1175
+
1176
+ id: Mapped[PrimaryKeyInt]
1177
+ run_name: Mapped[Str64] = mapped_column(unique=True)
1178
+ processed: Mapped[bool] = mapped_column(default=False)
1179
+ comment: Mapped[Text] = mapped_column(default="")
1180
+ instrument_name: Mapped[RevioNames] = mapped_column(
1181
+ types.Enum(*(revio_name.value for revio_name in RevioNames))
1182
+ )
1183
+
1184
+ smrt_cell_metrics: Mapped[list[PacbioSMRTCellMetrics]] = orm.relationship(
1185
+ back_populates="sequencing_run"
1186
+ )
1187
+
1188
+
1132
1189
  class OrderTypeApplication(Base):
1133
1190
  """Maps an order type to its allowed applications"""
1134
1191
 
cg/store/store.py CHANGED
@@ -10,4 +10,11 @@ class Store(
10
10
  DeleteMixin,
11
11
  UpdateMixin,
12
12
  ):
13
- pass
13
+ def recalculate_sample_reads_pacbio(self, sample_id: str) -> None:
14
+ reads: int = sum(
15
+ metric.hifi_reads
16
+ for metric in self.get_pacbio_sample_sequencing_metrics(
17
+ sample_id=sample_id, smrt_cell_ids=None
18
+ )
19
+ )
20
+ self.update_sample_reads_pacbio(internal_id=sample_id, reads=reads)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cg
3
- Version: 80.1.0
3
+ Version: 83.14.0
4
4
  Summary: Clinical Genomics command center
5
5
  Requires-Python: >=3.11,<3.13
6
6
  Classifier: Programming Language :: Python