sbtabpy 1.0.8__py2.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.
sbtab/SBtab.py ADDED
@@ -0,0 +1,1567 @@
1
+ """
2
+ sbtab is a table format for Systems Biology.
3
+
4
+ It can store different kinds of data. The SBtabTable and SBtabDocument
5
+ classes allow the storage, manipulation, and removal of data from the tables.
6
+
7
+ See specification for further information.
8
+ """
9
+
10
+ import datetime
11
+ import logging
12
+ import re
13
+ from pathlib import Path
14
+
15
+ from . import utils
16
+
17
+
18
+ def read_csv(filepath, document_name, xlsx=False, definitions_file=None):
19
+ """
20
+ Read an sbtab file; it can be csv, but also tsv.
21
+
22
+ Parameters
23
+ ----------
24
+ filepath: str
25
+ Path to the file that shall be read.
26
+ document_name: str
27
+ A name for the document to be created.
28
+ xlsx: Bool
29
+ Boolean flag that indicates if the file is in xlsx format.
30
+
31
+ Returns: sbtab.SBtabDocument
32
+ sbtab document which is created from the given file.
33
+ """
34
+ sbtab_file = False
35
+
36
+ if xlsx:
37
+ try:
38
+ sbtab_xlsx = open(filepath, "rb")
39
+ sbtab_tsv = utils.xlsx_to_tsv(sbtab_xlsx, f="file")
40
+ sbtab_xlsx.close()
41
+ sbtab_doc = SBtabDocument(
42
+ document_name, sbtab_tsv, filepath, definitions_file
43
+ )
44
+ return sbtab_doc
45
+ except Exception as e:
46
+ raise SBtabError("The sbtab could not be generated: %s" % (str(e)))
47
+
48
+ try:
49
+ sbtab_file = open(filepath, "r")
50
+ sbtab_doc = SBtabDocument(
51
+ document_name, sbtab_file.read(), filepath, definitions_file
52
+ )
53
+ sbtab_file.close()
54
+ return sbtab_doc
55
+ except Exception as e:
56
+ if sbtab_file:
57
+ sbtab_file.close()
58
+ raise SBtabError("The sbtab could not be generated: %s" % (str(e)))
59
+
60
+
61
+ class SBtabError(Exception):
62
+ """Base class for errors in the sbtab class."""
63
+
64
+ def __init__(self, *args):
65
+ """Initialize SBtabError with message."""
66
+ super().__init__(*args)
67
+ self.message = args[0] if args else ""
68
+
69
+ def __str__(self):
70
+ """Return string representation of error."""
71
+ return self.message
72
+
73
+
74
+ class SBtabTable:
75
+ """SBtabTable (version 1.0.0 24/12/2018)."""
76
+
77
+ def __init__(self, table_string=None, filename=None):
78
+ """
79
+ Create sbtab table object from string.
80
+
81
+ Parameters
82
+ ----------
83
+ table : str
84
+ One sbtab string from read file.
85
+ filename : str
86
+ Filename with extension.
87
+ """
88
+ if filename:
89
+ self.set_filename(filename)
90
+ else:
91
+ self.filename = None
92
+
93
+ if table_string:
94
+ self.add_sbtab_string(table_string)
95
+
96
+ self.object_type = "table"
97
+
98
+ def _validate_extension(self, test=None):
99
+ """Check the extension of the file for invalid formats."""
100
+ valid_extensions = {".tsv", ".csv", ".xlsx"}
101
+ filename = test if test is not None else self.filename
102
+ suffix = Path(filename).suffix
103
+ if suffix not in valid_extensions:
104
+ raise SBtabError(
105
+ "The file extension of %s is not valid for an sbtab file." % filename
106
+ )
107
+ return True
108
+
109
+ def _singular(self):
110
+ """
111
+ Check that SBtabTable contains only one sbtab.
112
+
113
+ If more than one SBtabs are contained, an error is issued and
114
+ the usage of SBtabDocument is suggested.
115
+ """
116
+ header_row_count = 0
117
+ for row in self.table_string.split("\n"):
118
+ if row.startswith("!!SBtab") or row.startswith("!!ObjTables"):
119
+ header_row_count += 1
120
+
121
+ if header_row_count > 1:
122
+ raise SBtabError(
123
+ "There are more than one sbtab tables in this file. Please"
124
+ " use the SBtabDocument class instead of SBtabTable."
125
+ )
126
+
127
+ def _preprocess_table_string(self, table_string):
128
+ """
129
+ Preprocess the table string to fix common formatting problems.
130
+
131
+ There is so much stuff that can be made wrong with the input files.
132
+ This function tries to catch some of the common problems.
133
+ """
134
+ table_string = table_string.replace("\r", "")
135
+ table_string = table_string.replace("^M", "")
136
+ table_string_prep = ""
137
+
138
+ for row in table_string.split("\n"):
139
+ row = self._dequote(row)
140
+ row = row.replace("=''", "X@X")
141
+ while "''" in row:
142
+ row = row.replace("''", "'")
143
+ row = row.replace("X@X", "=''")
144
+ row.replace(
145
+ "%s,%s" % (self.delimiter, self.delimiter),
146
+ "%s%s" % (self.delimiter, self.delimiter),
147
+ )
148
+ table_string_prep += row + "\n"
149
+
150
+ return table_string_prep
151
+
152
+ def _cut_table_string(self, table_string, delimiter_test=None):
153
+ """
154
+ Cut the sbtab string into a list to harvest content.
155
+
156
+ The sbtab is initially given as one long string; cut down this
157
+ string into list to harvest content.
158
+ """
159
+ if delimiter_test:
160
+ delimiter = delimiter_test
161
+ else:
162
+ delimiter = self.delimiter
163
+
164
+ table_list = []
165
+ for row in table_string.split("\n"):
166
+ if row.replace(delimiter, "") != "" and row.replace(delimiter, "") != "[]":
167
+ if not row.startswith('"!') and not row.startswith("!"):
168
+ if "'" in row or "{" in row or "[" in row:
169
+ try:
170
+ cut_row = self._handle_row(row, delimiter)
171
+ table_list.append(cut_row)
172
+ except Exception:
173
+ print(
174
+ "Row %s could not be attached due to bad syntax." % row
175
+ )
176
+ else:
177
+ table_list.append(row.split(delimiter))
178
+ else:
179
+ table_list.append(row.split(delimiter))
180
+
181
+ return table_list
182
+
183
+ def _handle_row(self, row, delimiter):
184
+ """
185
+ Handle rows that may contain characters causing format troubles.
186
+
187
+ Some rows may contain characters that cause format troubles:
188
+ quotes, commas, JSON strings, and combinations of them. This
189
+ function carefully handles these issues and cuts the row into
190
+ its correct single pieces (which are the columns).
191
+ """
192
+ # provide an anchor for the end of the row to support our regex
193
+ row += delimiter
194
+
195
+ # first, unify the employed quotes to '
196
+ row = self._dequote(row)
197
+
198
+ # then, find all quoted columns
199
+ if "'" in row or "{" in row or "[" in row:
200
+ # find beginning and start of quoted columns
201
+ iterators = re.finditer(r"('.*?')%s" % delimiter, row)
202
+ indices = [0]
203
+
204
+ for i in iterators:
205
+ indices.append(i.start())
206
+ indices.append(i.end())
207
+
208
+ # remove duplicates
209
+ indices_set = list(sorted(set(indices)))
210
+
211
+ # cut row at the beginning and start indices
212
+ items_pre = [
213
+ row[i : j - 1]
214
+ for i, j in zip(indices_set, indices_set[1:] + [len(row) + 1])
215
+ ]
216
+
217
+ # further cut row at the delimiter and finish off items
218
+ items = []
219
+ jsons = []
220
+ jlist = []
221
+ running_json = False
222
+ running_jlist = False
223
+
224
+ for item in items_pre:
225
+ # in the case of a comma as separator, we need to be careful
226
+ # with the JSONs which naturally hold commas
227
+ if delimiter == ",":
228
+ # 1st case: we have a currently open JSON column
229
+ if running_json and not item.endswith("}'"):
230
+ jsons.append(item)
231
+ # 2nd case: we have a JSON column start
232
+ elif item.startswith("'{"):
233
+ jsons.append(item)
234
+ running_json = True
235
+ if item.endswith("}'"):
236
+ while "" in jsons:
237
+ jsons.remove("")
238
+ items.append(",".join(jsons))
239
+ jsons = []
240
+ running_json = False
241
+ # 3rd case: we have a JSON column end
242
+ elif running_json and item.endswith("}'"):
243
+ jsons.append(item)
244
+ while "" in jsons:
245
+ jsons.remove("")
246
+ items.append(",".join(jsons))
247
+ jsons = []
248
+ running_json = False
249
+
250
+ # 1bst case: we have a currently open Jlist column
251
+ elif running_jlist and not item.endswith("]'"):
252
+ if item.strip() != "":
253
+ jlist.append(item)
254
+ # 2bnd case: we have a Jlist column start
255
+ elif item.startswith("'["):
256
+ jlist.append(item)
257
+ running_jlist = True
258
+ if item.endswith("]'"):
259
+ while "" in jlist:
260
+ jlist.remove("")
261
+ items.append(",".join(jlist))
262
+ jlist = []
263
+ running_jlist = False
264
+ # 3brd case: we have a Jlist column end
265
+ elif running_jlist and item.endswith("]'"):
266
+ jlist.append(item)
267
+ while "" in jlist:
268
+ jlist.remove("")
269
+ items.append(",".join(jlist))
270
+ jlist = []
271
+ running_jlist = False
272
+
273
+ # 4th case: we have a quoted column
274
+ elif (
275
+ item.startswith("'")
276
+ and not item.startswith("'{")
277
+ and not item.endswith("}'")
278
+ and not item.startswith("'[")
279
+ and not item.endswith("]'")
280
+ ):
281
+ items.append(item)
282
+
283
+ # 5th case: we have a normal column
284
+ elif item == "":
285
+ items += [""]
286
+ else:
287
+ items = items + item.split(delimiter)
288
+ else:
289
+ # for all other delimiters we are comparably easy going:
290
+ if item.startswith("'"):
291
+ items.append(item)
292
+ else:
293
+ items = items + item.split(delimiter)
294
+
295
+ # remove the last element which was added in the beginning
296
+ items.pop()
297
+
298
+ return items
299
+
300
+ def _initialize_table(self):
301
+ """Load table information and class variables."""
302
+ # read a potential document row
303
+ self.doc_row = self._get_doc_row()
304
+
305
+ # Read the header row from table
306
+ self.header_row = self._get_header_row()
307
+
308
+ # Read the table information from header row
309
+ (
310
+ self.table_id,
311
+ self.table_type,
312
+ self.table_name,
313
+ self.table_document,
314
+ self.table_version,
315
+ self.standard_concentration,
316
+ ) = self._get_table_information()
317
+
318
+ # Read the columns of the table
319
+ self.columns, self.columns_dict = self._get_columns()
320
+
321
+ # Read data rows
322
+ self.value_rows = self._get_rows()
323
+
324
+ def _get_doc_row(self):
325
+ """
326
+ Check if the sbtab Table holds a !!!-line to declare a belonging sbtab document.
327
+
328
+ See if the sbtab Table holds a !!!-line to declare a belonging sbtab
329
+ document.
330
+ """
331
+ doc_row_dq = False
332
+
333
+ for row in self.table:
334
+ for entry in row:
335
+ if str(entry).startswith("!!!"):
336
+ doc_row = row
337
+ doc_row_dq = self._dequote(doc_row)
338
+
339
+ # determine if this is an sbtab or ObjTables Document
340
+ if "!!!ObjTables" in doc_row_dq:
341
+ self.document_format = "ObjTables"
342
+ elif "!!!SBtab" in doc_row_dq:
343
+ self.document_format = "sbtab"
344
+ else:
345
+ self.document_format = None
346
+
347
+ return doc_row_dq
348
+ elif str(entry).startswith('"!!!'):
349
+ rm2 = row.replace('""', "#").replace('"', "")
350
+ doc_row = rm2.replace("#", '"')
351
+ doc_row_dq = self._dequote(doc_row)
352
+
353
+ # determine if this is an sbtab or ObjTables Document
354
+ if "!!!ObjTables" in doc_row_dq:
355
+ self.document_format = "ObjTables"
356
+ elif "!!!SBtab" in doc_row_dq:
357
+ self.document_format = "sbtab"
358
+ else:
359
+ self.document_format = None
360
+
361
+ return doc_row_dq
362
+
363
+ def _get_header_row(self):
364
+ """Extract the declaration row from the sbtab file."""
365
+ header_row = None
366
+ # Find header row
367
+ for row in self.table:
368
+ for entry in row:
369
+ if str(entry).startswith("!!") and not str(entry).startswith("!!!"):
370
+ header_row = "".join(row).rstrip("\n")
371
+ break
372
+ elif str(entry).startswith("'!!") and not str(entry).startswith("'!!!"):
373
+ rm1 = entry.replace("''", "#")
374
+ rm2 = rm1.replace("'", "")
375
+ header_row = rm2.replace("#", "'")
376
+ break
377
+
378
+ # Save string or raise error
379
+ if not header_row:
380
+ raise SBtabError("""This is not a valid sbtab table, please use
381
+ validator to check format or have a look in the specification!""")
382
+
383
+ header_row_dq = self._dequote(header_row)
384
+
385
+ # determine if this is an sbtab or ObjTables Document
386
+ if "!!ObjTables" in header_row_dq:
387
+ self.table_format = "ObjTables"
388
+ elif "!!SBtab" in header_row_dq:
389
+ self.table_format = "sbtab"
390
+ else:
391
+ raise SBtabError("""This is not a valid sbtab table, please use
392
+ validator to check format or have a look in the specification!""")
393
+
394
+ return header_row_dq
395
+
396
+ def _dequote(self, row):
397
+ """
398
+ Bring consistency in the multifarious quotation mark problems.
399
+
400
+ Bring consistency in the multifarious quotation mark problems.
401
+ """
402
+ stupid_quotes = [
403
+ "\xe2\x80\x9d",
404
+ "\xe2\x80\x98",
405
+ "\xe2\x80\x99",
406
+ "\xe2\x80\x9b",
407
+ "\xe2\x80\x9c",
408
+ "\xe2\x80\x9f",
409
+ '"',
410
+ "\xe2\x80\xb2",
411
+ "\xe2\x80\xb3",
412
+ "\xe2\x80\xb4",
413
+ "\xe2\x80\xb5",
414
+ "\xe2\x80\xb6",
415
+ "\xe2\x80\xb7",
416
+ ]
417
+
418
+ for squote in stupid_quotes:
419
+ try:
420
+ row = row.replace(squote, "'")
421
+ except Exception:
422
+ pass
423
+
424
+ return row
425
+
426
+ def _generate_random_table_id(self):
427
+ """
428
+ Generate a random TableID for backwards compatibility.
429
+
430
+ The attribute TableID is enforced in sbtab. But to remain backwards compatible,
431
+ we generate a random TableID if none is given in the input file.
432
+ """
433
+ import random
434
+
435
+ i = random.randint(0, 1000)
436
+ table_id = "ID_%s" % str(i)
437
+
438
+ return table_id
439
+
440
+ def _get_table_information(self):
441
+ """Read declaration row and store the sbtab table attributes."""
442
+ # Save table id, otherwise raise error
443
+ try:
444
+ table_id = self._get_custom_table_information("TableID")
445
+ except Exception:
446
+ table_id = self._generate_random_table_id()
447
+ if "TableID=" not in self.header_row:
448
+ self.header_row = (
449
+ self.header_row.replace(self.delimiter, "").strip()
450
+ + " TableID='%s'" % table_id
451
+ )
452
+
453
+ # Save table type, otherwise raise error
454
+ try:
455
+ if self.table_format == "sbtab":
456
+ table_type = self._get_custom_table_information("TableType")
457
+ elif self.table_format == "ObjTables":
458
+ table_type = self._get_custom_table_information("class")
459
+ except Exception:
460
+ raise SBtabError("The TableType of the sbtab is not defined!")
461
+
462
+ # Save table name, otherwise create name
463
+ try:
464
+ table_name = self._get_custom_table_information("TableName")
465
+ except Exception:
466
+ table_name = table_type.capitalize() + "_unnamed"
467
+ self.header_row += " TableName='%s'" % table_name
468
+
469
+ # Save table document, otherwise return None
470
+ try:
471
+ table_document = self._get_custom_table_information("Document")
472
+ except Exception:
473
+ table_document = None
474
+
475
+ # save table version, otherwise return None
476
+ try:
477
+ table_version = self._get_custom_table_information("SBtabVersion")
478
+ except Exception:
479
+ table_version = None
480
+
481
+ # save table version, otherwise return None
482
+ try:
483
+ standard_concentration = self._get_custom_table_information(
484
+ "StandardConcentration"
485
+ )
486
+ except Exception:
487
+ standard_concentration = None
488
+
489
+ # save date
490
+ try:
491
+ self.date = self._get_custom_table_information("Date")
492
+ except Exception:
493
+ now = datetime.datetime.now()
494
+ self.date = "-".join([str(now.year), str(now.month), str(now.day)])
495
+ if "Date=" not in self.header_row:
496
+ self.header_row = (
497
+ self.header_row.replace(self.delimiter, "")
498
+ + " Date='%s'" % self.date
499
+ )
500
+
501
+ return (
502
+ table_id,
503
+ table_type,
504
+ table_name,
505
+ table_document,
506
+ table_version,
507
+ standard_concentration,
508
+ )
509
+
510
+ def _get_custom_table_information(self, attribute_name):
511
+ """
512
+ Retrieve the value of a table attribute in the declaration line.
513
+
514
+ Parameters
515
+ ----------
516
+ attribute_name : str
517
+ Name of the table attribute.
518
+ """
519
+ if re.search("%s='([^']*)'" % attribute_name, self.header_row) is not None:
520
+ return re.search("%s='([^']*)'" % attribute_name, self.header_row).group(1)
521
+ else:
522
+ raise SBtabError(
523
+ """The %s of the sbtab is
524
+ not defined!"""
525
+ % attribute_name
526
+ )
527
+
528
+ def _get_columns(self):
529
+ """Extract column headers, add mandatory first column name if necessary."""
530
+ # Save list of main columns
531
+ for row in self.table:
532
+ for _entry in row:
533
+ if str(row[0]).startswith("!") and not str(row[0]).startswith("!!"):
534
+ column_names = list(filter(lambda a: a != "", row))
535
+ break
536
+
537
+ # Get column positions
538
+ columns = dict(map(reversed, enumerate(column_names)))
539
+
540
+ return column_names, columns
541
+
542
+ def _get_rows(self):
543
+ """Extract the rows of the sbtab."""
544
+ value_rows = []
545
+ # Add to comments, if row starts with '%'
546
+ self.comments = []
547
+
548
+ for row in self.table:
549
+ if str(row[0]).startswith("!"):
550
+ continue
551
+ elif str(row[0]).startswith("%"):
552
+ self.comments.append(list(row))
553
+ else:
554
+ if len(list(row)) >= len(self.columns):
555
+ value_rows.append(list(row)[: len(self.columns)])
556
+ else:
557
+ value_rows.append(
558
+ list(row) + [""] * (len(self.columns) - len(list(row)))
559
+ )
560
+
561
+ return value_rows
562
+
563
+ # Here, the sbtab API starts
564
+ def to_str(self):
565
+ """
566
+ Return the sbtab table as a string.
567
+
568
+ Returns: str
569
+ The sbtab table in form of a string.
570
+ """
571
+ table_string = [self.header_row]
572
+ table_string.append("\t".join(self.columns))
573
+ for row in self.value_rows:
574
+ row = "\t".join(row)
575
+ table_string.append(row)
576
+
577
+ return "\n".join(table_string)
578
+
579
+ def change_attribute(self, attribute, value):
580
+ """
581
+ Change the value of an sbtab attribute.
582
+
583
+ Parameters
584
+ ----------
585
+ attribute : str
586
+ Attribute from the sbtab object's declaration row.
587
+ value: str
588
+ Value that the attribute should take.
589
+ """
590
+ att_value_new = "%s='%s'" % (attribute, value)
591
+
592
+ if attribute not in self.header_row:
593
+ self.header_row = self.header_row + " " + att_value_new
594
+ else:
595
+ try:
596
+ att_value = re.search(
597
+ "%s='([^']*)'" % attribute, self.header_row
598
+ ).group(0)
599
+ self.header_row = self.header_row.replace(att_value, att_value_new)
600
+ except Exception:
601
+ raise SBtabError(
602
+ "Attribute value %s could not be replaced in the header."
603
+ % attribute
604
+ )
605
+
606
+ def set_filename(self, filename):
607
+ """
608
+ Set the filename of the sbtab.
609
+
610
+ Parameters
611
+ ----------
612
+ filename: str or Path
613
+ New filename for the sbtab object.
614
+ """
615
+ self.filename = Path(filename)
616
+ # validate file extension
617
+ self._validate_extension()
618
+
619
+ def add_sbtab_string(self, sbtab_string, definitions_file=None):
620
+ """Set the content of the sbtab Table in form of a string."""
621
+ self.table_string = sbtab_string
622
+ # validate singular sbtab
623
+ self._singular()
624
+
625
+ # process string
626
+ self.delimiter = utils.check_delimiter(sbtab_string)
627
+ self.preprocess = self._preprocess_table_string(sbtab_string)
628
+ self.table = self._cut_table_string(self.preprocess)
629
+
630
+ # Initialise table
631
+ self._initialize_table()
632
+
633
+ def unset_attribute(self, attribute):
634
+ """
635
+ Remove an attribute from sbtab declaration row.
636
+
637
+ Parameters
638
+ ----------
639
+ attribute : str
640
+ Attribute that shall be removed from the sbtab object's declaration row.
641
+ """
642
+ obligatory_attributes = ["TableType", "TableID"]
643
+ if attribute in obligatory_attributes:
644
+ raise SBtabError(
645
+ "Attribute %s cannot be removed as it is obligatory." % attribute
646
+ )
647
+
648
+ if attribute in self.header_row:
649
+ try:
650
+ att = re.search("%s='([^']*)'" % attribute, self.header_row).group(0)
651
+ self.header_row = self.header_row.replace(" " + att, "")
652
+ except Exception:
653
+ raise SBtabError("Attribute %s cannot be removed." % attribute)
654
+ else:
655
+ raise SBtabError(
656
+ "Attribute %s is not in the header of the SBtabTable." % attribute
657
+ )
658
+
659
+ def get_attribute(self, attribute):
660
+ """
661
+ Return the value of an sbtab attribute.
662
+
663
+ Parameters
664
+ ----------
665
+ attribute : str
666
+ Attribute from the sbtab object's declaration row.
667
+
668
+ Returns: str
669
+ Value of the requested attribute.
670
+ """
671
+ try:
672
+ value = re.search("%s='([^']*)'" % attribute, self.header_row).group(1)
673
+ return value
674
+ except Exception:
675
+ raise SBtabError(
676
+ "The attribute %s was not found in the header row." % attribute
677
+ )
678
+
679
+ def change_value(self, row, column, new):
680
+ """
681
+ Change a single value in the sbtab table by position in the table.
682
+
683
+ Parameters
684
+ ----------
685
+ row : int
686
+ Number of rows in the table. First row is number 1.
687
+ column : int
688
+ Number of columns in the table. First column is number 1.
689
+ new : str
690
+ New entry.
691
+ """
692
+ try:
693
+ self.value_rows[row - 1]
694
+ except Exception:
695
+ raise SBtabError("The sbtab has only %s rows." % len(self.value_rows))
696
+ try:
697
+ self.columns[column - 1]
698
+ except Exception:
699
+ raise SBtabError("The sbtab has only %s columns." % len(self.columns))
700
+
701
+ try:
702
+ self.value_rows[row - 1][column - 1] = str(new)
703
+ except Exception:
704
+ raise SBtabError("Could not set the given value.")
705
+
706
+ return True
707
+
708
+ def change_value_by_name(self, name, column_name, new):
709
+ """
710
+ Change single value in the sbtab by column name and first row entry name.
711
+
712
+ Parameters
713
+ ----------
714
+ row : str
715
+ Name of the entry in the ID column.
716
+ column : str
717
+ Name of the column (with '!').
718
+ new : str
719
+ New entry.
720
+ """
721
+ try:
722
+ self.columns_dict[column_name]
723
+ except Exception:
724
+ raise SBtabError("The column %s is not in the sbtab." % column_name)
725
+
726
+ success = False
727
+ col = self.columns_dict[column_name]
728
+ for r in self.value_rows:
729
+ if r[0] == name:
730
+ r[col] = str(new)
731
+ success = True
732
+
733
+ if not success:
734
+ raise SBtabError("Row %s was not found in the sbtab." % name)
735
+
736
+ return True
737
+
738
+ def create_list(self):
739
+ """
740
+ Create a list object of the sbtab table object.
741
+
742
+ Returns: list
743
+ List containing of sbtab content: [declaration row, columns, value_rows]
744
+ """
745
+ # Create new list
746
+ sbtab_list = []
747
+
748
+ # Append the parts header row, main column row and
749
+ # value rows to the list
750
+ sbtab_list.append([self.header_row])
751
+ sbtab_list.append(self.columns)
752
+ sbtab_list.append(self.value_rows)
753
+
754
+ return sbtab_list
755
+
756
+ def add_row(self, row_list, position=None):
757
+ """
758
+ Add a row to the table, if position is None at the end of it.
759
+
760
+ Parameters
761
+ ----------
762
+ row_list : list
763
+ List of strings, containing the entries of the new row.
764
+ position : int
765
+ Position of new row in the table, 0 is on top.
766
+ """
767
+ if not isinstance(row_list, list):
768
+ raise SBtabError("%s is not a list" % row_list)
769
+
770
+ if len(row_list) != len(self.columns):
771
+ raise SBtabError("Given row %s has not the correct length." % row_list)
772
+
773
+ if position is not None and not isinstance(position, int):
774
+ raise SBtabError("Please provide an integer row position.")
775
+
776
+ for element in row_list:
777
+ if not isinstance(element, str):
778
+ raise SBtabError("Please only provide string elements in the list")
779
+
780
+ # If no position is set, add new row to the end
781
+ if position is None:
782
+ self.value_rows.append(row_list)
783
+ else:
784
+ self.value_rows.insert(position, row_list)
785
+
786
+ return True
787
+
788
+ def remove_row(self, position):
789
+ """
790
+ Remove one row from the table.
791
+
792
+ Parameters
793
+ ----------
794
+ position : int
795
+ Position of row to be removed. Starting with 1.
796
+ """
797
+ if not isinstance(position, int):
798
+ raise SBtabError("Please provide an integer row position.")
799
+
800
+ if position > len(self.value_rows):
801
+ raise SBtabError("The sbtab only has %s row/s." % len(self.value_rows))
802
+
803
+ del self.value_rows[position - 1]
804
+
805
+ return True
806
+
807
+ def add_column(self, column_list, position=None):
808
+ """
809
+ Add a column to the table, if position is None at the end of it.
810
+
811
+ Parameters
812
+ ----------
813
+ column_list : list
814
+ List of strings, containing the entries of the new column.
815
+ position : int
816
+ Position of new column in the table, 0 is right.
817
+ """
818
+ if not isinstance(column_list, list):
819
+ raise SBtabError("%s is not a list" % column_list)
820
+
821
+ if len(column_list) != (len(self.value_rows) + 1):
822
+ raise SBtabError("The given column list has not the correct length.")
823
+
824
+ if position is not None and not isinstance(position, int):
825
+ raise SBtabError("Please provide an integer column position.")
826
+
827
+ # If no position is set, add new column to the end
828
+ if not position:
829
+ for i, row in enumerate(self.value_rows):
830
+ row.append(str(column_list[i + 1]))
831
+ self.columns_dict[str(column_list[0])] = len(self.columns)
832
+ self.columns.append(str(column_list[0]))
833
+ else:
834
+ for i, row in enumerate(self.value_rows):
835
+ row.insert(position - 1, str(column_list[i + 1]))
836
+ self.columns_dict[str(column_list[0])] = position - 1
837
+ self.columns.insert(position - 1, str(column_list[0]))
838
+
839
+ return True
840
+
841
+ def remove_column(self, position):
842
+ """
843
+ Remove column from the table.
844
+
845
+ Parameters
846
+ ----------
847
+ position : int
848
+ Position of column to be removed. Sarting with 1.
849
+ """
850
+ if not isinstance(position, int):
851
+ raise SBtabError("Please provide an integer column position.")
852
+
853
+ if position > len(self.columns):
854
+ raise SBtabError(
855
+ "There are only %s columns in the sbtab" % str(len(self.columns))
856
+ )
857
+
858
+ # Remove entries on position
859
+ for row in self.value_rows:
860
+ del row[position - 1]
861
+
862
+ # Remove column from column list
863
+ column_to_remove = self.columns[position - 1]
864
+ del self.columns[position - 1]
865
+
866
+ # Remove column from columns dict
867
+ self.columns_dict.pop(column_to_remove)
868
+
869
+ return True
870
+
871
+ def write(self, filename):
872
+ """
873
+ Write sbtab to hard disk.
874
+
875
+ Parameters
876
+ ----------
877
+ filename: str or Path
878
+ Name for the output file.
879
+ """
880
+ path = Path(filename)
881
+ if path.suffix not in {".tsv", ".csv"}:
882
+ if self.delimiter == "\t":
883
+ path = path.with_suffix(".tsv")
884
+ elif self.delimiter in {",", ";"}:
885
+ path = path.with_suffix(".csv")
886
+ else:
887
+ raise SBtabError(
888
+ "The file extension is missing and the delimiter is no standard."
889
+ )
890
+
891
+ try:
892
+ table_string = self.to_str().replace("^M", "\n")
893
+ path.write_text(table_string)
894
+ return True
895
+ except Exception:
896
+ raise SBtabError("The file could not be written.")
897
+
898
+ def transpose_table(self):
899
+ """
900
+ Transpose sbtab table. Switch columns and rows.
901
+
902
+ Returns: sbtab.SBtabTable
903
+ sbtab object with transposed content.
904
+ """
905
+ # Initialize new table data
906
+ trans_columns = []
907
+ trans_columns_dict = {}
908
+ trans_value_rows = []
909
+
910
+ # Save old table data
911
+ columns = self.columns
912
+ value_rows = self.value_rows
913
+
914
+ # Append first entry to new column
915
+ trans_columns.append(columns.pop(0))
916
+
917
+ # Set new rows
918
+ for column in columns:
919
+ trans_value_rows.append([column])
920
+
921
+ # Set new values in tables
922
+ for row in value_rows:
923
+ trans_columns.append(row.pop(0))
924
+ for i, entry in enumerate(row):
925
+ trans_value_rows[i].append(entry)
926
+
927
+ # Write new columns dict
928
+ for i, column in enumerate(trans_columns):
929
+ trans_columns_dict[column] = i
930
+
931
+ # Overwrite old table data
932
+ self.columns = trans_columns
933
+ self.columns_dict = trans_columns_dict
934
+ self.value_rows = trans_value_rows
935
+
936
+ return True
937
+
938
+ def to_data_frame(self):
939
+ """
940
+ Export sbtab table object as pandas dataframe.
941
+
942
+ Returns: pandas.DataFrame
943
+ sbtab table object as pandas dataframe.
944
+ """
945
+ try:
946
+ import pandas as pd
947
+
948
+ rows = self._get_rows()
949
+ n_cols = max(map(len, rows))
950
+ column_names = list(map(lambda s: s[1:], self.columns))
951
+ while len(column_names) < n_cols:
952
+ column_names += ["Col%d" % len(column_names)]
953
+ df = pd.DataFrame(data=self._get_rows(), columns=column_names)
954
+ return df
955
+ except Exception:
956
+ raise SBtabError("Pandas dataframe could not be built.")
957
+
958
+ @staticmethod
959
+ def from_data_frame(
960
+ df,
961
+ table_id,
962
+ table_type,
963
+ table_name=None,
964
+ document_name=None,
965
+ document=None,
966
+ unit=None,
967
+ sbtab_version="1.0",
968
+ ) -> "SBtabTable":
969
+ """
970
+ Create sbtab table object from pandas dataframe.
971
+
972
+ Parameters
973
+ ----------
974
+ df : pandas.DataFrame
975
+ Dataframe of the Python library pandas.
976
+ table_id: str
977
+ Mandatory table ID for the sbtab object.
978
+ table_type: str
979
+ Mandatory table type for the sbtab object.
980
+ table_name: str
981
+ Optional table name for the sbtab object.
982
+ document_name: str
983
+ Optional document name for the sbtab object.
984
+ document: str
985
+ Optional document for the sbtab object.
986
+ unit: str
987
+ Optional unit for an sbtab TableType Quantity.
988
+ sbtab_version: str
989
+ Optional sbtab Version.
990
+
991
+ Returns: sbtab.SBtabTable
992
+ sbtab table object created from pandas dataframe.
993
+ """
994
+ sbtab = SBtabTable()
995
+
996
+ sbtab.table_id = table_id
997
+ sbtab.table_type = table_type
998
+ sbtab.table_name = table_name or table_id
999
+ sbtab.table_document = document
1000
+ sbtab.table_version = sbtab_version
1001
+
1002
+ header = [
1003
+ ("TableID", sbtab.table_id),
1004
+ ("TableType", sbtab.table_type),
1005
+ ("TableName", sbtab.table_name),
1006
+ ("SBtabVersion", sbtab.table_version),
1007
+ ]
1008
+ if document_name:
1009
+ header += [("DocumentName", document_name)]
1010
+ if document:
1011
+ header += [("Document", sbtab.table_document)]
1012
+ if unit:
1013
+ header += [("Unit", unit)]
1014
+ header_strings = ["!!SBtab"] + list(map(lambda x: "%s='%s'" % x, header))
1015
+
1016
+ sbtab.doc_row = None
1017
+ sbtab.header_row = " ".join(header_strings)
1018
+
1019
+ sbtab.columns = df.columns.tolist()
1020
+ sbtab.columns_dict = dict(map(reversed, enumerate(df.columns)))
1021
+ sbtab.value_rows = df.values.tolist()
1022
+
1023
+ return sbtab
1024
+
1025
+
1026
+ class SBtabDocument:
1027
+ """The sbtab document class can consist of one or more sbtab Table objects."""
1028
+
1029
+ def __init__(
1030
+ self, name=None, sbtab_init=None, filename=None, definitions_file=None
1031
+ ):
1032
+ """
1033
+ Create SBtabDocument with an optional sbtab table object.
1034
+
1035
+ Parameters
1036
+ ----------
1037
+ name: str
1038
+ Name for the sbtab document.
1039
+ sbtab_init: str | sbtab.SBtabTable
1040
+ Initial sbtab table as either string or sbtab table object
1041
+ filename: str
1042
+ If sbtab_init is string, also provide a fiename.
1043
+ """
1044
+ if filename:
1045
+ self.filename = filename
1046
+ else:
1047
+ self.filename = None
1048
+ self.sbtabs = []
1049
+ self.id_to_sbtab = {}
1050
+ self.name_to_sbtab = {}
1051
+ self.type_to_sbtab = {}
1052
+ self.sbtab_filenames = []
1053
+ self.doc_row = False
1054
+ self.document_format = None
1055
+
1056
+ if name:
1057
+ self.name = name
1058
+ self._get_doc_row_attributes()
1059
+ else:
1060
+ self.name = None
1061
+
1062
+ # if there is an initial sbtab given, see if it is
1063
+ # a string or an sbtab
1064
+ if sbtab_init and isinstance(sbtab_init, str):
1065
+ self.add_sbtab_string(sbtab_init, filename, definitions_file)
1066
+ elif sbtab_init:
1067
+ self.add_sbtab(sbtab_init, definitions_file)
1068
+
1069
+ self.object_type = "doc"
1070
+
1071
+ def add_sbtab(self, sbtab, definitions_file=None):
1072
+ """
1073
+ Add an sbtab Table object to the sbtab Document.
1074
+
1075
+ Parameters
1076
+ ----------
1077
+ sbtab: sbtab.SBtabTable
1078
+ sbtab table object to be added to the document.
1079
+ """
1080
+ if not self.filename:
1081
+ self.filename = sbtab.filename
1082
+
1083
+ if sbtab.table_id in self.id_to_sbtab.keys():
1084
+ raise SBtabError(
1085
+ "A table with the ID %s is already in the document. Table IDs need"
1086
+ " to be unique within one document." % sbtab.table_id
1087
+ )
1088
+
1089
+ valid_type = self.check_type_validity(sbtab.table_type, definitions_file)
1090
+ if valid_type:
1091
+ self.name_to_sbtab[sbtab.table_name] = sbtab
1092
+ self.id_to_sbtab[sbtab.table_id] = sbtab
1093
+ self.sbtabs.append(sbtab)
1094
+ self.sbtab_filenames.append(sbtab.filename)
1095
+ if sbtab.table_type in self.type_to_sbtab:
1096
+ tabs = self.type_to_sbtab[sbtab.table_type]
1097
+ tabs.append(sbtab)
1098
+ self.type_to_sbtab[sbtab.table_type] = tabs
1099
+ else:
1100
+ self.type_to_sbtab[sbtab.table_type] = [sbtab]
1101
+
1102
+ self._get_doc_row_attributes()
1103
+ return True
1104
+
1105
+ def add_sbtab_string(self, sbtab_string, filename, definitions_file=None):
1106
+ """
1107
+ Add one or multiple sbtab files as a string.
1108
+
1109
+ Parameters
1110
+ ----------
1111
+ sbtab_string: str
1112
+ One string holding one or more sbtab tables.
1113
+ filename: str
1114
+ Name of the given sbtab.
1115
+ """
1116
+ # set filename if not given
1117
+ if not filename:
1118
+ sbtab_count = len(self.sbtabs)
1119
+ filename = "unnamed_sbtab_%s.tsv" % (str(sbtab_count))
1120
+
1121
+ if not self.filename:
1122
+ self.filename = filename
1123
+ if not self.name:
1124
+ self.name = filename
1125
+
1126
+ # see if there are more than one SBtabs in the string
1127
+ try:
1128
+ sbtab_amount = utils.count_tabs(sbtab_string)
1129
+ except Exception:
1130
+ raise SBtabError("The sbtab file could not be read properly.")
1131
+
1132
+ # here, we find a possible doc row
1133
+ for row in sbtab_string.split("\n"):
1134
+ if row.startswith("!!!"):
1135
+ self.doc_row = self._dequote(row)
1136
+ break
1137
+ elif row.startswith('"!!!'):
1138
+ rm1 = row.replace('""', "#")
1139
+ rm2 = rm1.replace('"', "")
1140
+ self.doc_row = self._dequote(rm2.replace("#", '"'))
1141
+ break
1142
+ else:
1143
+ self.doc_row = None
1144
+ self.document_format = None
1145
+
1146
+ # determine if this is an sbtab or ObjTables Document
1147
+ if self.doc_row:
1148
+ if "!!!ObjTables" in self.doc_row:
1149
+ self.document_format = "ObjTables"
1150
+ elif "!!!SBtab" in self.doc_row:
1151
+ self.document_format = "sbtab"
1152
+
1153
+ # if there are more than one SBtabs, cut them in single SBtabs
1154
+ try:
1155
+ if sbtab_amount > 1:
1156
+ sbtab_strings = utils.split_sbtabs(sbtab_string)
1157
+ for i, sbtab_s in enumerate(sbtab_strings):
1158
+ name_single = str(i) + "_" + str(self.filename)
1159
+ sbtab_single = SBtabTable(sbtab_s, name_single)
1160
+ logging.debug(
1161
+ "name = %s, type = %s"
1162
+ % (sbtab_single.table_name, sbtab_single.table_type)
1163
+ )
1164
+ self.add_sbtab(sbtab_single, definitions_file)
1165
+ else:
1166
+ sbtab = SBtabTable(sbtab_string, filename)
1167
+ self.add_sbtab(sbtab)
1168
+ except Exception as e:
1169
+ raise SBtabError(
1170
+ "The sbtab Table object could not be created properly: " + str(e)
1171
+ )
1172
+ return True
1173
+
1174
+ def _dequote(self, row):
1175
+ """Bring consistency in the multifarious quotation mark problems."""
1176
+ stupid_quotes = [
1177
+ '"',
1178
+ "\xe2\x80\x9d",
1179
+ "\xe2\x80\x98",
1180
+ "\xe2\x80\x99",
1181
+ "\xe2\x80\x9b",
1182
+ "\xe2\x80\x9c",
1183
+ "\xe2\x80\x9f",
1184
+ "\xe2\x80\xb2",
1185
+ "\xe2\x80\xb3",
1186
+ "\xe2\x80\xb4",
1187
+ "\xe2\x80\xb5",
1188
+ "\xe2\x80\xb6",
1189
+ "\xe2\x80\xb7",
1190
+ ]
1191
+
1192
+ for squote in stupid_quotes:
1193
+ try:
1194
+ row = row.replace(squote, "'")
1195
+ except Exception:
1196
+ pass
1197
+
1198
+ row = row.replace("\n", "")
1199
+
1200
+ return row
1201
+
1202
+ def check_type_validity(self, ttype, definitions_file=None):
1203
+ """
1204
+ Check if the given table type is supported by default.
1205
+
1206
+ Parameters
1207
+ ----------
1208
+ ttype: str
1209
+ Table type to be tested.
1210
+
1211
+ Returns: Bool
1212
+ Flag that indicates if the given table type is supported by default.
1213
+
1214
+ """
1215
+ try:
1216
+ supported_types = utils.extract_supported_table_types(definitions_file)
1217
+ except Exception:
1218
+ raise SBtabError(
1219
+ "The definition file could not be found to"
1220
+ " establish supported table types."
1221
+ )
1222
+
1223
+ if ttype in supported_types:
1224
+ return True
1225
+ else:
1226
+ raise SBtabError(
1227
+ "The table type %s is not supported. Make sure to provide a"
1228
+ " definition file that contains this table type." % ttype
1229
+ )
1230
+
1231
+ def _get_doc_row_attributes(self):
1232
+ """Read content of the !!!-document declaration row."""
1233
+ now = datetime.datetime.now()
1234
+ self.date = "-".join([str(now.year), str(now.month), str(now.day)])
1235
+
1236
+ if not self.doc_row:
1237
+ self.doc_row = "!!!SBtab DocumentName='%s' SBtabVersion='1.0' Date='%s'" % (
1238
+ self.name,
1239
+ self.date,
1240
+ )
1241
+ else:
1242
+ # save document name, otherwise raise error
1243
+ # (overrides name given at document initialisation)
1244
+ try:
1245
+ self.name = self.get_custom_doc_information("DocumentName")
1246
+ except Exception:
1247
+ pass
1248
+
1249
+ # save SBtabVersion
1250
+ try:
1251
+ self.version = self.get_custom_doc_information("SBtabVersion")
1252
+ except Exception:
1253
+ self.version = None
1254
+
1255
+ # save date
1256
+ try:
1257
+ self.date = self.get_custom_doc_information("Date")
1258
+ except Exception:
1259
+ if "Date=" not in self.doc_row:
1260
+ self.doc_row = self.doc_row + " Date='%s'" % self.date
1261
+
1262
+ # save document type
1263
+ try:
1264
+ self.doc_type = self.get_custom_doc_information("DocumentType")
1265
+ except Exception:
1266
+ self.doc_type = None
1267
+
1268
+ def change_attribute(self, attribute, value):
1269
+ """
1270
+ Change the value of an sbtab attribute.
1271
+
1272
+ Parameters
1273
+ ----------
1274
+ attribute: str
1275
+ Attribute from the sbtab document object's declaration row.
1276
+ value: str
1277
+ Value for the attribute.
1278
+ """
1279
+ try:
1280
+ att_value_new = "%s='%s'" % (attribute, value)
1281
+ except Exception:
1282
+ raise SBtabError("Please provide only strings as attribute and value.")
1283
+
1284
+ if attribute not in self.doc_row:
1285
+ self.doc_row = self.doc_row + " " + att_value_new
1286
+ else:
1287
+ try:
1288
+ att_value = re.search("%s='([^']*)'" % attribute, self.doc_row).group(0)
1289
+ self.doc_row = self.doc_row.replace(att_value, att_value_new)
1290
+ except Exception:
1291
+ raise SBtabError(
1292
+ "Attribute value %s could not be replaced in the doc row."
1293
+ % attribute
1294
+ )
1295
+
1296
+ def unset_attribute(self, attribute):
1297
+ """
1298
+ Remove attribute from sbtab document object's declaration row.
1299
+
1300
+ Parameters
1301
+ ----------
1302
+ attribute: str
1303
+ Attribute that shall be removed.
1304
+ """
1305
+ obligatory_attributes = ["DocumentName"]
1306
+ if attribute in obligatory_attributes:
1307
+ raise SBtabError(
1308
+ "Attribute %s cannot be removed as it is obligatory." % attribute
1309
+ )
1310
+
1311
+ if attribute in self.doc_row:
1312
+ try:
1313
+ att = re.search("%s='([^']*)'" % attribute, self.doc_row).group(0)
1314
+ self.doc_row = self.doc_row.replace(" " + att, "")
1315
+ except Exception:
1316
+ raise SBtabError("Attribute %s cannot be removed." % attribute)
1317
+ else:
1318
+ raise SBtabError(
1319
+ "Attribute %s is not in the doc row of the SBtabDocument." % attribute
1320
+ )
1321
+
1322
+ def get_attribute(self, attribute):
1323
+ """
1324
+ Return the value of an sbtab attribute.
1325
+
1326
+ Parameters
1327
+ ----------
1328
+ attribute: str
1329
+ Attribute from the sbtab document object's declaration row.
1330
+
1331
+ Returns: str
1332
+ Value of the requested attribute.
1333
+ """
1334
+ try:
1335
+ value = re.search("%s='([^']*)'" % attribute, self.doc_row).group(1)
1336
+ return value
1337
+ except Exception:
1338
+ raise SBtabError(
1339
+ "The attribute %s was not found in the doc row." % attribute
1340
+ )
1341
+
1342
+ def set_version(self, version):
1343
+ """
1344
+ Set SBtabVersion of the document.
1345
+
1346
+ Parameters
1347
+ ----------
1348
+ version: str
1349
+ Version number of sbtab document.
1350
+ """
1351
+ try:
1352
+ self.version = version
1353
+ except Exception:
1354
+ raise SBtabError("Version could not be set to %s" % version)
1355
+
1356
+ def set_date(self, date):
1357
+ """
1358
+ Set date of the document.
1359
+
1360
+ Parameters
1361
+ ----------
1362
+ date: str
1363
+ Date of the sbtab document.
1364
+ """
1365
+ try:
1366
+ self.date = date
1367
+ except Exception:
1368
+ raise SBtabError("Date could not be set to %s" % date)
1369
+
1370
+ def set_doc_type(self, doc_type):
1371
+ """
1372
+ Set the type of the document.
1373
+
1374
+ Parameters
1375
+ ----------
1376
+ doc_type: str
1377
+ Type of the sbtab document.
1378
+ """
1379
+ try:
1380
+ self.doc_type = doc_type
1381
+ except Exception:
1382
+ raise SBtabError("Doc type could not be set to %s" % doc_type)
1383
+
1384
+ def remove_sbtab_by_name(self, name):
1385
+ """
1386
+ Remove sbtab Table from sbtab Document.
1387
+
1388
+ Parameters
1389
+ ----------
1390
+ name: str
1391
+ Name of the sbtab table to be removed.
1392
+ """
1393
+ for i, sbtab in enumerate(self.sbtabs):
1394
+ if sbtab.table_name == name:
1395
+ del self.sbtabs[i]
1396
+ del self.name_to_sbtab[sbtab.table_name]
1397
+ del self.id_to_sbtab[sbtab.table_id]
1398
+ del self.sbtab_filenames[i]
1399
+ tabs = self.type_to_sbtab[sbtab.table_type]
1400
+ for tab in tabs:
1401
+ if tab.table_name == sbtab.table_name:
1402
+ tabs.remove(tab)
1403
+ break
1404
+
1405
+ self.type_to_sbtab[sbtab.table_type] = tabs
1406
+ break
1407
+
1408
+ return True
1409
+
1410
+ def get_sbtab_by_name(self, name):
1411
+ """
1412
+ Return sbtab table object by given name.
1413
+
1414
+ Parameters
1415
+ ----------
1416
+ name: str
1417
+ Name of sbtab table to be returned.
1418
+
1419
+ Returns: sbtab.SBtabTable
1420
+ sbtab table object of requested name.
1421
+ """
1422
+ try:
1423
+ return self.name_to_sbtab[name]
1424
+ except Exception:
1425
+ return None
1426
+
1427
+ def get_sbtab_by_id(self, name):
1428
+ """
1429
+ Return sbtab table object by given ID.
1430
+
1431
+ Parameters
1432
+ ----------
1433
+ name: str
1434
+ TableID of a sbtab table object.
1435
+
1436
+ Returns: sbtab.SBtabTable
1437
+ sbtab table object of requested ID.
1438
+ """
1439
+ try:
1440
+ return self.id_to_sbtab[name]
1441
+ except Exception:
1442
+ return None
1443
+
1444
+ def get_sbtab_by_type(self, ttype):
1445
+ """
1446
+ Return list of sbtab objects by given table type.
1447
+
1448
+ Parameters
1449
+ ----------
1450
+ ttype: str
1451
+ Supported sbtab table type.
1452
+
1453
+ Returns: list
1454
+ List of sbtab.SBtabTable objects of the requested table type.
1455
+ """
1456
+ try:
1457
+ return self.type_to_sbtab[ttype]
1458
+ except Exception:
1459
+ return None
1460
+
1461
+ def set_filename(self, filename):
1462
+ """
1463
+ Set filename of sbtab document.
1464
+
1465
+ Parameters
1466
+ ----------
1467
+ filename: str or Path
1468
+ Filename for the sbtab document.
1469
+ """
1470
+ self.filename = Path(filename)
1471
+
1472
+ def set_name(self, name):
1473
+ """
1474
+ Set name of sbtab document.
1475
+
1476
+ Parameters
1477
+ ----------
1478
+ name: str
1479
+ Name for the sbtab document.
1480
+ """
1481
+ self.name = name
1482
+
1483
+ def write(self, filename=None):
1484
+ """
1485
+ Write SBtabDocument to hard disk.
1486
+
1487
+ Parameters
1488
+ ----------
1489
+ filename: str or Path
1490
+ Name for the output file.
1491
+ """
1492
+ path = Path(filename if filename is not None else self.filename)
1493
+ if path.suffix not in {".tsv", ".csv"}:
1494
+ delimiter = self.sbtabs[0].delimiter
1495
+ if delimiter == "\t":
1496
+ path = path.with_suffix(".tsv")
1497
+ elif delimiter in {",", ";"}:
1498
+ path = path.with_suffix(".csv")
1499
+ else:
1500
+ raise SBtabError(
1501
+ "The file extension is missing and the delimiter is not set."
1502
+ )
1503
+
1504
+ try:
1505
+ path.write_text(self.to_str())
1506
+ return True
1507
+ except Exception:
1508
+ raise SBtabError("The file could not be written.")
1509
+
1510
+ def to_str(self):
1511
+ """
1512
+ Return sbtab Document as one string.
1513
+
1514
+ Returns: str
1515
+ sbtab.SBtabDocument as string representation.
1516
+ """
1517
+ sbtab_document = self.doc_row + "\r\n"
1518
+ for sbtab in self.sbtabs:
1519
+ sbtab_document += sbtab.to_str() + "\n\n"
1520
+
1521
+ return sbtab_document
1522
+
1523
+ def get_custom_doc_information(self, attribute_name, test_row=None):
1524
+ """
1525
+ Retrieve the value of a doc attribute in the sbtab document declaration row.
1526
+
1527
+ Parameters
1528
+ ----------
1529
+ attribute_name: str
1530
+ Name of the requested attribute.
1531
+
1532
+ Returns: str
1533
+ Value of the requested attribute.
1534
+ """
1535
+ if test_row:
1536
+ doc_row = test_row
1537
+ else:
1538
+ doc_row = self.doc_row
1539
+
1540
+ if re.search("%s='([^']*)'" % attribute_name, doc_row) is not None:
1541
+ return re.search("%s='([^']*)'" % attribute_name, doc_row).group(1)
1542
+ else:
1543
+ raise SBtabError(
1544
+ """The %s of the Document is
1545
+ not defined!"""
1546
+ % attribute_name
1547
+ )
1548
+
1549
+ def set_doc_row(self, new_doc_row):
1550
+ """
1551
+ Set a new sbtab document declaration row.
1552
+
1553
+ Parameters
1554
+ ----------
1555
+ new_doc_row: str
1556
+ New sbtab document declaration row.
1557
+ """
1558
+ if not new_doc_row.startswith("!!!SBtab") and not new_doc_row.startswith(
1559
+ "!!!ObjTables"
1560
+ ):
1561
+ raise SBtabError('A doc row needs to be preceded with "!!!SBtab".')
1562
+
1563
+ if "DocumentName=" not in new_doc_row:
1564
+ raise SBtabError("A doc row needs to define the DocumentName attribute.")
1565
+
1566
+ self.doc_row = new_doc_row
1567
+ self._get_doc_row_attributes()