cg 76.0.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 (152) hide show
  1. cg/__init__.py +1 -1
  2. cg/apps/housekeeper/hk.py +18 -1
  3. cg/apps/tb/api.py +42 -5
  4. cg/cli/transfer.py +13 -2
  5. cg/cli/upload/mutacc.py +16 -3
  6. cg/cli/upload/scout.py +2 -2
  7. cg/cli/upload/utils.py +10 -1
  8. cg/cli/workflow/balsamic/base.py +86 -172
  9. cg/cli/workflow/balsamic/options.py +3 -48
  10. cg/cli/workflow/balsamic/umi.py +210 -15
  11. cg/cli/workflow/microsalt/base.py +4 -2
  12. cg/cli/workflow/mip_dna/base.py +1 -1
  13. cg/cli/workflow/nallo/base.py +73 -23
  14. cg/cli/workflow/nf_analysis.py +5 -207
  15. cg/cli/workflow/raredisease/base.py +41 -54
  16. cg/cli/workflow/rnafusion/base.py +38 -8
  17. cg/cli/workflow/taxprofiler/base.py +31 -18
  18. cg/cli/workflow/tomte/base.py +83 -10
  19. cg/constants/constants.py +25 -30
  20. cg/constants/devices.py +6 -1
  21. cg/constants/gene_panel.py +3 -1
  22. cg/constants/housekeeper_tags.py +28 -28
  23. cg/constants/lims.py +4 -0
  24. cg/constants/nf_analysis.py +0 -1
  25. cg/constants/observations.py +21 -5
  26. cg/constants/orderforms.py +3 -3
  27. cg/constants/pacbio.py +1 -0
  28. cg/constants/priority.py +1 -1
  29. cg/constants/report.py +1 -0
  30. cg/constants/scout.py +12 -9
  31. cg/constants/sequencing.py +2 -2
  32. cg/constants/tb.py +5 -5
  33. cg/exc.py +27 -5
  34. cg/meta/compress/compress.py +7 -2
  35. cg/meta/delivery_report/balsamic.py +3 -1
  36. cg/meta/delivery_report/delivery_report_api.py +4 -3
  37. cg/meta/delivery_report/nallo.py +11 -11
  38. cg/meta/delivery_report/raredisease.py +7 -3
  39. cg/meta/delivery_report/templates/macros/data_analysis/qc_metrics/balsamic_qc_metrics.html +1 -0
  40. cg/meta/delivery_report/templates/macros/ticket_system.html +1 -1
  41. cg/meta/observations/balsamic_observations_api.py +110 -14
  42. cg/meta/observations/mip_dna_observations_api.py +1 -1
  43. cg/meta/observations/nallo_observations_api.py +1 -1
  44. cg/meta/observations/observations_api.py +23 -32
  45. cg/meta/observations/raredisease_observations_api.py +1 -1
  46. cg/meta/tar/tar.py +5 -2
  47. cg/meta/transfer/lims.py +32 -3
  48. cg/meta/upload/balsamic/balsamic.py +1 -8
  49. cg/meta/upload/coverage.py +5 -5
  50. cg/meta/upload/raredisease/raredisease.py +3 -0
  51. cg/meta/upload/scout/hk_tags.py +1 -0
  52. cg/meta/upload/scout/nallo_config_builder.py +31 -7
  53. cg/meta/workflow/balsamic.py +70 -36
  54. cg/meta/workflow/fastq.py +8 -0
  55. cg/meta/workflow/microsalt/quality_controller/models.py +0 -2
  56. cg/meta/workflow/microsalt/quality_controller/quality_controller.py +8 -16
  57. cg/meta/workflow/microsalt/quality_controller/result_logger.py +3 -6
  58. cg/meta/workflow/microsalt/quality_controller/utils.py +2 -45
  59. cg/meta/workflow/nallo.py +21 -99
  60. cg/meta/workflow/nf_analysis.py +12 -263
  61. cg/meta/workflow/raredisease.py +3 -112
  62. cg/meta/workflow/rnafusion.py +2 -34
  63. cg/meta/workflow/taxprofiler.py +2 -38
  64. cg/meta/workflow/tomte.py +2 -42
  65. cg/models/balsamic/config.py +0 -24
  66. cg/models/balsamic/metrics.py +5 -3
  67. cg/models/cg_config.py +39 -16
  68. cg/models/deliverables/metric_deliverables.py +1 -1
  69. cg/models/delivery_report/metadata.py +2 -1
  70. cg/models/nallo/nallo.py +14 -64
  71. cg/models/nf_analysis.py +1 -41
  72. cg/models/raredisease/raredisease.py +1 -63
  73. cg/models/rnafusion/rnafusion.py +0 -26
  74. cg/models/scout/scout_load_config.py +5 -2
  75. cg/models/taxprofiler/taxprofiler.py +0 -42
  76. cg/models/tomte/tomte.py +0 -69
  77. cg/resources/nallo_bundle_filenames.yaml +292 -22
  78. cg/resources/raredisease_bundle_filenames.yaml +11 -1
  79. cg/resources/taxprofiler_bundle_filenames.yaml +20 -0
  80. cg/server/admin.py +106 -25
  81. cg/server/app.py +15 -4
  82. cg/server/endpoints/sequencing_run/dtos.py +21 -3
  83. cg/server/endpoints/sequencing_run/pacbio_sequencing_run.py +29 -10
  84. cg/server/endpoints/sequencing_run/pacbio_smrt_cell_metrics.py +20 -0
  85. cg/services/analysis_starter/{service.py → analysis_starter.py} +11 -9
  86. cg/services/analysis_starter/configurator/abstract_model.py +8 -0
  87. cg/services/analysis_starter/configurator/configurator.py +1 -1
  88. cg/services/analysis_starter/configurator/extensions/nallo.py +27 -0
  89. cg/services/analysis_starter/configurator/extensions/{abstract.py → pipeline_extension.py} +1 -1
  90. cg/services/analysis_starter/configurator/extensions/raredisease.py +3 -1
  91. cg/services/analysis_starter/configurator/extensions/tomte_extension.py +28 -0
  92. cg/services/analysis_starter/configurator/file_creators/balsamic_config.py +240 -0
  93. cg/services/analysis_starter/configurator/file_creators/gene_panel.py +10 -5
  94. cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/abstract.py +2 -1
  95. cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/models.py +40 -1
  96. cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/nallo.py +37 -0
  97. cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/raredisease.py +8 -5
  98. cg/services/analysis_starter/configurator/file_creators/nextflow/params_file/tomte_params_file_creator.py +64 -0
  99. cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/creator.py +1 -1
  100. cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/nallo_sample_sheet_creator.py +65 -0
  101. cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/protocol.py +12 -0
  102. cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/{raredisease.py → raredisease_sample_sheet_creator.py} +2 -2
  103. cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/{rnafusion.py → rnafusion_sample_sheet_creator.py} +2 -2
  104. cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/{taxprofiler.py → taxprofiler_sample_sheet_creator.py} +2 -2
  105. cg/services/analysis_starter/configurator/file_creators/nextflow/sample_sheet/tomte_sample_sheet_creator.py +36 -0
  106. cg/services/analysis_starter/configurator/implementations/balsamic.py +68 -0
  107. cg/services/analysis_starter/configurator/implementations/nextflow.py +22 -5
  108. cg/services/analysis_starter/configurator/models/balsamic.py +152 -0
  109. cg/services/analysis_starter/configurator/models/mip_dna.py +6 -8
  110. cg/services/analysis_starter/configurator/models/nextflow.py +9 -0
  111. cg/services/analysis_starter/constants.py +2 -0
  112. cg/services/analysis_starter/factories/configurator_factory.py +131 -51
  113. cg/services/analysis_starter/factories/starter_factory.py +36 -7
  114. cg/services/analysis_starter/input_fetcher/implementations/bam_fetcher.py +57 -0
  115. cg/services/analysis_starter/input_fetcher/implementations/fastq_fetcher.py +3 -3
  116. cg/services/analysis_starter/submitters/seqera_platform/{client.py → seqera_platform_client.py} +19 -3
  117. cg/services/analysis_starter/submitters/seqera_platform/seqera_platform_submitter.py +73 -0
  118. cg/services/analysis_starter/submitters/submitter.py +1 -1
  119. cg/services/analysis_starter/submitters/subprocess/submitter.py +2 -1
  120. cg/services/analysis_starter/tracker/implementations/balsamic.py +22 -0
  121. cg/services/analysis_starter/tracker/implementations/microsalt.py +4 -4
  122. cg/services/analysis_starter/tracker/implementations/mip_dna.py +4 -1
  123. cg/services/analysis_starter/tracker/implementations/{nextflow.py → nextflow_tracker.py} +6 -4
  124. cg/services/analysis_starter/tracker/tracker.py +19 -15
  125. cg/services/deliver_files/factory.py +1 -1
  126. cg/services/delivery_message/messages/__init__.py +24 -14
  127. cg/services/delivery_message/messages/{microsalt_mwr_message.py → microsalt_message.py} +1 -1
  128. cg/services/delivery_message/utils.py +4 -40
  129. cg/services/illumina/backup/backup_service.py +29 -7
  130. cg/services/orders/validation/constants.py +3 -0
  131. cg/services/orders/validation/index_sequences.py +558 -0
  132. cg/services/orders/validation/order_types/microsalt/models/sample.py +2 -3
  133. cg/services/run_devices/pacbio/data_storage_service/pacbio_store_service.py +39 -18
  134. cg/services/run_devices/pacbio/data_transfer_service/data_transfer_service.py +8 -2
  135. cg/services/run_devices/pacbio/data_transfer_service/dto.py +9 -3
  136. cg/services/run_devices/pacbio/data_transfer_service/utils.py +14 -7
  137. cg/services/run_devices/pacbio/metrics_parser/models.py +1 -0
  138. cg/services/run_devices/pacbio/sequencing_runs_service.py +35 -7
  139. cg/services/sequencing_qc_service/quality_checks/checks.py +18 -16
  140. cg/services/sequencing_qc_service/quality_checks/utils.py +82 -18
  141. cg/services/sequencing_qc_service/sequencing_qc_service.py +12 -10
  142. cg/store/crud/create.py +73 -42
  143. cg/store/crud/read.py +73 -7
  144. cg/store/crud/update.py +14 -3
  145. cg/store/models.py +98 -35
  146. cg/store/store.py +8 -1
  147. {cg-76.0.0.dist-info → cg-83.14.0.dist-info}/METADATA +1 -1
  148. {cg-76.0.0.dist-info → cg-83.14.0.dist-info}/RECORD +150 -138
  149. cg/services/analysis_starter/submitters/seqera_platform/submitter.py +0 -39
  150. cg/services/delivery_message/messages/microsalt_mwx_message.py +0 -18
  151. {cg-76.0.0.dist-info → cg-83.14.0.dist-info}/WHEEL +0 -0
  152. {cg-76.0.0.dist-info → cg-83.14.0.dist-info}/entry_points.txt +0 -0
