cg 83.13.3__py3-none-any.whl → 83.15.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.
cg/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
1
  __title__ = "cg"
2
- __version__ = "83.13.3"
2
+ __version__ = "83.15.0"
cg/exc.py CHANGED
@@ -354,3 +354,7 @@ class MissingConfigFilesError(CgError):
354
354
 
355
355
  class SeqeraError(CgError):
356
356
  """Exception raised when receiving an unexpected response from Seqera platform"""
357
+
358
+
359
+ class ApplicationDoesNotHaveHiFiYieldError(CgError):
360
+ """Exception raised when application does not have HiFi yield set."""
@@ -1,12 +1,14 @@
1
+ from collections.abc import Callable
1
2
  from enum import Enum
2
- from typing import Callable
3
3
 
4
4
  from cg.constants import Workflow
5
5
  from cg.services.sequencing_qc_service.quality_checks.utils import (
6
6
  all_samples_in_case_have_reads,
7
7
  any_sample_in_case_has_reads,
8
- case_pass_sequencing_qc,
9
- sample_pass_sequencing_qc,
8
+ case_pass_sequencing_qc_on_hifi_yield,
9
+ case_pass_sequencing_qc_on_reads,
10
+ raw_data_case_pass_qc,
11
+ sample_pass_sequencing_qc_on_reads,
10
12
  )
11
13
  from cg.store.models import Case
12
14
 
@@ -18,10 +20,12 @@ class QualityCheck(Enum):
18
20
 
19
21
 
20
22
  class SequencingQCCheck(QualityCheck):
21
- CASE_PASSES: Callable = case_pass_sequencing_qc
22
- SAMPLE_PASSES: Callable = sample_pass_sequencing_qc
23
+ CASE_PASSES_ON_READS: Callable = case_pass_sequencing_qc_on_reads
24
+ CASE_PASSES_ON_YIELD: Callable = case_pass_sequencing_qc_on_hifi_yield
25
+ SAMPLE_PASSES: Callable = sample_pass_sequencing_qc_on_reads
23
26
  ALL_SAMPLES_IN_CASE_HAVE_READS: Callable = all_samples_in_case_have_reads
24
27
  ANY_SAMPLE_IN_CASE_HAS_READS: Callable = any_sample_in_case_has_reads
28
+ RAW_DATA_CASE_QC: Callable = raw_data_case_pass_qc
25
29
 
26
30
 
27
31
  def get_sequencing_quality_check_for_case(case: Case) -> Callable:
@@ -29,20 +33,20 @@ def get_sequencing_quality_check_for_case(case: Case) -> Callable:
29
33
  workflow: Workflow = case.data_analysis
30
34
 
31
35
  workflow_qc_mapping = {
32
- Workflow.BALSAMIC: SequencingQCCheck.CASE_PASSES,
33
- Workflow.BALSAMIC_PON: SequencingQCCheck.CASE_PASSES,
34
- Workflow.BALSAMIC_UMI: SequencingQCCheck.CASE_PASSES,
35
- Workflow.MIP_DNA: SequencingQCCheck.CASE_PASSES,
36
- Workflow.MIP_RNA: SequencingQCCheck.CASE_PASSES,
37
- Workflow.RAREDISEASE: SequencingQCCheck.CASE_PASSES,
38
- Workflow.RNAFUSION: SequencingQCCheck.CASE_PASSES,
39
- Workflow.TOMTE: SequencingQCCheck.CASE_PASSES,
36
+ Workflow.BALSAMIC: SequencingQCCheck.CASE_PASSES_ON_READS,
37
+ Workflow.BALSAMIC_PON: SequencingQCCheck.CASE_PASSES_ON_READS,
38
+ Workflow.BALSAMIC_UMI: SequencingQCCheck.CASE_PASSES_ON_READS,
40
39
  Workflow.FLUFFY: SequencingQCCheck.ANY_SAMPLE_IN_CASE_HAS_READS,
41
- Workflow.RAW_DATA: SequencingQCCheck.ANY_SAMPLE_IN_CASE_HAS_READS,
42
40
  Workflow.MICROSALT: SequencingQCCheck.ANY_SAMPLE_IN_CASE_HAS_READS,
41
+ Workflow.MIP_DNA: SequencingQCCheck.CASE_PASSES_ON_READS,
42
+ Workflow.MIP_RNA: SequencingQCCheck.CASE_PASSES_ON_READS,
43
43
  Workflow.MUTANT: SequencingQCCheck.ANY_SAMPLE_IN_CASE_HAS_READS,
44
+ Workflow.NALLO: SequencingQCCheck.CASE_PASSES_ON_YIELD,
45
+ Workflow.RAREDISEASE: SequencingQCCheck.CASE_PASSES_ON_READS,
46
+ Workflow.RAW_DATA: SequencingQCCheck.RAW_DATA_CASE_QC,
47
+ Workflow.RNAFUSION: SequencingQCCheck.CASE_PASSES_ON_READS,
44
48
  Workflow.TAXPROFILER: SequencingQCCheck.ALL_SAMPLES_IN_CASE_HAVE_READS,
45
- Workflow.NALLO: SequencingQCCheck.ALL_SAMPLES_IN_CASE_HAVE_READS,
49
+ Workflow.TOMTE: SequencingQCCheck.CASE_PASSES_ON_READS,
46
50
  }
