Mercurial > dottes
comparison abcfield.py @ 586:daa3b76bd11f
More abcfield.py updates and mark it Python 3.
- Move title fixups into Python.
- Move key display name expansion into Python.
- Add Dottes-specific header continuation line format. None of the usual
tools appears to support the official +: continuation.
author | Jim Hague <jim.hague@acm.org> |
---|---|
date | Mon, 31 Oct 2016 23:48:45 +0000 |
parents | 696c461c8dc0 |
children | afc031477784 |
comparison
equal
deleted
inserted
replaced
585:0a2fc73ba5ec | 586:daa3b76bd11f |
---|---|
1 #!/usr/bin/env python | 1 #!/usr/bin/env python3 |
2 # | 2 # |
3 # Extact a text field (title, by default) from a .abc file, and print it out | 3 # Extact a text field (title, by default) from a .abc file, and print it out |
4 # with any ABC accented characters converted to HTML (default) or Latex. | 4 # with any ABC accented characters converted to HTML (default) or Latex. |
5 # Recognise continuation fields and print those too. | 5 # |
6 # Optionally rearrange a field into display format: | |
7 # * In Title fields, change 'sort' form such as 'Exploding Potato, The' | |
8 # to display format 'The Exploding Potato'. | |
9 # * In Key fields, translate the ABC key representation to full text, | |
10 # e.g. G#dor becomes G# Dorian. | |
11 # | |
12 # Recognise continuation header fields and print those too. The ABC standard | |
13 # defines continuation fields as starting ':+'. Regrettably none of the tools | |
14 # I am using the Booke recognise that syntax, so I am adopting a Booke | |
15 # convention of '<header>:+' *also* being a continuation. Note that a | |
16 # continuation is a distinct line in the field value; the value has a line | |
17 # break between it and the previous line. | |
6 # | 18 # |
7 | 19 |
8 import optparse | 20 import optparse |
9 import sys | 21 import sys |
10 | 22 |
85 "AE" : ("Æ", "\\AE"), | 97 "AE" : ("Æ", "\\AE"), |
86 "ae" : ("æ", "\\ae"), | 98 "ae" : ("æ", "\\ae"), |
87 "ss" : ("ß", "\\ss"), | 99 "ss" : ("ß", "\\ss"), |
88 } | 100 } |
89 | 101 |
90 def convertField(t, options): | 102 abckeys = { |
103 "m": "Minor", | |
104 "min": "Minor", | |
105 "mix": "Mixolydian", | |
106 "dor": "Dorian", | |
107 "phr": "Phrygian", | |
108 "lyd": "Lydian", | |
109 "loc": "Locrian", | |
110 } | |
111 | |
112 # Convert ABC accented chars to HTML entities or LaTex. | |
113 def convertAccents(t, latex=False): | |
91 res = "" | 114 res = "" |
92 while True: | 115 while True: |
93 p = t.partition('\\') | 116 p = t.partition('\\') |
94 res += p[0] | 117 res += p[0] |
95 if p[1] == "": | 118 if p[1] == "": |
96 break | 119 break |
97 abc = p[2][0:2] | 120 abc = p[2][0:2] |
98 t = p[2][2:] | 121 t = p[2][2:] |
99 if abc in accentedletters: | 122 if abc in accentedletters: |
100 if options.html: | 123 if latex: |
124 res += accentedletters[abc][1] | |
125 else: | |
101 res += accentedletters[abc][0] | 126 res += accentedletters[abc][0] |
102 else: | |
103 res += accentedletters[abc][1] | |
104 else: | 127 else: |
105 res += "\\" + abc | 128 res += "\\" + abc |
106 return res | 129 return res |
107 | 130 |
108 def process(inf, options): | 131 # Convert Title fields from sort to display, so Bat, The->The Bat. |
109 n = options.index | 132 def convertTitleToDisplay(t): |
110 found = False | 133 p = t.rpartition(',') |
134 if p[1] == "": | |
135 return t | |
136 else: | |
137 return p[2].strip() + " " + p[0].strip() | |
138 | |
139 # Convert Key field from ABC to display, so G#dor->G# Dorian. | |
140 def convertKeyToDisplay(t): | |
141 letter = t[0].upper() | |
142 accidental = "" | |
143 mode = "" | |
144 try: | |
145 accidental = t[1] | |
146 if accidental == '#' or accidental == 'b': | |
147 mode = t[2:] | |
148 else: | |
149 accidental = "" | |
150 mode = t[1:] | |
151 except IndexError: | |
152 pass | |
153 mode = mode.strip().lower() | |
154 return letter + accidental + ' ' + abckeys.get(mode, "Major") | |
155 | |
156 # Return the raw text for a given field. Optionally the nth field is taken, | |
157 # or the field data must start with a designated string to be recognised. | |
158 def getFieldText(inf, field, n = 1, starts = None): | |
159 res = None | |
111 for line in inf: | 160 for line in inf: |
112 line = line.strip() | 161 line = line.strip() |
113 if len(line) > 2 and line[1] == ':': | 162 if len(line) > 2 and line[1] == ':': |
114 if found: | 163 if line[0] == "+" or (line[0] == field and line[2] == "+"): |
115 if line[0] != '+': | 164 if not res: |
165 continue | |
166 if line[0] == "+": | |
167 line = line[2:] | |
168 else: | |
169 line = line[3:] | |
170 res = res + '\n' + line.strip() | |
171 else: | |
172 if res: | |
116 break | 173 break |
117 line = line[2:].strip() | 174 if line[0] == field: |
118 elif line[0] == options.field: | |
119 if n > 1: | |
120 n = n - 1 | |
121 continue | |
122 else: | |
123 line = line[2:].strip() | 175 line = line[2:].strip() |
124 if len(options.starts) > 0: | 176 if starts: |
125 if line.find(options.starts) == 0: | 177 if line.find(starts) != 0: |
126 line = line[len(options.starts):].strip() | |
127 else: | |
128 continue | 178 continue |
129 else: | 179 line = line[len(starts):].strip() |
130 continue | 180 if n > 1: |
131 found = True | 181 n = n - 1 |
132 print(convertField(line, options)) | 182 continue |
133 return found | 183 res = line |
134 | 184 return res |
135 parser = optparse.OptionParser(usage="usage: %prog [options] [filename]\n\n" | 185 |
136 " Extract field data from ABC file.") | 186 # Return display text for a given field. |
137 parser.add_option("-f", "--field", dest="field", default="T", | 187 def getFieldDisplayText(inf, field, n = 1, starts = None, latex = False): |
138 help="extract the field FIELD", metavar="FIELD") | 188 res = getFieldText(inf, field, n, starts) |
139 parser.add_option("-l", "--latex", dest="latex", | 189 if res: |
140 action="store_true", default=False, | 190 if field.upper() == "T": |
141 help="convert special characters for LaTeX") | 191 res = convertTitleToDisplay(res) |
142 parser.add_option("-n", "--index", dest="index", | 192 elif field.upper() == "K": |
143 action="store", type="int", default=1, | 193 res = convertKeyToDisplay(res) |
144 help="report INDEXth value [default: %default]", | 194 res = convertAccents(res, latex) |
145 metavar="INDEX") | 195 return res |
146 parser.add_option("-s", "--starts", dest="starts", | 196 |
147 action="store", type="string", default="", | 197 if __name__ == "__main__": |
148 help="report only if line starts CONTENT and remove CONTENT", | 198 def process(inf, options): |
149 metavar="CONTENT") | 199 if options.display: |
150 (options, args) = parser.parse_args() | 200 line = getFieldDisplayText(inf, options.field, options.index, options.starts, options.latex) |
151 | 201 else: |
152 res = False | 202 line = getFieldText(inf, options.field, options.index, options.starts) |
153 if len(args) > 0: | 203 if line: |
154 for arg in args: | 204 print(line) |
155 try: | 205 return True |
156 inf = open(arg, "r") | 206 else: |
157 res = res or process(inf, options) | 207 return False |
158 finally: | 208 |
159 inf.close() | 209 # execute only if run as a script |
160 else: | 210 parser = optparse.OptionParser(usage="usage: %prog [options] [filename]\n\n" |
161 res = process(sys.stdin, options) | 211 " Extract field data from ABC file.") |
162 sys.exit(int(not res)) | 212 parser.add_option("-f", "--field", dest="field", default="T", |
213 help="extract the field FIELD", metavar="FIELD") | |
214 parser.add_option("-l", "--latex", dest="latex", | |
215 action="store_true", default=False, | |
216 help="convert special characters for LaTeX") | |
217 parser.add_option("-d", "--display", dest="display", | |
218 action="store_true", default=False, | |
219 help="convert to display text") | |
220 parser.add_option("-n", "--index", dest="index", | |
221 action="store", type="int", default=1, | |
222 help="report INDEXth value [default: %default]", | |
223 metavar="INDEX") | |
224 parser.add_option("-s", "--starts", dest="starts", | |
225 action="store", type="string", default=None, | |
226 help="report only if line starts CONTENT and remove CONTENT", | |
227 metavar="CONTENT") | |
228 (options, args) = parser.parse_args() | |
229 | |
230 res = False | |
231 if len(args) > 0: | |
232 for arg in args: | |
233 try: | |
234 inf = open(arg, "r") | |
235 res = res or process(inf, options) | |
236 finally: | |
237 inf.close() | |
238 else: | |
239 res = process(sys.stdin, options) | |
240 sys.exit(int(not res)) |