cg/server/admin.py CHANGED
@@ -12,6 +12,7 @@ from wtforms.form import Form
12
12
 
13
13
  from cg.constants.constants import NG_UL_SUFFIX, CaseActions, DataDelivery, Workflow
14
14
  from cg.models.orders.constants import OrderType
15
+ from cg.server.app_config import app_config
15
16
  from cg.server.ext import applications_service, db, sample_service
16
17
  from cg.server.utils import MultiCheckboxField
17
18
  from cg.store.models import Application
@@ -111,12 +112,6 @@ def view_pacbio_sample_sequencing_metrics_link(unused1, unused2, model, unused3)
111
112
  )
112
113
 
113
114
 
114
- def is_external_application(unused1, unused2, model, unused3):
115
- """column formatter to open this view"""
116
- del unused1, unused2, unused3
117
- return model.application_version.application.is_external if model.application_version else ""
118
-
119
-
120
115
  def view_order_types(unused1, unused2, model, unused3):
121
116
  del unused1, unused2, unused3
122
117
  order_type_list = "<br>".join(model.order_types)
@@ -197,6 +192,44 @@ def view_customer_link(unused1, unused2, model, unused3):
197
192
  return markup
198
193
 
199
194
 
195
+ def view_ticket_link(unused1, unused2, model, attribute_name):
196
+ """Column formatter used to add hyperlink to ticket ID, if applicable."""
197
+ del unused1, unused2
198
+
199
+ # Ticket attribute called differently from models, infer attribute name from args
200
+ ticket_attr = getattr(model, attribute_name)
201
+ ticket_str = str(ticket_attr).strip()
202
+
203
+ # Freshdesk ticket IDs have >=7 digits
204
+ if len(ticket_str) >= 7:
205
+ ticket_link = f"{app_config.freshdesk_url}/a/tickets/{ticket_str}"
206
+ ticket_markup = Markup(f"<a href='{ticket_link}'>{ticket_str}</a>")
207
+ return ticket_markup
208
+ else:
209
+ return ticket_str
210
+
211
+
212
+ def view_tickets_links(unused1, unused2, model, unused3):
213
+ """Column formatter used to add hyperlinks to comma-separated ticket IDs, if applicable. Assumes attribute name 'tickets'."""
214
+ del unused1, unused2, unused3
215
+
216
+ tickets_list = str(model.tickets).strip().split(sep=",")
217
+
218
+ tickets_markups = []
219
+ for ticket in tickets_list:
220
+ ticket_str = str(ticket).strip()
221
+
222
+ # Freshdesk ticket IDs have >=7 digits
223
+ if len(ticket_str) >= 7:
224
+ ticket_link = f"{app_config.freshdesk_url}/a/tickets/{ticket_str}"
225
+ ticket_markup = Markup(f"<a href='{ticket_link}'>{ticket_str}</a>")
226
+ tickets_markups.append(ticket_markup)
227
+ else:
228
+ tickets_markups.append(ticket_str)
229
+
230
+ return Markup(", ".join(tickets_markups))
231
+
232
+
200
233
  class ApplicationView(BaseView):