47
51
 
48
52
  if workflow in workflow_qc_mapping:
@@ -55,5 +59,5 @@ def get_sample_sequencing_quality_check() -> Callable:
55
59
  return SequencingQCCheck.SAMPLE_PASSES
56
60
 
57
61
 
58
- def run_quality_checks(quality_checks: list[QualityCheck], **kwargs) -> bool:
62
+ def run_quality_checks(quality_checks: list[Callable], **kwargs) -> bool:
59
63
  return all(quality_check(**kwargs) for quality_check in quality_checks)
@@ -1,13 +1,15 @@
1
1
  import logging
2
2
 
3
+ from cg.constants.devices import DeviceType
3
4
  from cg.constants.priority import Priority
4
5
  from cg.constants.sequencing import SeqLibraryPrepCategory
6
+ from cg.exc import ApplicationDoesNotHaveHiFiYieldError
5
7
  from cg.store.models import Case, Sample
6
8
 
7
9
  LOG = logging.getLogger(__name__)
8
10
 
9
11
 
10
- def case_pass_sequencing_qc(case: Case) -> bool:
12
+ def case_pass_sequencing_qc_on_reads(case: Case) -> bool:
11
13
  """
12
14
  Get the sequencing QC of a case. The checks are performed in the following order:
13
15
  1. If the case is a ready-made library, the ready made library QC is used.
@@ -21,22 +23,66 @@ def case_pass_sequencing_qc(case: Case) -> bool:
21
23
  if is_case_ready_made_library(case):
22
24
  return ready_made_library_case_pass_sequencing_qc(case)
23
25
  if is_case_express_priority(case):
24
- return express_case_pass_sequencing_qc(case)
26
+ return express_case_pass_sequencing_qc_on_reads(case)
25
27
  return all(sample_has_enough_reads(sample) for sample in case.samples)
26
28
 
27
29
 
28
- def express_case_pass_sequencing_qc(case: Case) -> bool:
30
+ def case_pass_sequencing_qc_on_hifi_yield(case: Case) -> bool:
31
+ """
32
+ Get the sequencing QC of a case using yield. The checks are performed in the following order:
33
+ 1. If the case is express priority, the express QC is used.
34
+ 2. If the above condition is not met then it checks if all samples have enough yield.
35
+
36
+ The express QC should be checked before the standard QC.
37
+ """
38
+ if is_case_express_priority(case):
39
+ return express_case_pass_sequencing_qc_on_hifi_yield(case)
40
+ return all(sample_has_enough_hifi_yield(sample) for sample in case.samples)
41
+
42
+
43
+ def express_case_pass_sequencing_qc_on_reads(case: Case) -> bool:
29
44
  """
30
45
  Checks if all samples in an express case have enough reads.
