Belle II Software  release-05-02-19
format.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 from string import Formatter
5 from pathlib import Path
6 import re
7 import json
8 from basf2 import B2ERROR
9 
10 
11 def string(some_string):
12  """
13  Used to escape user strings for LaTex.
14  @param some_string which is escaped
15  @return string escaped version of some_string
16  """
17  return some_string.replace('\\', r'\textbackslash').replace('_', r'\_').replace('^', r'\^{}')
18 
19 
20 def variable(variable_string):
21  """
22  Adds hyphenations after brackets, and for common variables.
23  @param variable_string variable name
24  @return string with hyphenation hints for latex
25  """
26  substitutes = {
27  '=': r'=\allowbreak ',
28  '_': r'\_\allowbreak ',
29  ':': r':\allowbreak ',
30  '(': r'(\allowbreak ',
31  'extraInfo': r'ex\-tra\-In\-fo',
32  'SignalProbability': r'Sig\-nal\-Prob\-a\-bil\-i\-ty',
33  'cosAngleBetweenMomentumAndVertexVector': r'cosAngle\-Between\-Momentum\-And\-Vertex\-Vector'}
34  for key, value in substitutes.items():
35  variable_string = variable_string.replace(key, value)
36  return variable_string
37 
38 
39 def decayDescriptor(decay_string):
40  """
41  Prettifies the given decay string by using latex-symbols instead of plain-text
42  @param decay_string string containing a decay descriptor
43  @return string latex version of the decay descriptor
44  """
45  decay_string = decay_string.replace(':generic', '')
46  decay_string = decay_string.replace(':semileptonic', '$_{SL}$')
47  decay_string = decay_string.replace(':FSP', '$_{FSP}$')
48  decay_string = decay_string.replace(':V0', '$_{V0}$')
49  # Note: these are applied from top to bottom, so if you have
50  # both B0 and anti-B0, put anti-B0 first.
51  substitutes = [
52  ('==>', '$\\to$'),
53  ('gamma', r'$\gamma$'),
54  ('p+', r'$p$'),
55  ('anti-p-', r'$\bar{p}$'),
56  ('pi+', r'$\pi^+$'),
57  ('pi-', r'$\pi^-$'),
58  ('pi0', r'$\pi^0$'),
59  ('K_S0', r'$K^0_S$'),
60  ('K_L0', r'$K^0_L$'),
61  ('mu+', r'$\mu^+$'),
62  ('mu-', r'$\mu^-$'),
63  ('tau+', r'$\tau^+$'),
64  ('tau-', r'$\tau^-$'),
65  ('nu', r'$\nu$'),
66  ('K+', r'$K^+$'),
67  ('K-', r'$K^-$'),
68  ('e+', r'$e^+$'),
69  ('e-', r'$e^-$'),
70  ('J/psi', r'$J/\psi$'),
71  ('anti-Lambda_c-', r'$\Lambda^{-}_{c}$'),
72  ('anti-Sigma+', r'$\overline{\Sigma}^{+}$'),
73  ('anti-Lambda0', r'$\overline{\Lambda}^{0}$'),
74  ('anti-D0*', r'$\overline{D^{0*}}$'),
75  ('anti-D*0', r'$\overline{D^{0*}}$'),
76  ('anti-D0', r'$\overline{D^0}$'),
77  ('anti-B0', r'$\overline{B^0}$'),
78  ('Sigma+', r'$\Sigma^{+}$'),
79  ('Lambda_c+', r'$\Lambda^{+}_{c}$'),
80  ('Lambda0', r'$\Lambda^{0}$'),
81  ('D+', r'$D^+$'),
82  ('D-', r'$D^-$'),
83  ('D0', r'$D^0$'),
84  ('D*+', r'$D^{+*}$'),
85  ('D*-', r'$D^{-*}$'),
86  ('D*0', r'$D^{0*}$'),
87  ('D_s+', r'$D^+_s$'),
88  ('D_s-', r'$D^-_s$'),
89  ('D_s*+', r'$D^{+*}_s$'),
90  ('D_s*-', r'$D^{-*}_s$'),
91  ('B+', r'$B^+$'),
92  ('B-', r'$B^-$'),
93  ('B0', r'$B^0$'),
94  ('B_s0', r'$B^0_s$'),
95  ('K*0', r'$K^{0*}$')]
96  tex_string = decay_string
97  for (key, value) in substitutes:
98  tex_string = tex_string.replace(key, value)
99  return '\\texorpdfstring{%s}{%s}' % (tex_string, string(decay_string))
100 
101 
102 def duration(seconds):
103  """
104  Converts a duration given in seconds into a nice latex-style duration string
105  @param seconds duration in seconds
106  @return string describing a duration in a natural format
107  """
108  minutes = int(seconds / 60)
109  hours = int(minutes / 60)
110  minutes %= 60
111  ms = int(seconds * 1000) % 1000
112  us = int(seconds * 1000 * 1000) % 1000
113  seconds = int(seconds % 60)
114  string = ''
115  if hours != 0:
116  string += "%dh" % (hours)
117  if minutes != 0:
118  string += "%dm" % (minutes)
119  if seconds != 0 and hours == 0:
120  string += "%ds" % (seconds)
121  if ms != 0 and hours == 0 and minutes == 0 and seconds == 0:
122  string += "%dms" % (ms)
123  if us != 0 and hours == 0 and minutes == 0 and seconds == 0 and ms == 0:
124  string += r"%d$\mu$s" % (us)
125 
126  if hours == 0 and minutes == 0 and seconds == 0 and ms == 0 and us == 0:
127  string += r'$<1\mu$s'
128  return string
129 
130 
131 class AttrDict:
132  """Simple wrapper class to allow accessing dictionary elements via
133  attribute access"""
134 
135  def __init__(self, content):
136  """Remember the dictionary"""
137 
138  self.__content = content
139 
140  def __getattr__(self, key):
141  """Return any dictionay element as attribute"""
142  return self.__content[key]
143 
144 
145 class DefaultValueFormatter(Formatter):
146  """Custom formatter class which allows to substitute a missing value with a
147  default value"""
148 
149  def format_field(self, value, spec):
150  """
151  Format a single field:
152 
153  * if the field is None and we have a ``:=`` specification we replace
154  the field with the default value and change the spec to 's'
155  * if the value is None we replace it with an empty string
156 
157  then we just run the normal formatter ...
158  """
159  if spec.startswith("="):
160  if value is None:
161  value = spec[1:]
162  spec = "s"
163  if value is None:
164  value = ""
165  spec = ""
166  return super().format_field(value, spec)
167 
168  def get_field(self, field_name, args, kwargs):
169  """
170  Try to get the field as usual but in case we cannot find it because it
171  either doesn't exist or it doesn't have the correct attribute/item we
172  just return None
173  """
174  try:
175  return super().get_field(field_name, args, kwargs)
176  except (KeyError, TypeError):
177  return None, None
178 
179 
180 def format_filename(template, filename, metadata_json):
181  """
182  Format a file name as described in BELLE2-NOTE-TE-2017-012
183 
184  Parameters:
185  template (str): the format string
186  filename (str): the name of the file
187  metadata_json (str): a string representation of the file metadata json
188 
189  Returns:
190  a string with the formatted file name
191  """
192  filepath = Path(filename)
193  metadata = json.loads(metadata_json)
194  for key, value in metadata.items():
195  if isinstance(value, dict):
196  metadata[key] = AttrDict(value)
197  formatter = DefaultValueFormatter()
198  result = formatter.format(template, file=filepath, **metadata)
199  result = re.sub(r'//+', "/", result)
200  result = re.sub(r'\s+', '_', result)
201  return result
B2Tools.format.AttrDict.__init__
def __init__(self, content)
Definition: format.py:135
B2Tools.format.AttrDict.__getattr__
def __getattr__(self, key)
Definition: format.py:140
B2Tools.format.DefaultValueFormatter.get_field
def get_field(self, field_name, args, kwargs)
Definition: format.py:168
B2Tools.format.DefaultValueFormatter.format_field
def format_field(self, value, spec)
Definition: format.py:149
B2Tools.format.AttrDict
Definition: format.py:131
B2Tools.format.DefaultValueFormatter
Definition: format.py:145
B2Tools.format.AttrDict.__content
__content
Dictionary we want to access via attributes.
Definition: format.py:138