201
234
  """Admin view for Model.Application"""
202
235
 
@@ -207,6 +240,8 @@ class ApplicationView(BaseView):
207
240
  "is_accredited",
208
241
  "target_reads",
209
242
  "percent_reads_guaranteed",
243
+ "target_hifi_yield",
244
+ "percent_hifi_yield_guaranteed",
210
245
  "comment",
211
246
  "prep_category",
212
247
  "sequencing_depth",
@@ -267,10 +302,11 @@ class ApplicationView(BaseView):
267
302
  def on_model_change(self, form: Form, model: Application, is_created: bool):
268
303
  """Override to persist entries to the OrderTypeApplication table."""
269
304
  super(ApplicationView, self).on_model_change(form=form, model=model, is_created=is_created)
270
- order_types: list[OrderType] = form["suitable_order_types"].data
271
- applications_service.update_application_order_types(
272
- application=model, order_types=order_types
273
- )
305
+ if "suitable_order_types" in form.data:
306
+ order_types: list[OrderType] = form["suitable_order_types"].data
307
+ applications_service.update_application_order_types(
308
+ application=model, order_types=order_types
309
+ )
274
310
 
275
311
  def edit_form(self, obj=None):
276
312
  """Override to prefill the order types according to the current Application entry."""
@@ -435,6 +471,7 @@ class CaseView(BaseView):
435
471
  "customer": view_customer_link,