31
46
  """
32
47
  return all(express_sample_has_enough_reads(sample) for sample in case.samples)
33
48
 
34
49
 
35
- def express_sample_pass_sequencing_qc(sample: Sample) -> bool:
50
+ def express_case_pass_sequencing_qc_on_hifi_yield(case: Case) -> bool:
51
+ """
52
+ Checks if all samples in an express case have enough hifi yield.
53
+ """
54
+ return all(express_sample_has_enough_yield(sample) for sample in case.samples)
55
+
56
+
57
+ def express_sample_pass_sequencing_qc_on_reads(sample: Sample) -> bool:
36
58
  return express_sample_has_enough_reads(sample)
37
59
 
38
60
 
39
- def sample_pass_sequencing_qc(sample: Sample) -> bool:
61
+ def express_sample_has_enough_reads(sample: Sample) -> bool:
62
+ """
63
+ Checks if given express sample has enough reads. Gets the threshold from the sample's
64
+ application version.
65
+ """
66
+ express_reads_threshold: int = get_express_reads_threshold_for_sample(sample)
67
+ enough_reads: bool = sample.reads >= express_reads_threshold
68
+ if not enough_reads:
69
+ LOG.warning(f"Sample {sample.internal_id} has too few reads.")
70
+ return enough_reads
71
+
72
+
73
+ def express_sample_has_enough_yield(sample: Sample) -> bool:
74
+ if not sample.hifi_yield:
75
+ LOG.debug(f"Sample {sample.internal_id} has no hifi yield.")
76
+ return False
77
+
78
+ express_yield_threshold: int = get_express_yield_threshold_for_sample(sample)
79
+ enough_yield: bool = sample.hifi_yield >= express_yield_threshold
80
+ if not enough_yield:
81
+ LOG.warning(f"Sample {sample.internal_id} does not have enough yield.")
82
+ return enough_yield
83
+
84
+
85
+ def sample_pass_sequencing_qc_on_reads(sample: Sample) -> bool:
40
86
  """
41
87
  Get the standard sequencing QC of a sample. The checks are performed in the following order:
42
88
  1. If the sample is express priority, the express QC is used.
@@ -47,7 +93,7 @@ def sample_pass_sequencing_qc(sample: Sample) -> bool:
47
93
  checked before the standard QC.
48
94
  """
49
95
  if is_sample_express_priority(sample):
50
- return express_sample_pass_sequencing_qc(sample)
96
+ return express_sample_pass_sequencing_qc_on_reads(sample)
51
97
  if is_sample_ready_made_library(sample):
52
98
  return ready_made_library_sample_has_enough_reads(sample)
53
99
  return sample_has_enough_reads(sample)
@@ -87,6 +133,31 @@ def any_sample_in_case_has_reads(case: Case) -> bool:
87
133
  return passed_quality_check
88
134
 
89
135
 
136
+ def raw_data_case_pass_qc(case: Case) -> bool:
137
+ if is_case_ready_made_library(case):
138
+ return ready_made_library_case_pass_sequencing_qc(case)
139
+ if is_first_sample_yield_based_and_processed(case):
140
+ return all(sample_has_enough_hifi_yield(sample) for sample in case.samples)
141
+ elif is_first_sample_reads_based_and_processed(case):
142
+ return all(sample_has_enough_reads(sample) for sample in case.samples)
143
+ LOG.warning(f"Not all samples for case {case.internal_id} have been post-processed.")
144
+ return False
145
+
146
+
147
+ def is_first_sample_yield_based_and_processed(case: Case) -> bool:
148
+ sample: Sample = case.samples[0]
149
+ if metrics := sample.sample_run_metrics:
150
+ return metrics[0].type == DeviceType.PACBIO
151
+ return False
152
+
153
+
154
+ def is_first_sample_reads_based_and_processed(case: Case) -> bool:
155
+ sample: Sample = case.samples[0]
156
+ if metrics := sample.sample_run_metrics:
157
+ return metrics[0].type == DeviceType.ILLUMINA
158
+ return False
159
+
160
+
90
161
  def is_case_express_priority(case: Case) -> bool:
91
162
  """
92
163
  Check if a case is express priority.
@@ -94,18 +165,6 @@ def is_case_express_priority(case: Case) -> bool:
94
165
  return case.priority == Priority.express
95
166
 
96
167
 
97
- def express_sample_has_enough_reads(sample: Sample) -> bool:
98
- """
99
- Checks if given express sample has enough reads. Gets the threshold from the sample's
100
- application version.
101
- """
102
- express_reads_threshold: int = get_express_reads_threshold_for_sample(sample)
103
- enough_reads: bool = sample.reads >= express_reads_threshold
104
- if not enough_reads:
105
- LOG.warning(f"Sample {sample.internal_id} has too few reads.")
106
- return enough_reads
107
-
108
-
109
168
  def get_express_reads_threshold_for_sample(sample: Sample) -> int:
