pypsmcbor 0.0.3__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 (54) hide show
  1. pypsmcbor/__init__.py +37 -0
  2. pypsmcbor/cbor2/__init__.py +1 -0
  3. pypsmcbor/cbor2/cborstreamdec.py +274 -0
  4. pypsmcbor/comparsample.py +72 -0
  5. pypsmcbor/data/typst/global_report.typ +59 -0
  6. pypsmcbor/data/typst/mz_delta_plot.typ +118 -0
  7. pypsmcbor/data/typst/psm_report.typ +14 -0
  8. pypsmcbor/data/typst/psm_report_svg.typ +16 -0
  9. pypsmcbor/enums.py +42 -0
  10. pypsmcbor/fastasequence.py +20 -0
  11. pypsmcbor/harvest/__init__.py +1 -0
  12. pypsmcbor/harvest/harvest_psmcbor.py +86 -0
  13. pypsmcbor/identificationdataset.py +97 -0
  14. pypsmcbor/ods/__init__.py +0 -0
  15. pypsmcbor/ods/ods_all_tables.py +43 -0
  16. pypsmcbor/ods/ods_informations.py +46 -0
  17. pypsmcbor/ods/ods_ms_delta.py +52 -0
  18. pypsmcbor/ods/ods_protein.py +39 -0
  19. pypsmcbor/ods/ods_sample_scan.py +218 -0
  20. pypsmcbor/peaks/__init__.py +2 -0
  21. pypsmcbor/peaks/peakscomparsample.py +58 -0
  22. pypsmcbor/peaks/peakspeptidepsm.py +139 -0
  23. pypsmcbor/peaks/peakspeptidesample.py +82 -0
  24. pypsmcbor/peaks/peakspeptidescan.py +136 -0
  25. pypsmcbor/peaks/peakspeptidetable.py +101 -0
  26. pypsmcbor/peaks/peaksscanpair.py +52 -0
  27. pypsmcbor/proforma/__init__.py +1 -0
  28. pypsmcbor/proforma/proforma.py +79 -0
  29. pypsmcbor/protein.py +77 -0
  30. pypsmcbor/proteinmap.py +78 -0
  31. pypsmcbor/proteinref.py +62 -0
  32. pypsmcbor/psm.py +214 -0
  33. pypsmcbor/sample.py +145 -0
  34. pypsmcbor/scan.py +237 -0
  35. pypsmcbor/scanid.py +41 -0
  36. pypsmcbor/scanpair.py +74 -0
  37. pypsmcbor/stream/__init__.py +3 -0
  38. pypsmcbor/stream/ms2pipeval.py +58 -0
  39. pypsmcbor/stream/proformamodreplace.py +104 -0
  40. pypsmcbor/stream/psmcborpsmfilter.py +117 -0
  41. pypsmcbor/stream/psmcborreplacer.py +41 -0
  42. pypsmcbor/stream/psmcborstreamin.py +24 -0
  43. pypsmcbor/stream/psmcborstreamout.py +310 -0
  44. pypsmcbor/typst/__init__.py +0 -0
  45. pypsmcbor/typst/json_scan_report.py +174 -0
  46. pypsmcbor/utils/__init__.py +0 -0
  47. pypsmcbor/utils/aa_mass_delta_distribution.py +66 -0
  48. pypsmcbor/utils/ms_delta.py +92 -0
  49. pypsmcbor/utils/sample_ms2_distribution.py +54 -0
  50. pypsmcbor-0.0.3.dist-info/METADATA +14 -0
  51. pypsmcbor-0.0.3.dist-info/RECORD +54 -0
  52. pypsmcbor-0.0.3.dist-info/WHEEL +5 -0
  53. pypsmcbor-0.0.3.dist-info/licenses/LICENSE +232 -0
  54. pypsmcbor-0.0.3.dist-info/top_level.txt +1 -0
