Belle II Software development
format.py
1#!/usr/bin/env python3
2
3
10
11from string import Formatter
12from pathlib import Path
13import re
14import json
15
16
17def 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
26def 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
45def 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 f'\\texorpdfstring{{{tex_string}}}{{{string(decay_string)}}}'
106
107
108def 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 += f"{hours}h"
123 if minutes != 0:
124 string += f"{minutes}m"
125 if seconds != 0 and hours == 0:
126 string += f"{seconds}s"
127 if ms != 0 and hours == 0 and minutes == 0 and seconds == 0:
128 string += f"{ms}ms"
129 if us != 0 and hours == 0 and minutes == 0 and seconds == 0 and ms == 0:
130 string += f"{us}$\\mu$s"
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
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
145
146 def __getattr__(self, key):
147 """Return any dictionay element as attribute"""
148 return self.__content[key]
149
150
151class 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
186def 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