110
169
  """
111
170
  Get the express reads threshold for a sample.
@@ -113,6 +172,15 @@ def get_express_reads_threshold_for_sample(sample: Sample) -> int:
113
172
  return round(sample.application_version.application.target_reads / 2)
114
173
 
115
174
 
175
+ def get_express_yield_threshold_for_sample(sample: Sample) -> int:
176
+ if threshold := sample.application_version.application.expected_express_hifi_yield:
177
+ return threshold
178
+ else:
179
+ raise ApplicationDoesNotHaveHiFiYieldError(
180
+ f"Application for sample {sample.internal_id} does not have target HiFi yield."
181
+ )
182
+
183
+
116
184
  def is_sample_ready_made_library(sample: Sample) -> bool:
117
185
  return sample.prep_category == SeqLibraryPrepCategory.READY_MADE_LIBRARY
118
186
 
@@ -143,6 +211,28 @@ def sample_has_enough_reads(sample: Sample) -> bool:
143
211
  return enough_reads
144
212
 
145
213
 
214
+ def sample_has_enough_hifi_yield(sample: Sample) -> bool:
215
+ """
216
+ Return true if the sample's HiFi yield is greater than or equal to the threshold.
217
+ Returns false if the HiFi yield is lower than the threshold or None.
218
+ Raises:
219
+ ApplicationDoesNotHaveHiFiYieldError if the sample doesn't have expected HiFi yield.
220
+ """
221
+ if not sample.expected_hifi_yield:
222
+ raise ApplicationDoesNotHaveHiFiYieldError(
223
+ f"Application for sample {sample.internal_id} does not have target HiFi yield."
224
+ )
225
+
226
+ if not sample.hifi_yield:
227
+ LOG.debug(f"Sample {sample.internal_id} has no hifi yield.")
228
+ return False
229
+
230
+ enough_hifi_yield: bool = sample.hifi_yield >= sample.expected_hifi_yield
231
+ if not enough_hifi_yield:
232
+ LOG.warning(f"Sample {sample.internal_id} does not have enough HiFi yield.")
233
+ return enough_hifi_yield
234
+
235
+
146
236
  def is_sample_express_priority(sample: Sample) -> bool:
147
237
  """
148
238
  Check if a sample is express priority.
@@ -2,15 +2,13 @@ import logging
2
2
  from typing import Callable
3
3
 
4
4
  from cg.constants.constants import SequencingQCStatus
5
- from cg.services.sequencing_qc_service.utils import qc_bool_to_status
6
- from typing import Callable
7
-
8
- from cg.store.models import Case, Sample
9
5
  from cg.services.sequencing_qc_service.quality_checks.checks import (
10
- run_quality_checks,
11
- get_sequencing_quality_check_for_case,
12
6
  get_sample_sequencing_quality_check,
7
+ get_sequencing_quality_check_for_case,
8
+ run_quality_checks,
13
9
  )
10
+ from cg.services.sequencing_qc_service.utils import qc_bool_to_status
11
+ from cg.store.models import Case, Sample
14
12
  from cg.store.store import Store
15
13
 
16
14
  LOG = logging.getLogger(__name__)
@@ -24,10 +22,14 @@ class SequencingQCService:
24
22
  """Run QC for samples in pending or failed cases and store the aggregated score on each case."""
25
23
  cases: list[Case] = self.store.get_cases_for_sequencing_qc()
26
24
  for case in cases:
27
- passes_qc: bool = self.case_pass_sequencing_qc(case)
28
- qc_status: SequencingQCStatus = qc_bool_to_status(passes_qc)
29
- self.store.update_sequencing_qc_status(case=case, status=qc_status)
30
- LOG.info(f"Sequencing QC status for case {case.internal_id}: {qc_status}")
25
+ LOG.debug(f"Performing sequencing QC for case: {case.internal_id}")
26
+ try:
27
+ passes_qc: bool = self.case_pass_sequencing_qc(case)
28
+ qc_status: SequencingQCStatus = qc_bool_to_status(passes_qc)
29
+ self.store.update_sequencing_qc_status(case=case, status=qc_status)
30
+ LOG.info(f"Sequencing QC status for case {case.internal_id}: {qc_status}")
31
+ except Exception as e:
32
+ LOG.error(f"Error found during sequencing QC of case: {case.internal_id}: {e}")
31
33
 
32
34
  @staticmethod
33
35
  def case_pass_sequencing_qc(case: Case) -> bool:
cg/store/models.py CHANGED
@@ -193,9 +193,20 @@ class Application(Base):
193
193
  return self.tag
194
194
 
195
195
  @property
196
- def expected_reads(self):
196
+ def expected_reads(self) -> float:
197
197
  return self.target_reads * self.percent_reads_guaranteed / 100
