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