pypsmcbor/__init__.py ADDED
@@ -0,0 +1,37 @@
1
+ # This file is part of pypsmcbor.
2
+ #
3
+ # pypsmcbor is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # pypsmcbor is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with pypsmcbor. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ from .comparsample import (
17
+ ComparSampleMatchingScanIndex,
18
+ ComparSampleMatchingScanNativeId,
19
+ )
20
+ from .enums import MzPrecisionUnit, PeaksEngine, PsmCborMass, PsmCborSoftware
21
+ from .fastasequence import FastaSequence
22
+ from .harvest.harvest_psmcbor import HarvestPsmCbor
23
+ from .identificationdataset import IdentificationDataSet
24
+ from .peaks.peakscomparsample import PeaksComparSampleMatchingScan
25
+ from .peaks.peakspeptidetable import PeaksPeptideTable
26
+ from .proforma import ProForma
27
+ from .protein import Protein
28
+ from .proteinmap import ProteinMap
29
+ from .psm import Psm
30
+ from .sample import Sample
31
+ from .scan import Scan
32
+ from .scanid import ScanId
33
+ from .scanpair import ScanPair
34
+ from .stream.proformamodreplace import ProFormaModReplace
35
+ from .stream.psmcborpsmfilter import PsmCborPsmFilter
36
+ from .stream.psmcborstreamin import PsmCborStreamIn
37
+ from .stream.psmcborstreamout import PsmCborStreamOut
@@ -0,0 +1 @@
1
+ from .cborstreamdec import CborStreamDec
@@ -0,0 +1,274 @@
1
+ # This file is part of pypsmcbor.
2
+ #
3
+ # pypsmcbor is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # pypsmcbor is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with pypsmcbor. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+
17
+ import struct
18
+ import sys
19
+ from dataclasses import dataclass
20
+ from typing import IO, Any, cast
21
+
22
+ from cbor2 import CBORDecoder
23
+ from cbor2._types import CBORDecodeValueError
24
+
25
+
26
+ @dataclass
27
+ class ContainerEnd:
28
+ end_is_reached: bool
29
+ remaining_items: int | None
30
+
31
+
32
+ class CborStreamDec:
33
+ def __init__(self, fp: IO[bytes]):
34
+ self._fp = fp
35
+ self._cbor_decoder = CBORDecoder(fp)
36
+ self._initial_byte = None
37
+ self._major_type = 0
38
+ self._subtype = 0
39
+ self._length = None
40
+ self._container_stack = []
41
+
42
+ @property
43
+ def length(self) -> int | None:
44
+ return self._length
45
+
46
+ @property
47
+ def expected_string(self) -> str:
48
+ if self._container_stack[-1].end_is_reached:
49
+ raise CBORDecodeValueError(f"end of container reached, use leave_container")
50
+ if self._initial_byte is None:
51
+ self._read_next_initial_byte()
52
+ if self.is_string():
53
+ the_string = self._cbor_decoder.decode_string(self._subtype)
54
+ self._initial_byte = None
55
+ self._check_container_remaining_items()
56
+ return the_string
57
+ else:
58
+ raise CBORDecodeValueError(
59
+ f"next value is not a string 0x{self._major_type:x}"
60
+ )
61
+
62
+ def to_dict(self) -> Any:
63
+ """Use the internal cbor2 decoder to return the whole data as a dictionnary or array"""
64
+ if self._container_stack[-1].end_is_reached:
65
+ raise CBORDecodeValueError("end of container reached, use leave_container")
66
+ if self._initial_byte is None:
67
+ self._read_next_initial_byte()
68
+ if self.is_map():
69
+ if self.has_next():
70
+ the_map = self._cbor_decoder.decode_map(self._subtype)
71
+ self._initial_byte = None
72
+ self._check_container_remaining_items()
73
+ return the_map
74
+ elif self.is_array():
75
+ if self.has_next():
76
+ the_map = self._cbor_decoder.decode_array(self._subtype)
77
+ self._initial_byte = None
78
+ self._check_container_remaining_items()
79
+ return the_map
80
+ elif self._container_stack[-1].end_is_reached:
81
+ raise CBORDecodeValueError("end of container reached, use leave_container")
82
+ raise CBORDecodeValueError("the value is not a dict nor an array")
83
+
84
+ def value(self) -> str | int | bytes | float | None:
85
+ # 0: CBORDecoder.decode_uint,
86
+ # 1: CBORDecoder.decode_negint,
87
+ # 2: CBORDecoder.decode_bytestring,
88
+ # 3: CBORDecoder.decode_string,
89
+ # 4: CBORDecoder.decode_array,
90
+ # 5: CBORDecoder.decode_map,
91
+ # 6: CBORDecoder.decode_semantic,
92
+ # 7: CBORDecoder.decode_special,
93
+ if self._container_stack[-1].end_is_reached:
94
+ raise CBORDecodeValueError(f"end of container reached, use leave_container")
95
+ if self._initial_byte is None:
96
+ self._read_next_initial_byte()
97
+ if self.is_string():
98
+ the_string = self._cbor_decoder.decode_string(self._subtype)
99
+ self._initial_byte = None
100
+ self._check_container_remaining_items()
101
+ return the_string
102
+ elif self.is_uint():
103
+ the_uint = self._cbor_decoder.decode_uint(self._subtype)
104
+ self._initial_byte = None
105
+ self._check_container_remaining_items()
106
+ return the_uint
107
+ elif self.is_negint():
108
+ the_negint = self._cbor_decoder.decode_negint(self._subtype)
109
+ self._initial_byte = None
110
+ self._check_container_remaining_items()
111
+ return the_negint
112
+ elif self.is_bytestring():
113
+ the_bytestr = self._cbor_decoder.decode_bytestring(self._subtype)
114
+ self._initial_byte = None
115
+ self._check_container_remaining_items()
116
+ return the_bytestr
117
+ elif self._major_type == 7:
118
+ if self.has_next():
119
+ the_special = self._cbor_decoder.decode_special(self._subtype)
120
+ self._initial_byte = None
121
+ self._check_container_remaining_items()
122
+ return the_special
123
+ elif self.is_container():
124
+ raise CBORDecodeValueError(
125
+ "the next value is a container, use enter_container or to_dict to proceed"
126
+ )
127
+ else:
128
+ raise CBORDecodeValueError(
129
+ f"next value is not taken into account 0x{self._major_type:x}"
130
+ )
131
+
132
+ def _check_container_remaining_items(self):
133
+ if len(self._container_stack) > 0:
134
+ item_number = self._container_stack[-1].remaining_items
135
+ if item_number is not None:
136
+ self._container_stack[-1].remaining_items = item_number - 1
137
+ if self._container_stack[-1].remaining_items == 0:
138
+ self._container_stack[-1].end_is_reached = True
139
+
140
+ def has_next(self) -> bool:
141
+ # print(f"1next major type is 0x{self._major_type:x} length{self._length}")
142
+ if self._container_stack[-1].end_is_reached:
143
+ return False
144
+ if self._initial_byte is None:
145
+ self._read_next_initial_byte()
146
+ # print(f"next major type is 0x{self._major_type:x}")
147
+ if self._major_type == 7:
148
+ if self._subtype == 31:
149
+ # print("special subtype", self._subtype)
150
+ self._initial_byte = None
151
+ self._container_stack[-1].end_is_reached = True
152
+ return False
153
+ else:
154
+ pass
155
+ # print("special subtype", self._subtype)
156
+
157
+ return True
158
+
159
+ def next(self) -> bool:
160
+ """Advance the CBOR stream decoding one element. You should usually call this function when parsing fixed-width basic elements (that is, integers, simple values, tags and floating point values). But this function can be called when the current item is a string, array or map too and it will skip over that entire element, including all contained elements."""
161
+ if self._container_stack[-1].end_is_reached:
162
+ return False
163
+ self._cbor_decoder.decode()
164
+ self._initial_byte = None
165
+ self._check_container_remaining_items()
166
+ return True
167
+
168
+ def _read_next_initial_byte(self):
169
+ self._initial_byte = self._cbor_decoder.read(1)[0]
170
+ self._major_type = self._initial_byte >> 5
171
+ self._subtype = self._initial_byte & 31
172
+ self._length = None
173
+
174
+ def is_array(self) -> bool:
175
+ # 0: CBORDecoder.decode_uint,
176
+ # 1: CBORDecoder.decode_negint,
177
+ # 2: CBORDecoder.decode_bytestring,
178
+ # 3: CBORDecoder.decode_string,
179
+ # 4: CBORDecoder.decode_array,
180
+ # 5: CBORDecoder.decode_map,
181
+ # 6: CBORDecoder.decode_semantic,
182
+ # 7: CBORDecoder.decode_special,
183
+ if self._initial_byte is None:
184
+ self._read_next_initial_byte()
185
+ if self._major_type == 4:
186
+ return True
187
+ return False
188
+
189
+ def is_negint(self) -> bool:
190
+ if self._initial_byte is None:
191
+ self._read_next_initial_byte()
192
+ if self._major_type == 1:
193
+ return True
194
+ return False
195
+
196
+ def is_bytestring(self) -> bool:
197
+ if self._initial_byte is None:
198
+ self._read_next_initial_byte()
199
+ if self._major_type == 2:
200
+ return True
201
+ return False
202
+
203
+ def is_uint(self) -> bool:
204
+ if self._initial_byte is None:
205
+ self._read_next_initial_byte()
206
+ if self._major_type == 0:
207
+ return True
208
+ return False
209
+
210
+ def is_string(self) -> bool:
211
+ if self._initial_byte is None:
212
+ self._read_next_initial_byte()
213
+ if self._major_type == 3:
214
+ return True
215
+ return False
216
+
217
+ def is_map(self) -> bool:
218
+ if self._initial_byte is None:
219
+ self._read_next_initial_byte()
220
+ if self._major_type == 5:
221
+ return True
222
+ return False
223
+
224
+ def is_container(self) -> bool:
225
+ if self.is_array() or self.is_map():
226
+ return True
227
+ return False
228
+
229
+ def leave_container(self) -> bool:
230
+ if self._container_stack[-1].end_is_reached:
231
+ self._container_stack.pop()
232
+ return True
233
+ return False
234
+
235
+ def enter_container(self) -> bool:
236
+ if self.is_container():
237
+ self._check_container_remaining_items()
238
+ self._length = self._decode_length(self._subtype, allow_indefinite=True)
239
+ items = self._length
240
+ if items is not None:
241
+ if self.is_map():
242
+ items = items * 2
243
+ self._initial_byte = None
244
+ c_end = ContainerEnd(end_is_reached=False, remaining_items=items)
245
+ self._container_stack.append(c_end)
246
+ if self._length is None:
247
+ return True
248
+ else:
249
+ if self._length > sys.maxsize:
250
+ raise CBORDecodeValueError(
251
+ f"invalid length for array 0x{self._length:x}"
252
+ )
253
+ return True
254
+ return False
255
+
256
+ def _decode_length(
257
+ self, subtype: int, allow_indefinite: bool = False
258
+ ) -> int | None:
259
+ if subtype < 24:
260
+ return subtype
261
+ elif subtype == 24:
262
+ return self._cbor_decoder.read(1)[0]
263
+ elif subtype == 25:
264
+ return cast(int, struct.unpack(">H", self._cbor_decoder.read(2))[0])
265
+ elif subtype == 26:
266
+ return cast(int, struct.unpack(">L", self._cbor_decoder.read(4))[0])
267
+ elif subtype == 27:
268
+ return cast(int, struct.unpack(">Q", self._cbor_decoder.read(8))[0])
269
+ elif subtype == 31 and allow_indefinite:
270
+ return None
271
+ else:
272
+ raise CBORDecodeValueError(
273
+ f"unknown unsigned integer subtype 0x{subtype:x}"
274
+ )
@@ -0,0 +1,72 @@
1
+ # This file is part of pypsmcbor.
2
+ #
3
+ # pypsmcbor is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # pypsmcbor is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with pypsmcbor. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+
17
+ from .sample import Sample
18
+ from .scanpair import ScanPair
19
+
20
+
21
+ class ComparSampleMatchingScanBase(object):
22
+ def __init__(self, sample_data1: Sample, sample_data2: Sample, compar_scan):
23
+ self.scan_pair_list = []
24
+ for scan_data1 in sample_data1.scan_list:
25
+ for scan_data2 in sample_data2.scan_list:
26
+ if compar_scan(scan_data1, scan_data2):
27
+ self.scan_pair_list.append(ScanPair(scan_data1, scan_data2))
28
+
29
+ @property
30
+ def count_scan_pair(self) -> int:
31
+ return len(self.scan_pair_list)
32
+
33
+ def find_if(self, check_scanpair) -> list[ScanPair]:
34
+ """find scanpair that respect the check_scanpair conditions
35
+ :param check_scanpair: a function accepting a scanpair as argument
36
+ and return true if conditions are met"""
37
+ scan_pair_list = []
38
+ for scan_pair in self.scan_pair_list:
39
+ if check_scanpair(scan_pair):
40
+ scan_pair_list.append(scan_pair)
41
+ return scan_pair_list
42
+
43
+ def count_if(self, check_scanpair) -> int:
44
+ """count scanpair that respect the check_scanpair conditions
45
+ :param check_scanpair: a function accepting a scanpair as argument
46
+ and return true if conditions are met"""
47
+ count = 0
48
+ for scan_pair in self.scan_pair_list:
49
+ if check_scanpair(scan_pair):
50
+ count = count + 1
51
+ return count
52
+
53
+ def count_scan_containing_similar_sequence(self) -> int:
54
+ return self.count_if(lambda scan_pair: scan_pair.has_similar_sequence())
55
+
56
+
57
+ class ComparSampleMatchingScanNativeId(ComparSampleMatchingScanBase):
58
+ def __init__(self, sample_data1: Sample, sample_data2: Sample):
59
+ super().__init__(
60
+ sample_data1,
61
+ sample_data2,
62
+ lambda scan1, scan2: scan1.id.native_id == scan2.id.native_id,
63
+ )
64
+
65
+
66
+ class ComparSampleMatchingScanIndex(ComparSampleMatchingScanBase):
67
+ def __init__(self, sample_data1: Sample, sample_data2: Sample):
68
+ super().__init__(
69
+ sample_data1,
70
+ sample_data2,
71
+ lambda scan1, scan2: scan1.has_index(scan2.id.index),
72
+ )
@@ -0,0 +1,59 @@
1
+ #import "@preview/proteograph:0.2.2": *
2
+
3
+ #import "mz_delta_plot.typ": aa-mass-delta-plot, mz-delta-plot
4
+
5
+ = Global report
6
+
7
+
8
+
9
+ #let psm_json_global = json("psm_data.json").global
10
+
11
+ == #psm_json_global.samples.keys().len() samples
12
+
13
+ #for (name, sample) in psm_json_global.samples.pairs() {
14
+ terms.item(name, [#sample.peaklist_file.name
15
+ #if name in psm_json_global.mzstats {
16
+ let count_usable_scans = 0
17
+ if "tic" in psm_json_global.mzstats.at(name) {
18
+ let xic0 = psm_json_global.mzstats.at(name).at("tic")
19
+ xic0.insert("TIC", "TIC")
20
+
21
+ xic-plot(
22
+ height: 10cm,
23
+ title: "Total Ion Count",
24
+ xic0,
25
+ )
26
+ }
27
+ if "ms_level_count" in psm_json_global.mzstats.at(name) {
28
+ let ms_level_count = psm_json_global.mzstats.at(name).at("ms_level_count")
29
+ for (ms_level, count) in ms_level_count.pairs() {
30
+ terms.item([MS level #ms_level], [#count scans])
31
+ if int(ms_level) > 1 {
32
+ count_usable_scans += count
33
+ }
34
+ }
35
+ }
36
+
37
+ if "psm_count" in psm_json_global.mzstats.at(name) {
38
+ let psm_count = psm_json_global.mzstats.at(name).at("psm_count")
39
+ terms.item(
40
+ [Coverage],
41
+ [#calc.round(psm_count.scans * 100 / count_usable_scans, digits: 2)% #psm_count.scans assigned on #count_usable_scans scans],
42
+ )
43
+
44
+ terms.item(
45
+ [PSM FDR],
46
+ [#calc.round((psm_count.psms - psm_count.target_psms) * 100 / psm_count.psms, digits: 2)% (total PSMs: #psm_count.psms)],
47
+ )
48
+ }
49
+ }
50
+ ])
51
+ }
52
+
53
+ #mz-delta-plot(psm_json_global.ppm_delta)
54
+
55
+ #mz-delta-plot(psm_json_global.dalton_delta, unit: "dalton")
56
+
57
+ #if "aa_mass_delta" in psm_json_global {
58
+ aa-mass-delta-plot(psm_json_global.aa_mass_delta)
59
+ }
@@ -0,0 +1,118 @@
1
+ #import "@preview/lilaq:0.6.0" as lq
2
+
3
+ #let mz-delta-diagram(unit: "ppm") = it => {
4
+ show: lq.set-diagram(
5
+ xlabel: [$m/z$ #sym.Delta (observed - theoretical) in #unit],
6
+ ylabel: [PSM count],
7
+ legend: none,
8
+ xaxis: (mirror: none),
9
+ yaxis: (mirror: none),
10
+ )
11
+ it
12
+ }
13
+
14
+
15
+ /// Generates a XIC plot.
16
+ /// -> content
17
+ #let mz-delta-plot(
18
+ /// The width of the diagram. This can be
19
+ /// - A `length`; in this case, it defines just the width of the data area,
20
+ /// excluding axes, labels, title etc.
21
+ /// - A `ratio` or `relative` where the ratio part is relative to the width
22
+ /// of the parent that the diagram is placed in. This is not allowed if the
23
+ /// parent has an unbounded width, e.g., a page with `width: auto`.
24
+ /// -> length | relative
25
+ width: 15cm,
26
+ /// The height of the diagram. This can be
27
+ /// - A `length`; in this case, it defines just the height of the data area,
28
+ /// excluding axes, labels, title etc.
29
+ /// - A `ratio` or `relative` where the ratio part is relative to the height
30
+ /// of the parent that the diagram is placed in. This is not allowed if the
31
+ /// parent has an unbounded height, e.g., a page with `height: auto`.
32
+ /// -> length | relative
33
+ height: 10cm,
34
+ /// Graph title
35
+ /// -> content
36
+ title: none,
37
+ unit: "ppm",
38
+ /// dictionary containing delta histogram
39
+ delta,
40
+ ) = {
41
+ show: lq.cond-set(lq.grid.with(kind: "x"), stroke: orange.lighten(50%))
42
+ show: lq.cond-set(lq.grid.with(kind: "y"), stroke: none)
43
+ show: mz-delta-diagram(unit: unit)
44
+
45
+ let color-cycle = lq.color.map.petroff6
46
+
47
+ let ylimit = auto
48
+
49
+ lq.diagram(
50
+ width: width,
51
+ height: height,
52
+ title: title,
53
+ lq.bar(
54
+ delta.interval,
55
+ delta.count,
56
+ ),
57
+
58
+ lq.place(10%, 10%, box(baseline: top, [#set align(left)
59
+ / std dev: $#calc.round(delta.stddev, digits: 2)$
60
+ / mean: $#if delta.mean > 0 { sym.plus }#calc.round(delta.mean, digits: 2)$
61
+ / median: $#if delta.median > 0 { sym.plus }#calc.round(delta.median, digits: 2)$
62
+ ])),
63
+ )
64
+ }
65
+
66
+ #let aa-mass-delta-plot(
67
+ /// The width of the diagram. This can be
68
+ /// - A `length`; in this case, it defines just the width of the data area,
69
+ /// excluding axes, labels, title etc.
70
+ /// - A `ratio` or `relative` where the ratio part is relative to the width
71
+ /// of the parent that the diagram is placed in. This is not allowed if the
72
+ /// parent has an unbounded width, e.g., a page with `width: auto`.
73
+ /// -> length | relative
74
+ width: 15cm,
75
+ /// The height of the diagram. This can be
76
+ /// - A `length`; in this case, it defines just the height of the data area,
77
+ /// excluding axes, labels, title etc.
78
+ /// - A `ratio` or `relative` where the ratio part is relative to the height
79
+ /// of the parent that the diagram is placed in. This is not allowed if the
80
+ /// parent has an unbounded height, e.g., a page with `height: auto`.
81
+ /// -> length | relative
82
+ height: 10cm,
83
+ /// Graph title
84
+ /// -> content
85
+ title: none,
86
+ /// dictionary containing delta histogram
87
+ delta,
88
+ ) = {
89
+ show: lq.cond-set(lq.grid.with(kind: "y"), stroke: none)
90
+ show: lq.show_(
91
+ lq.tick-label.with(kind: "x"),
92
+ it => scale(x: 50%, y: 50%, reflow: true, box(
93
+ width: 0pt,
94
+ align(right, rotate(-45deg, reflow: true, it)),
95
+ )),
96
+ )
97
+
98
+ let ylimit = auto
99
+
100
+ lq.diagram(
101
+ width: width,
102
+ height: height,
103
+ title: "most frequent AA mass delta",
104
+ xlim: (0, 60),
105
+ xaxis: (
106
+ ticks: delta.interval.map(massf => str(massf)).enumerate(),
107
+ subticks: none,
108
+ ),
109
+ yaxis: (
110
+ label: [count],
111
+ auto-exponent-threshold: 5,
112
+ ),
113
+ lq.bar(
114
+ range(delta.interval.len()),
115
+ delta.count,
116
+ ),
117
+ )
118
+ }
@@ -0,0 +1,14 @@
1
+ #import "@preview/proteograph:0.2.4": *
2
+
3
+ #let psm_json = json("psm_data.json")
4
+
5
+ #if "global" in psm_json {
6
+ include "global_report.typ"
7
+ }
8
+
9
+
10
+ #if psm_json.scan_arr.len() > 0 {
11
+ heading([PSM scan report])
12
+
13
+ for scan in psm_json.scan_arr { psm-cbor-scan-report(scan, protein-dict: psm_json.protein_map) }
14
+ }
@@ -0,0 +1,16 @@
1
+ #import "@preview/proteograph:0.2.4": *
2
+
3
+ #set page(height: auto, width: auto, margin: 1em)
4
+
5
+ #let psm_json = json("psm_data.json")
6
+
7
+ #if "global" in psm_json {
8
+ include "global_report.typ"
9
+ }
10
+
11
+
12
+ #if psm_json.scan_arr.len() > 0 {
13
+ heading([PSM scan report])
14
+
15
+ for scan in psm_json.scan_arr { psm-cbor-scan-report(scan, protein-dict: psm_json.protein_map) }
16
+ }
pypsmcbor/enums.py ADDED
@@ -0,0 +1,42 @@
1
+ # This file is part of pypsmcbor.
2
+ #
3
+ # pypsmcbor is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # pypsmcbor is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with pypsmcbor. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ from enum import Enum
17
+
18
+
19
+ class MzPrecisionUnit(Enum):
20
+ ppm = "ppm"
21
+ dalton = "dalton"
22
+
23
+
24
+ class PeaksEngine(Enum):
25
+ db_search = "DB Search"
26
+ deepnovo = "DeepNovo"
27
+
28
+
29
+ class PsmCborSoftware(Enum):
30
+ grouping = "grouping"
31
+ sage = "sage"
32
+ spoms = "spoms"
33
+ features = "features"
34
+
35
+
36
+ class PsmCborMass(float, Enum):
37
+ MHPLUS = 1.007276466879 # The (monoisotopic) mass of the H+ ion
38
+ MPROTIUM = 1.007825032241 # The (monoisotopic) mass of the H atom
39
+ ONEMILLION = 1000000
40
+ MASSOXYGEN = 15.99491461956
41
+ MASSCARBON = 12.0
42
+ MASSNITROGEN = 14.0030740048
@@ -0,0 +1,20 @@
1
+ # This file is part of pypsmcbor.
2
+ #
3
+ # pypsmcbor is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # pypsmcbor is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with pypsmcbor. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+
17
+ class FastaSequence:
18
+ def __init__(self, header: str, sequence: str):
19
+ self.header = header
20
+ self.sequence = sequence
@@ -0,0 +1 @@
1
+ from .harvest_psmcbor import HarvestPsmCbor