198
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
+
199
210
  @property
200
211
  def analysis_type(self) -> str:
201
212
  if self.prep_category == SeqLibraryPrepCategory.WHOLE_TRANSCRIPTOME_SEQUENCING.value:
@@ -774,10 +785,15 @@ class Sample(Base, PriorityMixin):
774
785
  return self.customer.data_archive_location
775
786
 
776
787
  @property
777
- def expected_reads_for_sample(self) -> int:
788
+ def expected_reads_for_sample(self) -> float | None:
778
789
  """Return the expected reads of the sample."""
779
790
  return self.application_version.application.expected_reads
780
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
+
781
797
  @property
782
798
  def has_reads(self) -> bool:
783
799
  return bool(self.reads)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cg
3
- Version: 83.13.3
3
+ Version: 83.15.0
4
4
  Summary: Clinical Genomics command center
5
5
  Requires-Python: >=3.11,<3.13
6
6
  Classifier: Programming Language :: Python
@@ -1,4 +1,4 @@
1
- cg/__init__.py,sha256=7BuDkW3ecOxdQ17R23C4DkKxsn1kIqKjJSk0PKbZr7U,41
1
+ cg/__init__.py,sha256=LzHKmL8dVmAmq-6AHs2IvH5WkbB5nsiiArjFCiCZoEA,41
2
2
  cg/apps/__init__.py,sha256=pYf0vxo4iYQqURzFRYzqpOCdV8Cm9MWx0GHvJOz0EMg,315
3
3
  cg/apps/coverage/__init__.py,sha256=dJtsmNf8tODE2-VEomMIoYA7ugLYZAk_upsfOQCZeF8,27
4
4
  cg/apps/coverage/api.py,sha256=e_ozC3QeNKoEfpjjMaL-XjeBLtz-JySWccrtw0E9mLM,2940
@@ -225,7 +225,7 @@ cg/constants/slurm.py,sha256=_Y_InISKpGKT-oVMnES7k7Csz2G1l52MNtJwBZm0prI,313
225
225
  cg/constants/subject.py,sha256=EE26Emu6T3_HWvsWeuI5GRGGIvB0BwCjN53KHrsr18U,577
226
226
  cg/constants/symbols.py,sha256=c06D1mYFWstZbVEgiby2lUNriswUPVRU5TnSSy889tY,71
227
227
  cg/constants/tb.py,sha256=UJHdsu109oR_zxvQrko9SqFANiSUn_UJTw5BFebWcds,621
228
- cg/exc.py,sha256=QUeoBKw6V4w4G3w2uts9smCd7LejfsLTuza5O0FI6r4,8289
228
+ cg/exc.py,sha256=IYYRa6saq17OT8pA_PBdiorf2XyXTaxNV20JBkdx8QE,8418
229
229
  cg/io/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
230
230
  cg/io/api.py,sha256=WaiompYmK4bkl94fZkGzP2CTsQPmHaTSvy0oD0SKw4s,1037
231
231
  cg/io/controller.py,sha256=4TTHm9fNY7Ti3S0r-uhxB3oycJ-Ni5Y44lFj5Yvl3_I,2978
@@ -853,9 +853,9 @@ cg/services/sample_run_metrics_service/dtos.py,sha256=bJ0BL-QkqXIAAIg3F9edIdzAa_
853
853
  cg/services/sample_run_metrics_service/sample_run_metrics_service.py,sha256=aQvOuorTG97lUbw7OREO8nsPhO3ouQ38RwksstGwguQ,1795
854
854
  cg/services/sample_run_metrics_service/utils.py,sha256=BwICxptnhGg8oBghCLhzHaMNu0LuEV40cSYz1e8mB0A,829
855
855
  cg/services/sequencing_qc_service/__init__.py,sha256=OaL9dyyI6B8uRjopTIoD1zcX_H-SbGj46ZM-V-aoq6w,88