436
472
  "internal_id": view_case_sample_link,
437
473
  "priority": view_priority,
474
+ "tickets": view_tickets_links,
438
475
  }
439
476
  column_searchable_list = [
440
477
  "internal_id",
@@ -650,7 +687,10 @@ class OrderView(BaseView):
650
687
 
651
688
  column_default_sort = ("order_date", True)
652
689
  column_editable_list = ["is_open"]
653
- column_formatters = {"customer": view_customer_link}
690
+ column_formatters = {
691
+ "customer": view_customer_link,
692
+ "ticket_id": view_ticket_link,
693
+ }
654
694
  column_searchable_list = ["id", "ticket_id"]
655
695
  column_display_pk = True
656
696
  create_modal = True
@@ -691,10 +731,36 @@ class PoolView(BaseView):
691
731
  class SampleView(BaseView):
692
732
  """Admin view for Model.Sample"""
693
733
 
694
- column_exclude_list = [
695
- "age_at_sampling",
696
- "_phenotype_groups",
697
- "_phenotype_terms",
734
+ column_list = [
735
+ "application_version",
736
+ "customer",
737
+ "organism",
738
+ "invoice",
739
+ "is_cancelled",
740
+ "capture_kit",
741
+ "comment",
742
+ "control",
743
+ "created_at",
744
+ "delivered_at",
745
+ "downsampled_to",
746
+ "from_sample",
747
+ "internal_id",
748
+ "is_tumour",
749
+ "loqusdb_id",
750
+ "name",
751
+ "no_invoice",
752
+ "order",
753
+ "ordered_at",
754
+ "original_ticket",
755
+ "prepared_at",
756
+ "priority",
757
+ "reads",
758
+ "hifi_yield",
759
+ "last_sequenced_at",
760
+ "received_at",
761
+ "reference_genome",
762
+ "sex",
763
+ "subject_id",
698
764
  ]
699
765
  column_default_sort = ("created_at", True)
700
766
  column_editable_list = [
@@ -714,9 +780,9 @@ class SampleView(BaseView):
714
780
  column_formatters = {
715
781
  "application_version": view_application_link_via_application_version,
716
782
  "customer": view_customer_link,
717
- "is_external": is_external_application,
718
783
  "internal_id": view_case_sample_link,
719
784
  "invoice": InvoiceView.view_invoice_link,
785
+ "original_ticket": view_ticket_link,
720
786
  "priority": view_priority,
721
787
  }
722
788
  column_searchable_list = [
@@ -834,12 +900,12 @@ class IlluminaSampleSequencingMetricsView(BaseView):
834
900
  column_searchable_list = ["sample.internal_id", "instrument_run.device.internal_id"]
835
901
 
836
902
 
837
- class PacbioSmrtCellView(BaseView):
903
+ class PacbioSmrtCellMetricsView(BaseView):
838
904
  """Admin view for Model.PacbioSMRTCell"""
839
905
 
840
906
  column_list = (
841
907
  "internal_id",
842
- "run_name",
908
+ "sequencing_run.run_name",
843
909
  "movie_name",
844
910
  "well",
845
911
  "plate",
@@ -863,8 +929,9 @@ class PacbioSmrtCellView(BaseView):
863
929
  "internal_id": view_pacbio_sample_sequencing_metrics_link,
864
930
  "model": view_smrt_cell_model,
865
931
  }
932
+ column_labels = {"sequencing_run.run_name": "Run Name"}
866
933
  column_default_sort = ("completed_at", True)
867
- column_searchable_list = ["device.internal_id", "run_name", "movie_name"]
934
+ column_searchable_list = ["device.internal_id", "movie_name", "sequencing_run.run_name"]
868
935
  column_sortable_list = [
869
936
  ("internal_id", "device.internal_id"),
870
937
  "started_at",
@@ -880,7 +947,7 @@ class PacbioSmrtCellView(BaseView):
880
947
  "<a href='%s'>%s</a>"
881
948
  % (
882
949
  url_for(
883
- "pacbiosequencingrun.index_view",
950
+ "pacbiosmrtcellmetrics.index_view",
884
951
  search=model.instrument_run.device.internal_id,
885
952
  ),
886
953
  model.instrument_run.device.internal_id,
@@ -892,16 +959,30 @@ class PacbioSmrtCellView(BaseView):
892
959
 
893
960
 
894
961
  class PacbioSampleRunMetricsView(BaseView):
962
+ column_filters = [
963
+ "instrument_run.plate",
964
+ "instrument_run.sequencing_run.run_name",
965
+ ]
966
+ column_formatters = {
967
+ "smrt_cell": PacbioSmrtCellMetricsView.view_smrt_cell_link,
968
+ "sample": SampleView.view_sample_link,
969
+ }
970
+ column_labels = {
971
+ "instrument_run.plate": "Plate",
972
+ "instrument_run.sequencing_run.run_name": "Run Name",
973
+ }
895
974
  column_list = [
896
975
  "smrt_cell",
897
976
  "sample",
977
+ "instrument_run.sequencing_run.run_name",
978
+ "instrument_run.plate",
898
979
  "hifi_reads",
899
980
  "hifi_yield",
900
981
  "hifi_mean_read_length",
901
982
  "hifi_median_read_quality",
902
983
  ]
903
- column_formatters = {
904
- "smrt_cell": PacbioSmrtCellView.view_smrt_cell_link,
905
- "sample": SampleView.view_sample_link,
906
- }
907
- column_searchable_list = ["sample.internal_id", "instrument_run.device.internal_id"]
984
+ column_searchable_list = [
985
+ "sample.internal_id",
986
+ "instrument_run.device.internal_id",
987
+ "instrument_run.sequencing_run.run_name",
988
+ ]
cg/server/app.py CHANGED
@@ -19,7 +19,12 @@ from cg.server.endpoints.sequencing_metrics.illumina_sequencing_metrics import F
19
19
  from cg.server.endpoints.sequencing_metrics.pacbio_sequencing_metrics import (
20
20
  PACBIO_SAMPLE_SEQUENCING_METRICS_BLUEPRINT,
21
21
  )
22
- from cg.server.endpoints.sequencing_run.pacbio_sequencing_run import PACBIO_SEQUENCING_RUN_BLUEPRINT
22
+ from cg.server.endpoints.sequencing_run.pacbio_sequencing_run import (
23
+ PACBIO_SEQUENCING_RUNS_BLUEPRINT,
24
+ )
25
+ from cg.server.endpoints.sequencing_run.pacbio_smrt_cell_metrics import (
26
+ PACBIO_SMRT_CELL_METRICS_BLUEPRINT,
27
+ )
23
28
  from cg.server.endpoints.users import USERS_BLUEPRINT
24
29
  from cg.store.database import get_scoped_session_registry
25
30
  from cg.store.models import (
@@ -39,7 +44,7 @@ from cg.store.models import (
39
44
  Order,
40
45
  Organism,
41
46
  PacbioSampleSequencingMetrics,
42
- PacbioSequencingRun,
47
+ PacbioSMRTCellMetrics,
43
48
  Panel,
44
49
  Pool,
45
50
  Sample,
@@ -107,7 +112,8 @@ def _register_blueprints(app: Flask):
107
112
  app.register_blueprint(ANALYSES_BLUEPRINT)
108
113
  app.register_blueprint(USERS_BLUEPRINT)
109
114
  app.register_blueprint(PACBIO_SAMPLE_SEQUENCING_METRICS_BLUEPRINT)
110
- app.register_blueprint(PACBIO_SEQUENCING_RUN_BLUEPRINT)
115
+ app.register_blueprint(PACBIO_SEQUENCING_RUNS_BLUEPRINT)
116
+ app.register_blueprint(PACBIO_SMRT_CELL_METRICS_BLUEPRINT)
111
117
  app.register_blueprint(INDEX_SEQUENCES_BLUEPRINT)
112
118
  _register_admin_views()
113
119
 
@@ -119,6 +125,7 @@ def _register_blueprints(app: Flask):
119
125
  ext.csrf.exempt(FLOW_CELLS_BLUEPRINT)
120
126
  ext.csrf.exempt(ANALYSES_BLUEPRINT)
121
127
  ext.csrf.exempt(USERS_BLUEPRINT)
128
+ ext.csrf.exempt(PACBIO_SEQUENCING_RUNS_BLUEPRINT)
122
129
 
123
130
  @app.route("/")
124
131
  def index():
@@ -158,7 +165,11 @@ def _register_admin_views():
158
165
  ext.admin.add_view(
159
166
  admin.IlluminaSampleSequencingMetricsView(IlluminaSampleSequencingMetrics, ext.db.session)
160
167
  )
161
- ext.admin.add_view(admin.PacbioSmrtCellView(PacbioSequencingRun, ext.db.session))
168
+ ext.admin.add_view(
169
+ admin.PacbioSmrtCellMetricsView(
170
+ PacbioSMRTCellMetrics, ext.db.session, "Pacbio SMRT Cell Metrics"
171
+ )
172
+ )
162
173
  ext.admin.add_view(
163
174
  admin.PacbioSampleRunMetricsView(PacbioSampleSequencingMetrics, ext.db.session)
164
175
  )
@@ -3,7 +3,7 @@ from datetime import datetime
3
3
  from pydantic import BaseModel
4
4
 
5
5
 
6
- class PacbioSequencingRunDTO(BaseModel):
6
+ class PacbioSmrtCellMetricsDTO(BaseModel):
7
7
  barcoded_hifi_mean_read_length: int
8
8
  barcoded_hifi_reads: int
9
9
  barcoded_hifi_reads_percentage: float
@@ -24,5 +24,23 @@ class PacbioSequencingRunDTO(BaseModel):
24
24
  well: str
25
25
 
26
26
 
27
- class PacbioSequencingRunsResponse(BaseModel):
28
- runs: list[PacbioSequencingRunDTO]
27
+ class PacbioSmrtCellMetricsResponse(BaseModel):
28
+ runs: list[PacbioSmrtCellMetricsDTO]
29
+
30
+
31
+ class PacbioSequencingRunDTO(BaseModel):
32
+ id: int
33
+ run_name: str
34
+ comment: str
35
+ processed: bool
36
+
37
+
38
+ class PacbioSequencingRunResponse(BaseModel):
39
+ pacbio_sequencing_runs: list[PacbioSequencingRunDTO]
40
+ total_count: int
41
+
42
+
43
+ class PacbioSequencingRunUpdateRequest(BaseModel):
44
+ id: int
45
+ comment: str | None = None
46
+ processed: bool | None = None
@@ -1,20 +1,39 @@
1
- from flask import Blueprint, jsonify
1
+ from http import HTTPStatus
2
+
3
+ from flask import Blueprint, Response, jsonify, request
4
+ from pydantic import ValidationError
2
5
 
3
6
  from cg.server.endpoints.error_handler import handle_missing_entries
4
- from cg.server.endpoints.sequencing_run.dtos import PacbioSequencingRunsResponse
7
+ from cg.server.endpoints.sequencing_run.dtos import (
8
+ PacbioSequencingRunResponse,
9
+ PacbioSequencingRunUpdateRequest,
10
+ )
5
11
  from cg.server.endpoints.utils import before_request
6
12
  from cg.server.ext import pacbio_sequencing_runs_service
7
13
 
8
- PACBIO_SEQUENCING_RUN_BLUEPRINT = Blueprint(
9
- "pacbio_sequencing_run", __name__, url_prefix="/api/v1/pacbio_sequencing_run"
14
+ PACBIO_SEQUENCING_RUNS_BLUEPRINT = Blueprint(
15
+ "pacbio_sequencing_runs", __name__, url_prefix="/api/v1/"
10
16
  )
11
- PACBIO_SEQUENCING_RUN_BLUEPRINT.before_request(before_request)
17
+ PACBIO_SEQUENCING_RUNS_BLUEPRINT.before_request(before_request)
12
18
 
13
19
 
14
- @PACBIO_SEQUENCING_RUN_BLUEPRINT.route("/<run_name>", methods=["GET"])
20
+ @PACBIO_SEQUENCING_RUNS_BLUEPRINT.route("/pacbio_sequencing_runs", methods=["GET"])
15
21
  @handle_missing_entries
16
- def get_sequencing_runs(run_name: str):
17
- response: PacbioSequencingRunsResponse = (
18
- pacbio_sequencing_runs_service.get_sequencing_runs_by_name(run_name)
22
+ def get_sequencing_runs():
23
+ page: int = int(request.args.get("page", "0"))
24
+ page_size: int = int(request.args.get("pageSize", "0"))
25
+ sequencing_runs: PacbioSequencingRunResponse = (
26
+ pacbio_sequencing_runs_service.get_sequencing_runs(page=page, page_size=page_size)
19
27
  )
20
- return jsonify(response.model_dump())
28
+ return jsonify(sequencing_runs.model_dump())
29
+
30
+
31
+ @PACBIO_SEQUENCING_RUNS_BLUEPRINT.route("/pacbio_sequencing_runs/<id>", methods=["PATCH"])
32
+ @handle_missing_entries
33
+ def update_sequencing_run(id: str):
34
+ try:
35
+ update_request = PacbioSequencingRunUpdateRequest(id=id, **request.json)
36
+ except ValidationError:
37
+ return Response(status=HTTPStatus.UNPROCESSABLE_ENTITY)
38
+ pacbio_sequencing_runs_service.update_sequencing_run(update_request=update_request)
39
+ return Response(status=HTTPStatus.NO_CONTENT)
@@ -0,0 +1,20 @@
1
+ from flask import Blueprint, jsonify
2
+
3
+ from cg.server.endpoints.error_handler import handle_missing_entries
4
+ from cg.server.endpoints.sequencing_run.dtos import PacbioSmrtCellMetricsResponse
5
+ from cg.server.endpoints.utils import before_request
6
+ from cg.server.ext import pacbio_sequencing_runs_service
7
+
8
+ PACBIO_SMRT_CELL_METRICS_BLUEPRINT = Blueprint(
9
+ "pacbio_smrt_cell_metrics", __name__, url_prefix="/api/v1/pacbio_smrt_cell_metrics"
10
+ )
11
+ PACBIO_SMRT_CELL_METRICS_BLUEPRINT.before_request(before_request)
12
+
13
+
14
+ @PACBIO_SMRT_CELL_METRICS_BLUEPRINT.route("/<run_name>", methods=["GET"])
15
+ @handle_missing_entries
16
+ def get_smrt_cell_metrics(run_name: str):
17
+ response: PacbioSmrtCellMetricsResponse = (
18
+ pacbio_sequencing_runs_service.get_sequencing_runs_by_name(run_name)
19
+ )
20
+ return jsonify(response.model_dump())
@@ -1,8 +1,10 @@
1
1
  import logging
2
2
  from subprocess import CalledProcessError
3
3
 
4
+ from requests import HTTPError
5
+
4
6
  from cg.constants import Workflow
5
- from cg.exc import AnalysisNotReadyError
7
+ from cg.exc import AnalysisNotReadyError, SeqeraError
6
8
  from cg.services.analysis_starter.configurator.abstract_model import CaseConfig
7
9
  from cg.services.analysis_starter.configurator.configurator import Configurator
8
10
  from cg.services.analysis_starter.input_fetcher.input_fetcher import InputFetcher
@@ -51,20 +53,20 @@ class AnalysisStarter:
51
53
  LOG.info(f"Starting case {case_id}")
52
54
  self.tracker.ensure_analysis_not_ongoing(case_id)
53
55
  self.input_fetcher.ensure_files_are_ready(case_id)
54
- parameters: CaseConfig = self.configurator.configure(case_id=case_id, **flags)
55
- self._run_and_track(case_id=case_id, parameters=parameters)
56
+ case_config: CaseConfig = self.configurator.configure(case_id=case_id, **flags)
57
+ self._run_and_track(case_id=case_id, case_config=case_config)
56
58
 
57
59
  def run(self, case_id: str, **flags) -> None:
58
60
  """Run a case using an assumed existing configuration."""
59
61
  self.tracker.ensure_analysis_not_ongoing(case_id)
60
- parameters: CaseConfig = self.configurator.get_config(case_id=case_id, **flags)
61
- self._run_and_track(case_id=case_id, parameters=parameters)
62
+ case_config: CaseConfig = self.configurator.get_config(case_id=case_id, **flags)
63
+ self._run_and_track(case_id=case_id, case_config=case_config)
62
64
 
63
- def _run_and_track(self, case_id: str, parameters: CaseConfig):
65
+ def _run_and_track(self, case_id: str, case_config: CaseConfig):
64
66
  self.tracker.set_case_as_running(case_id)
65
67
  try:
66
- tower_workflow_id: str | None = self.submitter.submit(parameters)
67
- self.tracker.track(case_config=parameters, tower_workflow_id=tower_workflow_id)
68
- except CalledProcessError as exception:
68
+ submitted_case_config: CaseConfig = self.submitter.submit(case_config)
69
+ self.tracker.track(case_config=submitted_case_config)
70
+ except (CalledProcessError, HTTPError, SeqeraError) as exception:
69
71
  self.tracker.set_case_as_not_running(case_id)
70
72
  raise exception
@@ -6,3 +6,11 @@ from cg.constants import Workflow
6
6
  class CaseConfig(BaseModel):
7
7
  case_id: str
8
8
  workflow: Workflow
9
+
10
+ def get_session_id(self) -> str | None:
11
+ # This will be set by the NextflowCaseConfig
12
+ return None
13
+
14
+ def get_workflow_id(self) -> str | None:
15
+ # This will be set by the NextflowCaseConfig
16
+ return None
@@ -20,7 +20,7 @@ class Configurator(ABC):
20
20
  @staticmethod
21
21
  def _set_flags(config: SpecificCaseConfig, **flags) -> SpecificCaseConfig:
22
22
  curated_flags: dict = {key: value for key, value in flags.items() if value is not None}
23
- return config.model_copy(update=curated_flags)
23
+ return config.model_validate(config.model_dump(by_alias=True) | curated_flags)
24
24
 
25
25
  @abstractmethod
26
26
  def _ensure_required_config_files_exist(self, **kwargs) -> None:
@@ -0,0 +1,27 @@
1
+ from pathlib import Path
2
+
3
+ from cg.constants.scout import ScoutExportFileName
4
+ from cg.services.analysis_starter.configurator.extensions.pipeline_extension import (
5
+ PipelineExtension,
6
+ )
7
+ from cg.services.analysis_starter.configurator.file_creators.gene_panel import GenePanelFileCreator
8
+
9
+
10
+ class NalloExtension(PipelineExtension):
11
+ def __init__(self, gene_panel_file_creator: GenePanelFileCreator):
12
+ self.gene_panel_file_creator = gene_panel_file_creator
13
+
14
+ def configure(self, case_id: str, case_run_directory: Path) -> None:
15
+ self.gene_panel_file_creator.create(
16
+ case_id=case_id,
17
+ file_path=self._get_gene_panel_file_path(case_run_directory),
18
+ double_hashtag_filtering=True,
19
+ )
20
+
21
+ def do_required_files_exist(self, case_run_directory: Path) -> bool:
22
+ gene_panel_file_path: Path = self._get_gene_panel_file_path(case_run_directory)
23
+ return gene_panel_file_path.is_file()
24
+
25
+ @staticmethod
26
+ def _get_gene_panel_file_path(case_run_directory: Path) -> Path:
27
+ return case_run_directory.joinpath(ScoutExportFileName.PANELS_TSV)
@@ -7,5 +7,5 @@ class PipelineExtension:
7
7
  can be used."""
8
8
  pass
9
9
 
10
- def do_required_files_exist(self, **kwargs) -> bool:
10
+ def do_required_files_exist(self, case_run_directory: Path) -> bool:
11
11
  return True
@@ -1,7 +1,9 @@
1
1
  from os.path import isfile
2
2
  from pathlib import Path
3
3
 
4
- from cg.services.analysis_starter.configurator.extensions.abstract import PipelineExtension
4
+ from cg.services.analysis_starter.configurator.extensions.pipeline_extension import (
5
+ PipelineExtension,
6
+ )
5
7
  from cg.services.analysis_starter.configurator.file_creators.gene_panel import GenePanelFileCreator
6
8
  from cg.services.analysis_starter.configurator.file_creators.managed_variants import (
7
9
  ManagedVariantsFileCreator,
@@ -0,0 +1,28 @@
1
+ from os.path import isfile
2
+ from pathlib import Path
3
+
4
+ from cg.constants.scout import ScoutExportFileName
5
+ from cg.services.analysis_starter.configurator.extensions.pipeline_extension import (
6
+ PipelineExtension,
7
+ )
8
+ from cg.services.analysis_starter.configurator.file_creators.gene_panel import GenePanelFileCreator
9
+
10
+
11
+ class TomteExtension(PipelineExtension):
12
+ def __init__(self, gene_panel_file_creator: GenePanelFileCreator):
13
+ self.gene_panel_file_creator = gene_panel_file_creator
14
+
15
+ def configure(self, case_id: str, case_run_directory: Path) -> None:
16
+ self.gene_panel_file_creator.create(
17
+ case_id=case_id,
18
+ file_path=self._get_gene_panel_file_path(case_run_directory),
19
+ double_hashtag_filtering=False,
20
+ )
21
+
22
+ def do_required_files_exist(self, case_run_directory: Path) -> bool:
23
+ gene_panel_file_path: Path = self._get_gene_panel_file_path(case_run_directory)
24
+ return isfile(gene_panel_file_path)
25
+
26
+ @staticmethod
27
+ def _get_gene_panel_file_path(case_run_directory: Path) -> Path:
28
+ return case_run_directory.joinpath(ScoutExportFileName.PANELS)