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" : ("&AElig;", "\\AE"), 97 "AE" : ("&AElig;", "\\AE"),
86 "ae" : ("&aelig;", "\\ae"), 98 "ae" : ("&aelig;", "\\ae"),
87 "ss" : ("&szlig;", "\\ss"), 99 "ss" : ("&szlig;", "\\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))