856
- cg/services/sequencing_qc_service/quality_checks/checks.py,sha256=3V_N0jecDkqF9KBEPIZa3RVuzFEDLJnlmQ9c6FIwpfM,2293
857
- cg/services/sequencing_qc_service/quality_checks/utils.py,sha256=54dvy30YMbbwfo99hnSQAfnIoV7m0G-BQ00BnsumULQ,5325
858
- cg/services/sequencing_qc_service/sequencing_qc_service.py,sha256=uE5bu7xfTHB8CYRZ4uVzryfaSE42Ou6YbkvyormCobE,1754
856
+ cg/services/sequencing_qc_service/quality_checks/checks.py,sha256=qdcHRwZD7uxrOmacyiAG4-EN6nG5z-r4rLxcReU5kSI,2593
857
+ cg/services/sequencing_qc_service/quality_checks/utils.py,sha256=2WN7S4kYsN_9iYPx7d4coAYcWHeueQWrPP2pJ8ztMMc,8969
858
+ cg/services/sequencing_qc_service/sequencing_qc_service.py,sha256=2QIJ9IDZeMtO74ZHJ9bfzyK3-RhDK-m9xaoKsi9RHt8,1969
859
859
  cg/services/sequencing_qc_service/utils.py,sha256=5WHJltBICfRODEzFVPFTdCbGNaMKCaVeOswHgIOGgFc,199
860
860
  cg/services/slurm_service/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
861
861
  cg/services/slurm_service/slurm_cli_service.py,sha256=t-XZoAOPgL-eufwK0A6H8WJvGtfWnlIYxnvKZJIX5-Y,702
@@ -904,7 +904,7 @@ cg/store/filters/status_panel_filters.py,sha256=qMYhIsrF9G3mjCvL9b9O2l_cmD3SfPWh
904
904
  cg/store/filters/status_pool_filters.py,sha256=XcYqe2z5k_q09xpp4cleDMQ4Q3o2y7UPCkB0QUkl1ck,4308
905
905
  cg/store/filters/status_sample_filters.py,sha256=mRpt_ik7niMjBo9K7pP8cmdfDMdr0B3I6Tt2MWXkLoc,9656
906
906
  cg/store/filters/status_user_filters.py,sha256=sMwKeWqgEtqv8gyhbN_Uf5huPUxbOt5qEqMWVu67Zys,1328
907
- cg/store/models.py,sha256=MsKSG9UBiXySCEsODMRDmntjpjkVCd_rlRpqTEw2TZ8,43274
907
+ cg/store/models.py,sha256=dSJRWdvqFmgIbYTlQbsZD8utH10BIjbtUKERybnlssI,43915
908
908
  cg/store/store.py,sha256=mz1TfKPlanH2uQyhZdwe_8VKg0IdFolmromKLu668IY,630
909
909
  cg/utils/__init__.py,sha256=gGjdV2l_hfWFCTybU6dwDk_FcItM88EIHNPaX6g1qUk,30
910
910
  cg/utils/calculations.py,sha256=zLVJO6nNw6n7AW7fHZI56UnQ23lqJrShB4Hh2ow6-TQ,349
@@ -923,7 +923,7 @@ cg/utils/flask/enum.py,sha256=xwNVtFPkSzoloJctLHu7obRyxcng1GJrhkeYkqwf9tw,1052
923
923
  cg/utils/mapping.py,sha256=oZpZW2kgsbtAP2FZ7RtRPELiEE1zZk_nAGisHGtCOUo,491
924
924
  cg/utils/time.py,sha256=_VOglhrFEZ5cwHK1U1g36SdwzB7UvV-Nvlt4ymuZUho,1501
925
925
  cg/utils/utils.py,sha256=RciI_UhWcnG_pMZrmQZ1ZYb-O1N0DweTYMmhE0SIRgQ,1410
926
- cg-83.13.3.dist-info/METADATA,sha256=DhvQrRopWX7HGbwF1GgTJthjXpxPR4BgcpxTs9hMhg4,4940
927
- cg-83.13.3.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
928
- cg-83.13.3.dist-info/entry_points.txt,sha256=q5f47YQQGltzK_xnIq1mDopRXXEItr85Xe1BCtG-Wts,39
929
- cg-83.13.3.dist-info/RECORD,,
926
+ cg-83.15.0.dist-info/METADATA,sha256=wj51NyMN6wmwfhFYzc6eCNr6yc2UzVNWHh5V_r30Vgs,4940
927
+ cg-83.15.0.dist-info/WHEEL,sha256=3ny-bZhpXrU6vSQ1UPG34FoxZBp3lVcvK0LkgUz6VLk,88
928
+ cg-83.15.0.dist-info/entry_points.txt,sha256=q5f47YQQGltzK_xnIq1mDopRXXEItr85Xe1BCtG-Wts,39
929
+ cg-83.15.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.2.1
2
+ Generator: poetry-core 2.3.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any