1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 """The BMRB library base classes.
24
25 This file is part of the U{BMRB library<https://sourceforge.net/projects/bmrblib>}.
26 """
27
28
29 from numpy import float64, ndarray, zeros
30 from warnings import warn
31
32
33 from bmrblib.misc import no_missing, translate
34 from bmrblib.pystarlib.SaveFrame import SaveFrame
35 from bmrblib.pystarlib.TagTable import TagTable
36 from bmrblib.version import Star_version; version = Star_version()
37
38
40 """The base class for the saveframe classes."""
41
43 """Initialise the class, placing the pystarlib data nodes into the namespace.
44
45 @param datanodes: The pystarlib data nodes object.
46 @type datanodes: list
47 """
48
49
50 self.datanodes = datanodes
51
52
53 self.count = 0
54
55
56 self.tag_categories = CategoryList()
57 self.add_tag_categories()
58
59
60 - def add(self, **keywords):
61 """Add data to the saveframe.
62
63 If the keywords are within the tag dictionary structure as the variable name, then the data will be checked, translated and stored in that variable. If not, then a warning will be given.
64
65 @return: The saveframe count.
66 @rtype: int
67 """
68
69
70 self.reset()
71
72
73 for cat in self.tag_categories:
74
75 for key in cat._key_list:
76
77 if not cat[key].var_name or not cat[key].default:
78 continue
79
80
81 setattr(self, cat[key].var_name, translate(cat[key].default))
82
83
84 for name, val in keywords.items():
85
86 info = self.tag_categories.get_tag(name)
87
88
89 if not info:
90 setattr(self, name, val)
91 continue
92
93
94 cat_index, key, obj = info
95
96
97 if not obj.missing:
98 no_missing(val, name)
99
100
101 if obj.allowed != None:
102
103 if not (isinstance(val, list) and not isinstance(val, ndarray)):
104 val_list = [val]
105 else:
106 val_list = val
107
108
109 for i in range(len(val_list)):
110 if val_list[i] not in obj.allowed:
111 raise NameError("The %s keyword argument of '%s' must be one of %s." % (name, val_list[i], obj.allowed))
112
113
114 if (isinstance(val, list) or isinstance(val, ndarray)):
115
116 N = self.tag_categories[cat_index]._N()
117
118
119 if N == None:
120 pass
121
122
123 if N != None and len(val) != N:
124 raise NameError("The number of elements in the %s keyword argument should be N = %s." % (name, N))
125
126
127 setattr(self, name, translate(val))
128
129
130 self.count = self.count + 1
131 self.count_str = str(self.count)
132
133
134 for i in range(len(self.tag_categories)):
135 ids = self.tag_categories[i].generate_data_ids()
136 if ids:
137 self.data_ids = translate(ids)
138
139
140 self.pre_ops()
141
142
143 self.frame = SaveFrame(title=self.create_title())
144
145
146 for i in range(len(self.tag_categories)):
147 self.tag_categories[i].create()
148
149
150 self.datanodes.append(self.frame)
151
152
153 return self.count
154
155
157 """Create the saveframe title.
158
159 @return: The title.
160 @rtype: str
161 """
162
163
164 if hasattr(self, 'label'):
165 return self.label + '_' + self.count_str
166 else:
167 return self.sf_label + '_' + self.count_str
168
169
171 """Read all the tags from the datanodes.
172
173 @keyword datanode: The datanode.
174 @type datanode: Datanode instance
175 @return: The data.
176 @rtype: tuple
177 """
178
179
180 mapping = self.find_mapping(datanode)
181
182
183 for i in range(len(mapping)):
184
185 if mapping[i] == None:
186 continue
187
188
189 self.tag_categories[mapping[i]].extract_tag_data(datanode.tagtables[i])
190
191
192 if version.major == 2:
193 self.sf_framecode = datanode.title
194
195
197 """Determine the mapping between the tag categories of the NMR-STAR file and the bmrblib class.
198
199 @keyword datanode: The datanode.
200 @type datanode: Datanode instance
201 @return: The mapping structure.
202 @rtype: list
203 """
204
205
206 N = len(self.tag_categories)
207 M = len(datanode.tagtables)
208 counts = zeros((M, N), float64)
209 mapping = []
210
211
212 for table_ind in range(M):
213 for cat_ind in range(N):
214
215 cat = self.tag_categories[cat_ind]
216 table = datanode.tagtables[table_ind]
217
218
219 for key in cat.keys():
220 for name in table.tagnames:
221
222 if name == cat[key].tag_name_full():
223 counts[table_ind, cat_ind] += 1
224
225
226 if not counts[table_ind].sum():
227 index = None
228 else:
229 index = counts[table_ind].tolist().index(counts[table_ind].max())
230 mapping.append(index)
231
232
233 return mapping
234
235
237 """Loop over the saveframes, yielding the data.
238
239 @return: The saveframe data.
240 @rtype: tuple
241 """
242
243
244 for i in range(len(self.tag_categories)):
245 self.tag_categories[i].tag_setup()
246
247
248 sf_name = getattr(self, 'sf_label')
249
250
251 for datanode in self.datanodes:
252
253 found = False
254 for index in range(len(datanode.tagtables[0].tagnames)):
255
256 if datanode.tagtables[0].tagnames[index] == self.tag_categories[0]['SfCategory'].tag_name_full():
257
258 if datanode.tagtables[0].tagvalues[index][0] == sf_name:
259 found = True
260 break
261
262
263 if not found:
264 continue
265
266
267 self.extract_data(datanode)
268
269
270 yield self.read()
271
272
274 """A dummy method for performing no saveframe specific operations prior to XML creation."""
275
276
278 """Read all the data from the saveframe.
279
280 @return: A dictionary of all the data.
281 @rtype: dict
282 """
283
284
285 data = {}
286
287
288 for cat in self.tag_categories:
289
290 for key in cat._key_list:
291
292 if not cat[key].var_name:
293 continue
294
295
296 if not hasattr(self, cat[key].var_name):
297 obj = None
298
299
300 else:
301 obj = getattr(self, cat[key].var_name)
302
303
304 data[cat[key].var_name] = translate(obj, format=cat[key].format, reverse=True)
305
306
307 return data
308
309
311 """Reset all data structures to None."""
312
313
314 for cat in self.tag_categories:
315
316 for key in cat._key_list:
317
318 if not cat[key].var_name or not hasattr(self, cat[key].var_name):
319 continue
320
321
322 if cat[key].var_name in ['sf_label', 'count', 'count_str']:
323 continue
324
325
326 setattr(self, cat[key].var_name, translate(None))
327
328
330 """Special class for when BMRB saveframes are non-existent in certain NMR-STAR versions."""
331
333 """Initialise the special class.
334
335 @param name: The name of the missing Saveframe.
336 @type name: str
337 """
338
339
340 self.name = name
341
342
343 - def add(self, *args, **keywords):
344 """Special function for giving a warning."""
345
346
347 warn(Warning("The %s saveframe does not exist in this NMR-STAR version." % self.name))
348
349
351 """Special function for giving a warning."""
352
353
354 warn(Warning("The %s saveframe does not exist in this NMR-STAR version." % self.name))
355
356
357 yield None
358
359
360
361
363 """A special lits object for holding the different saveframe tag categories."""
364
366 """Return the tag object possessing the given variable name.
367
368 @param var_name: The variable name.
369 @type var_name: str
370 @return: The key and tag objects.
371 @rtype: str, TagObject instance
372 """
373
374
375 for i in range(len(self)):
376
377 for key, obj in self[i].items():
378 if var_name == obj.var_name:
379 return i, key, obj
380
381
382
384 """A special dictionary object for creating the tag translation tables."""
385
387 """Set up the table."""
388
389
390 super(TagTranslationTable, self).__init__()
391
392
393 self.N = None
394
395
396 self._key_list = []
397
398
399 - def add(self, key, var_name=None, tag_name=None, allowed=None, default=None, format='str', missing=True):
400 """Add an entry to the translation table.
401
402 @keyword key: The dictionary key. This is also the BMRB NMR-STAR database table name.
403 @type key: str
404 @keyword var_name: The saveframe variable name corresponding to the key.
405 @type var_name: None or str
406 @keyword tag_name: The BMRB NMR-STAR tag name corresponding to the key.
407 @type tag_name: None or str
408 @keyword allowed: A list of allowable values for the data.
409 @type allowed: None or list
410 @keyword default: The default value.
411 @type default: anything
412 @keyword format: The original python format of the data.
413 @type format: str
414 @keyword missing: A flag which if True will allow the data to be set to None.
415 @type missing: bool
416 """
417
418
419 if key in self._key_list:
420
421 self[key].allowed = allowed
422 self[key].missing = missing
423 self[key].tag_name = tag_name
424 self[key].var_name = var_name
425 self[key].default = default
426 self[key].format = format
427
428
429 self._key_list.remove(key)
430 self._key_list.append(key)
431
432
433 else:
434
435 self[key] = TagObject(self, var_name=var_name, tag_name=tag_name, allowed=allowed, default=default, format=format, missing=missing)
436
437
438 self._key_list.append(key)
439
440
441
443 """An object for filling the translation table."""
444
445 - def __init__(self, category, var_name=None, tag_name=None, allowed=None, default=None, format='str', missing=True):
446 """Setup the internal variables.
447
448 This stores the variable name, BMRB NMR-STAR tag name, a list of allowable values, the missing flag, and any other tag specific information corresponding to the key.
449
450 @param category: The parent tag category class object.
451 @type category: TagTranslationTable instance
452 @keyword var_name: The saveframe variable name corresponding to the key.
453 @type var_name: None or str
454 @keyword tag_name: The BMRB NMR-STAR tag name corresponding to the key.
455 @type tag_name: None or str
456 @keyword allowed: A list of allowable values for the data.
457 @type allowed: None or list
458 @keyword default: The default value.
459 @type default: anything
460 @keyword format: The original python format of the data.
461 @type format: str
462 @keyword missing: A flag which if True will allow the data to be set to None.
463 @type missing: bool
464 """
465
466
467 self.category = category
468
469
470 self.allowed = allowed
471 self.missing = missing
472 self.tag_name = tag_name
473 self.var_name = var_name
474 self.default = default
475 self.format = format
476
477
479 """Add the prefix to the tag name and return the full tag name.
480
481 @return: The full tag name with prefix.
482 @rtype: str
483 """
484
485
486 if not self.tag_name:
487 return None
488 else:
489 return self.category.tag_prefix + self.tag_name
490
491
492
494 """The base class for tag category classes."""
495
496
497 tag_category_label = None
498
499
500 free = False
501
503 """Initialise the tag category object, placing the saveframe into its namespace.
504
505 @param sf: The saveframe object.
506 @type sf: saveframe instance
507 """
508
509
510 super(TagCategory, self).__init__()
511
512
513 self.sf = sf
514
515
516 self.tag_category_label = None
517
518
520 """Determine the length of the variables.
521
522 @return: The length.
523 @rtype: int
524 """
525
526
527 N = None
528 for key in self.keys():
529
530 if self[key].var_name and hasattr(self.sf, self[key].var_name):
531
532 obj = getattr(self.sf, self[key].var_name)
533
534
535 if not isinstance(obj, list) and not isinstance(obj, ndarray):
536 continue
537
538
539 N = len(obj)
540 break
541
542
543 if N:
544 self.N = N
545
546
547 return N
548
549
551 """Create the tag category."""
552
553
554 self.tag_setup()
555 tag_names = []
556 tag_values = []
557
558
559 if self.is_empty():
560 return
561
562
563 for key in self._key_list:
564
565 if self[key].tag_name != None and self[key].var_name != None:
566
567 tag_names.append(self[key].tag_name_full())
568
569
570 if hasattr(self.sf, self[key].var_name):
571 val = getattr(self.sf, self[key].var_name)
572 else:
573 val = translate(None)
574
575
576 if not isinstance(val, list):
577 val = [val]
578
579
580 tag_values.append(val)
581
582
583 if not len(tag_names):
584 return
585
586
587 N = len(tag_values[0])
588 for i in range(len(tag_values)):
589 if len(tag_values[i]) > N:
590
591 if N != 1:
592 raise NameError("The tag values are not all the same length '%s'." % tag_values)
593
594
595 N = len(tag_values[i])
596
597
598 for i in range(len(tag_values)):
599 if len(tag_values[i]) == 1:
600 tag_values[i] = tag_values[i] * N
601
602
603 table = TagTable(free=self.free, tagnames=tag_names, tagvalues=tag_values)
604
605
606 self.sf.frame.tagtables.append(table)
607
608
610 """Extract all of the tag data from the tagtable, placing it into the designated variable names.
611
612 @param tagtable: The tagtable.
613 @type tagtable: Tagtable instance
614 """
615
616
617 for key in self._key_list:
618
619 if self[key].tag_name_full() not in tagtable.tagnames:
620 continue
621
622
623 if self[key].var_name == None:
624 continue
625
626
627 index = tagtable.tagnames.index(self[key].tag_name_full())
628
629
630 data = tagtable.tagvalues[index]
631
632
633 if self.free:
634 data = data[0]
635
636
637 setattr(self.sf, self[key].var_name, data)
638
639
641 """Generate the data ID structure.
642
643 @keyword N: The number of data points.
644 @type N: int
645 """
646
647
648 N = self._N()
649
650
651 if not N:
652 return
653
654
655 return list(range(1, N+1))
656
657
659 """Dummy method for check if the tag category is empty.
660
661 @return: The state of emptiness (False).
662 @rtype: bool
663 """
664
665
666 return False
667
668
669 - def tag_setup(self, tag_category_label=None, sep=None):
670 """Setup the tag names.
671
672 @keyword tag_category_label: The tag name prefix specific for the tag category.
673 @type tag_category_label: None or str
674 @keyword sep: The string separating the tag name prefix and suffix.
675 @type sep: str
676 """
677
678
679 if tag_category_label:
680 self.tag_category_label = tag_category_label
681 if sep:
682 self.sep = sep
683 else:
684 self.sep = '.'
685
686
687 self.tag_prefix = '_'
688 if self.tag_category_label:
689 self.tag_prefix = self.tag_prefix + self.tag_category_label + self.sep
690
691
692
694 """The free version of the TagCategory class."""
695
696
697 free = True
698
700 """Setup the TagCategoryFree tag category.
701
702 @param sf: The saveframe object.
703 @type sf: saveframe instance
704 """
705
706
707 super(TagCategoryFree, self).__init__(sf)
708
709
710 self.add(key='SfCategory', var_name='sf_label', tag_name='Saveframe_category')
711 self.add(key='SfFramecode', var_name='sf_framecode', tag_name=None)
712