import os
import re # find and replace
import shutil # move tmp file for overwrite
cantoDir = os.path.abspath(os.getcwd())
def getXMLFileName(xmlCantoFile):
xmlCanto = xmlCantoFile.split('.')[0] + '.xml'
return xmlCanto
def addWordTags(cantoFile):
"""
- [x] Parse filename to create xml file
- [x] Open file as one huge string (keep track of newlines)
- [x] Replace every space with a dummy char not used in the text, like `~`
- [ ] Traverse the big file-string from beginning to end
- [x] Define a "word-chars" group: letters, apostrofes
- [x] If you encounter a char that's a word-char
put a `` in front of it and move to the next.
- [x] If the next char is also a word-char, move on
to the next because we're still in a word.
- [x] If the next char is not a word-char,
put a `` in front of it and move to the next.
- [x] Replace all `~` with spaces and get back the newlines
- [x] Write to XML file
"""
xmlCanto = getXMLFileName(cantoFile)
with open(xmlCanto, 'w') as xmlCanto:
with open(cantoFile, 'r') as txtCanto:
originalText = txtCanto.read()
# Prepare canto for traversing
plainText = traversePreparation(originalText, '\n', '+', False)
plainText = traversePreparation(plainText, ' ', '~', False)
# Define variables
wordChars = '[a-zA-ZÀ-ÿ0-9‘’]'
openWord = ''
closeWord = ''
insideWord = False
# Traverse canto
for char in plainText:
if re.match(wordChars, char):
if insideWord == False:
xmlCanto.write(openWord)
xmlCanto.write(char)
insideWord = True
else:
xmlCanto.write(char)
else:
if char == '~':
if insideWord == True:
xmlCanto.write(closeWord + ' ')
insideWord = False
else:
xmlCanto.write(' ')
elif char == '+':
if insideWord == True:
xmlCanto.write(closeWord + '\n') # line ends with word
insideWord = False
else:
xmlCanto.write('\n') # line ends with punctuation
elif insideWord == True:
xmlCanto.write(closeWord + char) # punctuation right after to word
insideWord = False
else:
xmlCanto.write(char) # punctuation after punctuation
txtCanto.close()
xmlCanto.close()
def traversePreparation(cantoText, find, replace, negate):
"""
Substitute find with replace.
If negate is True, substitute replace with find.
"""
if negate == False:
replacedText = cantoText.replace(find, replace)
else:
replacedText = cantoText.replace(replace, find)
return replacedText
def addLineTags(xmlCantoFile):
"""
add to the beginning of each line number n
add to the end of each line
"""
xmlCanto = getXMLFileName(xmlCantoFile)
tmpFile = '.' + xmlCantoFile + '.tmp'
with open(xmlCanto, 'r') as xmlCanto:
with open(tmpFile, 'w') as tmpCanto:
i = 1
for line in xmlCanto:
# replace '\n' in line with ' \n'
newLine = re.sub('\n', ' \n', line)
newLine = ' ' + newLine
i += 1
tmpCanto.write(newLine)
closeCopyRemoveTmp(tmpFile, tmpCanto, xmlCantoFile, xmlCanto)
def addTercetTags(xmlCantoFile):
"""
add ... around each group of three lines until the end
"""
xmlCanto = getXMLFileName(xmlCantoFile)
tmpFile = '.' + xmlCantoFile + '.tmp'
with open(xmlCanto, 'r') as xmlCanto:
lastLine = xmlCanto.readlines()[-1]
lastLineNum = int(lastLine.split('"')[1])
xmlCanto.seek(0)
with open(tmpFile, 'w') as tmpCanto:
i = 0
j = 2
for line in xmlCanto:
#print(line)
i += 1
#print('new i: ' + str(i))
if i == 1:
newLine = '\n' + line
tmpCanto.write(newLine)
#print('first line newline: ' + newLine)
elif i % 3 == 0:
tercetNum = str(j)
newLineSub = '\n\n\n'
newLine = re.sub('\n', newLineSub, line)
tmpCanto.write(newLine)
j += 1
#print('every 3 lines newline: ' + newLine)
elif i == lastLineNum:
newLine = re.sub('\n', '\n', line)
tmpCanto.write(newLine)
#print('last line newline: ' + newLine)
else:
tmpCanto.write(line)
closeCopyRemoveTmp(tmpFile, tmpCanto, xmlCantoFile, xmlCanto)
def wrapTags(tag, xmlCantoFile):
"""
used to add , , and tags around body
"""
xmlCanto = getXMLFileName(xmlCantoFile)
cantoNum = str(int(xmlCanto.split('-')[0]))
canticle = xmlCanto.split('-')[1]
if tag == 'TEI':
openTag = '\n'
elif tag == 'canto':
openTag = '<' + tag + ' canticle="' + canticle + '"' + ' c_num="' + cantoNum + '">\n'
else:
openTag = '<' + tag + '>\n'
closeTag = '\n' + tag + '>'
with open(xmlCanto, 'r') as xmlCantoContents:
contents = xmlCantoContents.read()
with open(xmlCanto, 'w') as xmlCantoContents:
xmlCantoContents.write(openTag)
xmlCantoContents.write(contents)
xmlCantoContents.write(closeTag)
xmlCantoContents.close()
def addTEIHeader(xmlCantoFile):
"""
The following is the minimum required TEI header
"""
xmlCanto = getXMLFileName(xmlCantoFile)
def addXMLHeader(xmlCantoFile):
xmlCanto = getXMLFileName(xmlCantoFile)
xmlHeader = '\n\n'
with open(xmlCanto, 'r') as xmlCantoContents:
contents = xmlCantoContents.read()
with open(xmlCanto, 'w') as xmlCantoContents:
xmlCantoContents.write(xmlHeader)
xmlCantoContents.write(contents)
xmlCantoContents.close()
def addSpaces(xmlCantoFile):
"""
Add a given number of spaces before each opening and closing tag.
User specifies the number of spaces only once, in a series of lists
which hold strings of each tag that needs spaces before it. This
function parses the names of the list to determine the number of
spaces to add before each tag (e.g. list2 => 2 spaces). It replaces
the tags in the document with the spaces affixed.
"""
# XML is hierarchical, so each "level" needs its own number of spaces
levelDict = {
'0' : ['TEI'],
'1' : ['teiHeader', 'text' ],
'2' : ['fileDesc' , 'canto' ],
'3' : ['titleStmt', 'tercets'],
'4' : [ 'tercet' ],
'5' : [ 'l' ]
}
#levelDictLen = int(sorted(levelDict.keys())[-1]) + 1 # number of levels. add 1 to make range() work below
tags = ['TEI', 'teiHeader', 'text', 'fileDesc', 'canto', 'titleStmt', 'tercets', 'tercet', 'l' ]
xmlCanto = getXMLFileName(xmlCantoFile)
tmpFile = '.' + xmlCantoFile + '.tmp'
with open(xmlCantoFile, 'r') as xmlCanto:
with open(tmpFile, 'w') as tmpCanto:
for line in xmlCanto:
for tag in tags:
# need open tag regex b/c we don't know if tag has attributes,
# and if we did, we might not know their value
openTagRegex = '(<' + tag + ')(>|\s.*>)' # match and but not
closeTag = '' + tag + '>'
for level in levelDict.keys():
if tag in levelDict[level]:
spaces = ' ' * int(level)
if re.search(openTagRegex, line):
newLine = re.sub(openTagRegex, spaces + r"\1\2", line)
tmpCanto.write(newLine)
elif closeTag in line:
if tag != 'l': # don't add spaces to closing line tags
newLine = re.sub(closeTag, spaces + closeTag, line)
tmpCanto.write(newLine)
closeCopyRemoveTmp(tmpFile, tmpCanto, xmlCantoFile, xmlCanto)
def closeCopyRemoveTmp(tmpFile, tmpCanto, xmlCantoFile, xmlCanto):
tmpCanto.close()
xmlCanto.close()
shutil.copy(tmpFile, xmlCantoFile)
os.remove(tmpFile)
def main():
for cantoFile in os.listdir(cantoDir):
xmlCantoName = getXMLFileName(cantoFile)
if re.search('\.txt$', cantoFile):
addWordTags(cantoFile)
addLineTags(xmlCantoName)
addTercetTags(xmlCantoName)
wrapTags('tercets', xmlCantoName)
wrapTags('canto', xmlCantoName)
wrapTags('text', xmlCantoName)
wrapTags('TEI', xmlCantoName)
addSpaces(xmlCantoName)
addXMLHeader(xmlCantoName)
os.remove(cantoFile) # don't delete txt files while debugging so you don't have to keep copying them
main()