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