moved files, added CSV export to data.py
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -33,3 +33,5 @@ nosetests.xml
|
|||||||
.mr.developer.cfg
|
.mr.developer.cfg
|
||||||
.project
|
.project
|
||||||
.pydevproject
|
.pydevproject
|
||||||
|
|
||||||
|
GoogleIssues2CSV.py
|
||||||
|
|||||||
74
atg.py
74
atg.py
@@ -1,74 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
'''
|
|
||||||
Advanced Text Generator module for a KaiSD Text Tools.
|
|
||||||
|
|
||||||
(c) 2013 Ivan "Kai SD" Korystin
|
|
||||||
|
|
||||||
License: GPLv3
|
|
||||||
'''
|
|
||||||
from os.path import join, exists
|
|
||||||
from os import makedirs
|
|
||||||
|
|
||||||
class ATG:
|
|
||||||
'''
|
|
||||||
Advanced Text Generator is a class, created to generate multiple
|
|
||||||
text files from table data.
|
|
||||||
'''
|
|
||||||
def __init__(self, data, template):
|
|
||||||
'''
|
|
||||||
Constructor.
|
|
||||||
data - an instance of the data.Data class (i.e. CSVData)
|
|
||||||
template - an instance of the template.Template class (i.e. TemplateV2)
|
|
||||||
'''
|
|
||||||
self.data = data
|
|
||||||
self.template = template
|
|
||||||
self.out = template.process(data)
|
|
||||||
|
|
||||||
if type(self.out) == dict:
|
|
||||||
self.multiple = True
|
|
||||||
else:
|
|
||||||
self.multiple = False
|
|
||||||
|
|
||||||
def write_files(self, outputDir='.'):
|
|
||||||
'''
|
|
||||||
Write generated files to the given directory.
|
|
||||||
'''
|
|
||||||
encoding = self.template.encoding
|
|
||||||
extension = self.template.extension
|
|
||||||
out = self.out
|
|
||||||
if self.multiple:
|
|
||||||
for name in out.keys():
|
|
||||||
namepath = name.replace('\\', '/').split('/')
|
|
||||||
newpath = u''
|
|
||||||
for i in namepath[:-1]:
|
|
||||||
newpath = join(newpath, i)
|
|
||||||
if not exists(join(unicode(outputDir),newpath)):
|
|
||||||
makedirs(join(unicode(outputDir),newpath))
|
|
||||||
fname = join(unicode(outputDir),name+'.'+extension)
|
|
||||||
if fname.endswith('.'):
|
|
||||||
fname = fname[:-1]
|
|
||||||
f = open(fname, 'w')
|
|
||||||
f.write(out[name].encode(encoding))
|
|
||||||
self.log(' Saved %s' % (name+'.'+extension))
|
|
||||||
f.close()
|
|
||||||
else:
|
|
||||||
name = self.template.bonusPrefix + '.' + extension
|
|
||||||
if name == '.':
|
|
||||||
name = self.template.keyField
|
|
||||||
namepath = name.replace('\\', '/').split('/')
|
|
||||||
newpath = u''
|
|
||||||
for i in namepath[:-1]:
|
|
||||||
newpath = join(newpath, i)
|
|
||||||
if not exists(join(unicode(outputDir),newpath)):
|
|
||||||
makedirs(join(unicode(outputDir),newpath))
|
|
||||||
f = open(join(unicode(outputDir),name+'.'+extension), 'w')
|
|
||||||
f.write(out.encode(encoding))
|
|
||||||
self.log(' Saved %s' % (name+'.'+extension))
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
def log(self, text):
|
|
||||||
'''
|
|
||||||
Print information
|
|
||||||
'''
|
|
||||||
pass
|
|
||||||
168
atr.py
168
atr.py
@@ -1,168 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
'''
|
|
||||||
Advanced Text Replacer module for a KaiSD Text Tools.
|
|
||||||
|
|
||||||
(c) 2013 Ivan "Kai SD" Korystin
|
|
||||||
|
|
||||||
License: GPLv3
|
|
||||||
'''
|
|
||||||
import re
|
|
||||||
class ATR:
|
|
||||||
'''
|
|
||||||
Advanced Text Replacer - is a class, created to make multiple replacements
|
|
||||||
in the content or names of text file.
|
|
||||||
It can make plain replacements, or use ATG templates to do something more complex.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def __init__(self, files):
|
|
||||||
'''
|
|
||||||
Constructor
|
|
||||||
'''
|
|
||||||
self.files = files
|
|
||||||
self.replacements = []
|
|
||||||
|
|
||||||
def plain_replace(self, pattern, string, regexp=False):
|
|
||||||
'''
|
|
||||||
Replaces the given pattern with string in files.
|
|
||||||
'''
|
|
||||||
if regexp:
|
|
||||||
pattern = re.compile(pattern)
|
|
||||||
self.replacements.append((pattern, string))
|
|
||||||
|
|
||||||
|
|
||||||
def templated_replace(self, pattern, template, data, keyFormat='filename', regexp=False):
|
|
||||||
'''
|
|
||||||
Replaces the given pattern with data formated by template.
|
|
||||||
Valid values for keyFormat:
|
|
||||||
filename - take data rows by filename(path ignored), key value of the data row should store the filename.
|
|
||||||
fullname - as filename, but with path.
|
|
||||||
index - take data rows in order, key value of the data row should store the index. Indexes starts with 0.
|
|
||||||
If filename or index cannot be found in data keys, pattern will not be replaced.
|
|
||||||
'''
|
|
||||||
if regexp:
|
|
||||||
pattern = re.compile(pattern)
|
|
||||||
strings = template.process(data)
|
|
||||||
self.replacements.append((pattern, strings, keyFormat))
|
|
||||||
|
|
||||||
|
|
||||||
def write_in_place(self):
|
|
||||||
'''
|
|
||||||
Do replacement and save the files
|
|
||||||
'''
|
|
||||||
for f in self.files:
|
|
||||||
out = u''
|
|
||||||
with open(f, 'rb') as file:
|
|
||||||
out = file.read()
|
|
||||||
|
|
||||||
idx = 0
|
|
||||||
for r in self.replacements:
|
|
||||||
if type(r[0]) in (str, unicode):
|
|
||||||
pattern = re.compile(re.escape(r[0]))
|
|
||||||
string = r[1]
|
|
||||||
elif type(r[0]) is dict and len(r) == 3:
|
|
||||||
if r[2] == 'filename':
|
|
||||||
fname = f.replace('\\', '/').split('/')[-1]
|
|
||||||
string = f[1].get(fname, None)
|
|
||||||
elif r[2] == 'fullname':
|
|
||||||
string = f[1].get(f, None)
|
|
||||||
elif r[2] == 'index':
|
|
||||||
fname = f.replace('\\', '/').split('/')[-1]
|
|
||||||
string = f[1].get(idx, None)
|
|
||||||
else:
|
|
||||||
raise BaseException('Unknown data key format.')
|
|
||||||
elif hasattr(r[0], 'match'):
|
|
||||||
pattern = r[0]
|
|
||||||
string = r[1]
|
|
||||||
else:
|
|
||||||
raise BaseException('Unknown pattern type.')
|
|
||||||
if string:
|
|
||||||
out = re.sub(pattern, string, out)
|
|
||||||
|
|
||||||
with open(f, 'wb') as outfile:
|
|
||||||
outfile.write(out)
|
|
||||||
|
|
||||||
def write_new_files(self, outfiles):
|
|
||||||
'''
|
|
||||||
Do replacement, but save to given files instead of the original ones.
|
|
||||||
'''
|
|
||||||
if not len(outfiles) == len(self.files):
|
|
||||||
raise BaseException('Lists of original and new files has different length.')
|
|
||||||
|
|
||||||
for f in self.files:
|
|
||||||
out = u''
|
|
||||||
with open(f, 'rb') as file:
|
|
||||||
out = file.read()
|
|
||||||
|
|
||||||
idx = 0
|
|
||||||
for r in self.replacements:
|
|
||||||
if type(r[0]) in (str, unicode):
|
|
||||||
pattern = re.compile(re.escape(r[0]))
|
|
||||||
string = r[1]
|
|
||||||
elif type(r[0]) is dict and len(r) == 3:
|
|
||||||
if r[2] == 'filename':
|
|
||||||
fname = f.replace('\\', '/').split('/')[-1]
|
|
||||||
string = f[1].get(fname, None)
|
|
||||||
elif r[2] == 'fullname':
|
|
||||||
string = f[1].get(f, None)
|
|
||||||
elif r[2] == 'index':
|
|
||||||
fname = f.replace('\\', '/').split('/')[-1]
|
|
||||||
string = f[1].get(idx, None)
|
|
||||||
else:
|
|
||||||
raise BaseException('Unknown data key format.')
|
|
||||||
elif hasattr(r[0], 'match'):
|
|
||||||
pattern = r[0]
|
|
||||||
string = r[1]
|
|
||||||
else:
|
|
||||||
raise BaseException('Unknown pattern type.')
|
|
||||||
if string:
|
|
||||||
out = re.sub(pattern, string, out)
|
|
||||||
|
|
||||||
with open(outfiles[self.files.index(f)], 'wb') as outfile:
|
|
||||||
outfile.write(out)
|
|
||||||
|
|
||||||
def replace_in_names(self):
|
|
||||||
'''
|
|
||||||
Do replacement, but in file names instead of file content. Returns the list of new file names,
|
|
||||||
you can use it with writeNewFiles() method.
|
|
||||||
'''
|
|
||||||
out = []
|
|
||||||
for f in self.files:
|
|
||||||
new = f
|
|
||||||
idx = 0
|
|
||||||
for r in self.replacements:
|
|
||||||
if type(r[0]) in (str, unicode):
|
|
||||||
pattern = re.compile(re.escape(r[0]))
|
|
||||||
string = r[1]
|
|
||||||
elif type(r[0]) is dict and len(r) == 3:
|
|
||||||
if r[2] == 'filename':
|
|
||||||
fname = f.replace('\\', '/').split('/')[-1]
|
|
||||||
string = f[1].get(fname, None)
|
|
||||||
elif r[2] == 'fullname':
|
|
||||||
string = f[1].get(f, None)
|
|
||||||
elif r[2] == 'index':
|
|
||||||
fname = f.replace('\\', '/').split('/')[-1]
|
|
||||||
string = f[1].get(idx, None)
|
|
||||||
else:
|
|
||||||
raise BaseException('Unknown data key format.')
|
|
||||||
elif hasattr(r[0], 'match'):
|
|
||||||
pattern = r[0]
|
|
||||||
string = r[1]
|
|
||||||
else:
|
|
||||||
raise BaseException('Unknown pattern type.')
|
|
||||||
if string:
|
|
||||||
new = re.sub(pattern, string, new)
|
|
||||||
out.append(new)
|
|
||||||
return out
|
|
||||||
|
|
||||||
def clear_replacements(self):
|
|
||||||
'''
|
|
||||||
Removes all replacements.
|
|
||||||
'''
|
|
||||||
self.replacements = []
|
|
||||||
|
|
||||||
def log(self, string):
|
|
||||||
'''
|
|
||||||
Print information
|
|
||||||
'''
|
|
||||||
pass
|
|
||||||
191
data.py
191
data.py
@@ -1,191 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
'''
|
|
||||||
Data module for a KaiSD Text Tools.
|
|
||||||
|
|
||||||
(c) 2013 Ivan "Kai SD" Korystin
|
|
||||||
|
|
||||||
License: GPLv3
|
|
||||||
'''
|
|
||||||
|
|
||||||
import csv, codecs
|
|
||||||
|
|
||||||
class Data:
|
|
||||||
'''
|
|
||||||
Empty data class. Can be used for a subclassing or procedural data creation.
|
|
||||||
'''
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
'''
|
|
||||||
Constructor
|
|
||||||
'''
|
|
||||||
self.keys = []
|
|
||||||
self.rows = []
|
|
||||||
|
|
||||||
def __getitem__(self, pair):
|
|
||||||
'''
|
|
||||||
Returns a value for given key and row.
|
|
||||||
'''
|
|
||||||
key = pair[0]
|
|
||||||
row = pair[1]
|
|
||||||
|
|
||||||
keys = self.keys
|
|
||||||
rows = self.rows
|
|
||||||
if key in keys:
|
|
||||||
if len(rows) > row:
|
|
||||||
return rows[row][keys.index(key)]
|
|
||||||
else:
|
|
||||||
raise BaseException('Row %i not found in data' % (row))
|
|
||||||
else:
|
|
||||||
raise BaseException('Named value %s not found in data' % (key))
|
|
||||||
|
|
||||||
def __setitem__(self, pair, value):
|
|
||||||
'''
|
|
||||||
Sets a value for given key and row.
|
|
||||||
'''
|
|
||||||
key = pair[0]
|
|
||||||
row = pair[1]
|
|
||||||
|
|
||||||
keys = self.keys
|
|
||||||
rows = self.rows
|
|
||||||
if key in keys:
|
|
||||||
if len(rows) > row:
|
|
||||||
rows[row][keys.index(key)] = value
|
|
||||||
else:
|
|
||||||
raise BaseException('Row %i not found in data' % (row))
|
|
||||||
else:
|
|
||||||
raise BaseException('Named value %s not found in data' % (key))
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
'''
|
|
||||||
Returns data as string.
|
|
||||||
'''
|
|
||||||
return str((self.keys, self.rows))
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return self.__str__()
|
|
||||||
|
|
||||||
def has_key(self, key):
|
|
||||||
'''
|
|
||||||
Returns True if given key exists in data
|
|
||||||
'''
|
|
||||||
return key in self.keys
|
|
||||||
|
|
||||||
def add_rows(self, n=1):
|
|
||||||
'''
|
|
||||||
Adds some empty rows to the data.
|
|
||||||
'''
|
|
||||||
keys = self.keys
|
|
||||||
rows = self.rows
|
|
||||||
|
|
||||||
for n in xrange(0, n):
|
|
||||||
row = []
|
|
||||||
for k in keys:
|
|
||||||
row.append('')
|
|
||||||
rows.append(row)
|
|
||||||
|
|
||||||
def add_keys(self, *h):
|
|
||||||
'''
|
|
||||||
Adds new keys to the data.
|
|
||||||
'''
|
|
||||||
keys = self.keys
|
|
||||||
rows = self.rows
|
|
||||||
|
|
||||||
for i in h:
|
|
||||||
keys.append(i)
|
|
||||||
for r in rows:
|
|
||||||
for i in h:
|
|
||||||
r.append('')
|
|
||||||
|
|
||||||
def col_by_key(self, key):
|
|
||||||
'''
|
|
||||||
Returns a column by header's name
|
|
||||||
'''
|
|
||||||
cols = []
|
|
||||||
keys = self.keys
|
|
||||||
rows = self.rows
|
|
||||||
if key in keys:
|
|
||||||
idx = keys.index(key)
|
|
||||||
for r in rows:
|
|
||||||
cols.append(r[idx])
|
|
||||||
else:
|
|
||||||
raise BaseException('Named value %s not found in data' % (key))
|
|
||||||
return tuple(cols)
|
|
||||||
|
|
||||||
def row_by_idx(self, idx):
|
|
||||||
'''
|
|
||||||
Returns a row by index.
|
|
||||||
'''
|
|
||||||
return tuple(self.rows[idx])
|
|
||||||
|
|
||||||
class CSVData(Data):
|
|
||||||
'''
|
|
||||||
Class for reading CSV files.
|
|
||||||
'''
|
|
||||||
class Reader:
|
|
||||||
class Recoder:
|
|
||||||
def __init__(self, f, encoding):
|
|
||||||
self.reader = codecs.getreader(encoding)(f)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def next(self):
|
|
||||||
return self.reader.next().encode("utf-8")
|
|
||||||
|
|
||||||
def __init__(self, f, dialect=csv.excel, encoding="utf-8", **kwds):
|
|
||||||
f = self.Recoder(f, encoding)
|
|
||||||
self.reader = csv.reader(f, dialect=dialect, **kwds)
|
|
||||||
|
|
||||||
def next(self):
|
|
||||||
row = self.reader.next()
|
|
||||||
return [unicode(s, "utf-8") for s in row]
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __init__(self, filename, encoding='utf-8', delimiter=';', quotechar='"', **kwargs):
|
|
||||||
'''
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
filename - CSV table filename
|
|
||||||
encoding - CSV table encoding
|
|
||||||
delimiter - CSV table delimiter
|
|
||||||
quotechar - CSV table quotechar
|
|
||||||
transpose=True - transpose the table
|
|
||||||
'''
|
|
||||||
csvfile = self.Reader(open(filename), encoding=encoding, delimiter=delimiter, quotechar=quotechar)
|
|
||||||
sourceData = []
|
|
||||||
sourcekeys = None
|
|
||||||
|
|
||||||
if kwargs.get('transpose', False):
|
|
||||||
sourcekeys = []
|
|
||||||
rowData = []
|
|
||||||
for i in csvfile:
|
|
||||||
sourcekeys.append(i[0])
|
|
||||||
for k in xrange(1, len(i)):
|
|
||||||
sourceData.append([])
|
|
||||||
try:
|
|
||||||
i[k] = int(i[k])
|
|
||||||
except:
|
|
||||||
try:
|
|
||||||
i[k] = float(i[k])
|
|
||||||
except:
|
|
||||||
i[k] = i[k]
|
|
||||||
rowData.append(i[1:])
|
|
||||||
sourceData = list(map(lambda *x:x, *rowData))
|
|
||||||
else:
|
|
||||||
for i in csvfile:
|
|
||||||
if sourcekeys is None:
|
|
||||||
sourcekeys = i
|
|
||||||
else:
|
|
||||||
for k in xrange(0, len(i)):
|
|
||||||
try:
|
|
||||||
i[k] = int(i[k])
|
|
||||||
except:
|
|
||||||
try:
|
|
||||||
i[k] = float(i[k])
|
|
||||||
except:
|
|
||||||
i[k] = i[k]
|
|
||||||
sourceData.append(i)
|
|
||||||
self.keys = sourcekeys
|
|
||||||
self.rows = sourceData
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
||||||
<html><head><title>Python: module atg</title>
|
|
||||||
</head><body bgcolor="#f0f0f8">
|
|
||||||
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
|
|
||||||
<tr bgcolor="#7799ee">
|
|
||||||
<td valign=bottom> <br>
|
|
||||||
<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong>atg</strong></big></big></font></td
|
|
||||||
><td align=right valign=bottom
|
|
||||||
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:///C:/users/kaisd/documents/workbench/programming/ktt/src/atg.py">c:\users\kaisd\documents\workbench\programming\ktt\src\atg.py</a></font></td></tr></table>
|
|
||||||
<p><tt>Advanced Text Generator module for a KaiSD Text Tools.<br>
|
|
||||||
<br>
|
|
||||||
(c) 2013 Ivan "Kai SD" Korystin <br>
|
|
||||||
<br>
|
|
||||||
License: GPLv3</tt></p>
|
|
||||||
<p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#ee77aa">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
|
||||||
|
|
||||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td>
|
|
||||||
<td width="100%"><dl>
|
|
||||||
<dt><font face="helvetica, arial"><a href="atg.html#ATG">ATG</a>
|
|
||||||
</font></dt></dl>
|
|
||||||
<p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#ffc8d8">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#000000" face="helvetica, arial"><a name="ATG">class <strong>ATG</strong></a></font></td></tr>
|
|
||||||
|
|
||||||
<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td>
|
|
||||||
<td colspan=2><tt>Advanced Text Generator is a class, created to generate multiple<br>
|
|
||||||
text files from table data.<br> </tt></td></tr>
|
|
||||||
<tr><td> </td>
|
|
||||||
<td width="100%">Methods defined here:<br>
|
|
||||||
<dl><dt><a name="ATG-__init__"><strong>__init__</strong></a>(self, data, template)</dt><dd><tt>Constructor.<br>
|
|
||||||
data - an instance of the data.Data class (i.e. CSVData)<br>
|
|
||||||
template - an instance of the template.Template class (i.e. TemplateV2)</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="ATG-log"><strong>log</strong></a>(self, text)</dt><dd><tt>Print information</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="ATG-write_files"><strong>write_files</strong></a>(self, outputDir<font color="#909090">='.'</font>)</dt><dd><tt>Write generated files to the given directory.</tt></dd></dl>
|
|
||||||
|
|
||||||
</td></tr></table></td></tr></table>
|
|
||||||
</body></html>
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
||||||
<html><head><title>Python: module atr</title>
|
|
||||||
</head><body bgcolor="#f0f0f8">
|
|
||||||
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
|
|
||||||
<tr bgcolor="#7799ee">
|
|
||||||
<td valign=bottom> <br>
|
|
||||||
<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong>atr</strong></big></big></font></td
|
|
||||||
><td align=right valign=bottom
|
|
||||||
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:///C:/users/kaisd/documents/workbench/programming/ktt/src/atr.py">c:\users\kaisd\documents\workbench\programming\ktt\src\atr.py</a></font></td></tr></table>
|
|
||||||
<p><tt>Advanced Text Replacer module for a KaiSD Text Tools.<br>
|
|
||||||
<br>
|
|
||||||
(c) 2013 Ivan "Kai SD" Korystin <br>
|
|
||||||
<br>
|
|
||||||
License: GPLv3</tt></p>
|
|
||||||
<p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#aa55cc">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
|
||||||
|
|
||||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </td>
|
|
||||||
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="re.html">re</a><br>
|
|
||||||
</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#ee77aa">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
|
||||||
|
|
||||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td>
|
|
||||||
<td width="100%"><dl>
|
|
||||||
<dt><font face="helvetica, arial"><a href="atr.html#ATR">ATR</a>
|
|
||||||
</font></dt></dl>
|
|
||||||
<p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#ffc8d8">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#000000" face="helvetica, arial"><a name="ATR">class <strong>ATR</strong></a></font></td></tr>
|
|
||||||
|
|
||||||
<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td>
|
|
||||||
<td colspan=2><tt>Advanced Text Replacer - is a class, created to make multiple replacements<br>
|
|
||||||
in the content or names of text file.<br>
|
|
||||||
It can make plain replacements, or use ATG templates to do something more complex.<br> </tt></td></tr>
|
|
||||||
<tr><td> </td>
|
|
||||||
<td width="100%">Methods defined here:<br>
|
|
||||||
<dl><dt><a name="ATR-__init__"><strong>__init__</strong></a>(self, files)</dt><dd><tt>Constructor</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="ATR-clear_replacements"><strong>clear_replacements</strong></a>(self)</dt><dd><tt>Removes all replacements.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="ATR-log"><strong>log</strong></a>(self, string)</dt><dd><tt>Print information</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="ATR-plain_replace"><strong>plain_replace</strong></a>(self, pattern, string, regexp<font color="#909090">=False</font>)</dt><dd><tt>Replaces the given pattern with string in files.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="ATR-replace_in_names"><strong>replace_in_names</strong></a>(self)</dt><dd><tt>Do replacement, but in file names instead of file content. Returns the list of new file names,<br>
|
|
||||||
you can use it with writeNewFiles() method.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="ATR-templated_replace"><strong>templated_replace</strong></a>(self, pattern, template, data, keyFormat<font color="#909090">='filename'</font>, regexp<font color="#909090">=False</font>)</dt><dd><tt>Replaces the given pattern with data formated by template.<br>
|
|
||||||
Valid values for keyFormat:<br>
|
|
||||||
filename - take data rows by filename(path ignored), key value of the data row should store the filename.<br>
|
|
||||||
fullname - as filename, but with path.<br>
|
|
||||||
index - take data rows in order, key value of the data row should store the index. Indexes starts with 0.<br>
|
|
||||||
If filename or index cannot be found in data keys, pattern will not be replaced.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="ATR-write_in_place"><strong>write_in_place</strong></a>(self)</dt><dd><tt>Do replacement and save the files</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="ATR-write_new_files"><strong>write_new_files</strong></a>(self, outfiles)</dt><dd><tt>Do replacement, but save to given files instead of the original ones.</tt></dd></dl>
|
|
||||||
|
|
||||||
</td></tr></table></td></tr></table>
|
|
||||||
</body></html>
|
|
||||||
114
docs/data.html
114
docs/data.html
@@ -1,114 +0,0 @@
|
|||||||
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
||||||
<html><head><title>Python: module data</title>
|
|
||||||
</head><body bgcolor="#f0f0f8">
|
|
||||||
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
|
|
||||||
<tr bgcolor="#7799ee">
|
|
||||||
<td valign=bottom> <br>
|
|
||||||
<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong>data</strong></big></big></font></td
|
|
||||||
><td align=right valign=bottom
|
|
||||||
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:///C:/users/kaisd/documents/workbench/programming/ktt/src/data.py">c:\users\kaisd\documents\workbench\programming\ktt\src\data.py</a></font></td></tr></table>
|
|
||||||
<p><tt><a href="#Data">Data</a> module for a KaiSD Text Tools.<br>
|
|
||||||
<br>
|
|
||||||
(c) 2013 Ivan "Kai SD" Korystin <br>
|
|
||||||
<br>
|
|
||||||
License: GPLv3</tt></p>
|
|
||||||
<p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#aa55cc">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
|
||||||
|
|
||||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </td>
|
|
||||||
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="codecs.html">codecs</a><br>
|
|
||||||
</td><td width="25%" valign=top><a href="csv.html">csv</a><br>
|
|
||||||
</td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#ee77aa">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
|
||||||
|
|
||||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td>
|
|
||||||
<td width="100%"><dl>
|
|
||||||
<dt><font face="helvetica, arial"><a href="data.html#Data">Data</a>
|
|
||||||
</font></dt><dd>
|
|
||||||
<dl>
|
|
||||||
<dt><font face="helvetica, arial"><a href="data.html#CSVData">CSVData</a>
|
|
||||||
</font></dt></dl>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#ffc8d8">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#000000" face="helvetica, arial"><a name="CSVData">class <strong>CSVData</strong></a>(<a href="data.html#Data">Data</a>)</font></td></tr>
|
|
||||||
|
|
||||||
<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td>
|
|
||||||
<td colspan=2><tt>Class for reading CSV files.<br> </tt></td></tr>
|
|
||||||
<tr><td> </td>
|
|
||||||
<td width="100%">Methods defined here:<br>
|
|
||||||
<dl><dt><a name="CSVData-__init__"><strong>__init__</strong></a>(self, filename, encoding<font color="#909090">='utf-8'</font>, delimiter<font color="#909090">=';'</font>, quotechar<font color="#909090">='"'</font>, **kwargs)</dt><dd><tt>Constructor.<br>
|
|
||||||
<br>
|
|
||||||
filename - CSV table filename<br>
|
|
||||||
encoding - CSV table encoding<br>
|
|
||||||
delimiter - CSV table delimiter<br>
|
|
||||||
quotechar - CSV table quotechar<br>
|
|
||||||
transpose=True - transpose the table</tt></dd></dl>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
Data and other attributes defined here:<br>
|
|
||||||
<dl><dt><strong>Reader</strong> = <class data.Reader></dl>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
Methods inherited from <a href="data.html#Data">Data</a>:<br>
|
|
||||||
<dl><dt><a name="CSVData-__getitem__"><strong>__getitem__</strong></a>(self, pair)</dt><dd><tt>Returns a value for given key and row.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="CSVData-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="CSVData-__setitem__"><strong>__setitem__</strong></a>(self, pair, value)</dt><dd><tt>Sets a value for given key and row.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="CSVData-__str__"><strong>__str__</strong></a>(self)</dt><dd><tt>Returns data as string.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="CSVData-add_keys"><strong>add_keys</strong></a>(self, *h)</dt><dd><tt>Adds new keys to the data.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="CSVData-add_rows"><strong>add_rows</strong></a>(self, n<font color="#909090">=1</font>)</dt><dd><tt>Adds some empty rows to the data.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="CSVData-col_by_key"><strong>col_by_key</strong></a>(self, key)</dt><dd><tt>Returns a column by header's name</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="CSVData-has_key"><strong>has_key</strong></a>(self, key)</dt><dd><tt>Returns True if given key exists in data</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="CSVData-row_by_idx"><strong>row_by_idx</strong></a>(self, idx)</dt><dd><tt>Returns a row by index.</tt></dd></dl>
|
|
||||||
|
|
||||||
</td></tr></table> <p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#ffc8d8">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#000000" face="helvetica, arial"><a name="Data">class <strong>Data</strong></a></font></td></tr>
|
|
||||||
|
|
||||||
<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td>
|
|
||||||
<td colspan=2><tt>Empty data class. Can be used for a subclassing or procedural data creation.<br> </tt></td></tr>
|
|
||||||
<tr><td> </td>
|
|
||||||
<td width="100%">Methods defined here:<br>
|
|
||||||
<dl><dt><a name="Data-__getitem__"><strong>__getitem__</strong></a>(self, pair)</dt><dd><tt>Returns a value for given key and row.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="Data-__init__"><strong>__init__</strong></a>(self, *args, **kwargs)</dt><dd><tt>Constructor</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="Data-__repr__"><strong>__repr__</strong></a>(self)</dt></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="Data-__setitem__"><strong>__setitem__</strong></a>(self, pair, value)</dt><dd><tt>Sets a value for given key and row.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="Data-__str__"><strong>__str__</strong></a>(self)</dt><dd><tt>Returns data as string.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="Data-add_keys"><strong>add_keys</strong></a>(self, *h)</dt><dd><tt>Adds new keys to the data.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="Data-add_rows"><strong>add_rows</strong></a>(self, n<font color="#909090">=1</font>)</dt><dd><tt>Adds some empty rows to the data.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="Data-col_by_key"><strong>col_by_key</strong></a>(self, key)</dt><dd><tt>Returns a column by header's name</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="Data-has_key"><strong>has_key</strong></a>(self, key)</dt><dd><tt>Returns True if given key exists in data</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="Data-row_by_idx"><strong>row_by_idx</strong></a>(self, idx)</dt><dd><tt>Returns a row by index.</tt></dd></dl>
|
|
||||||
|
|
||||||
</td></tr></table></td></tr></table>
|
|
||||||
</body></html>
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
||||||
<html><head><title>Python: module template</title>
|
|
||||||
</head><body bgcolor="#f0f0f8">
|
|
||||||
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="heading">
|
|
||||||
<tr bgcolor="#7799ee">
|
|
||||||
<td valign=bottom> <br>
|
|
||||||
<font color="#ffffff" face="helvetica, arial"> <br><big><big><strong>template</strong></big></big></font></td
|
|
||||||
><td align=right valign=bottom
|
|
||||||
><font color="#ffffff" face="helvetica, arial"><a href=".">index</a><br><a href="file:///C:/users/kaisd/documents/workbench/programming/ktt/src/template.py">c:\users\kaisd\documents\workbench\programming\ktt\src\template.py</a></font></td></tr></table>
|
|
||||||
<p><tt><a href="#Template">Template</a> module for a KaiSD Text Tools.<br>
|
|
||||||
<br>
|
|
||||||
(c) 2013 Ivan "Kai SD" Korystin <br>
|
|
||||||
<br>
|
|
||||||
License: GPLv3</tt></p>
|
|
||||||
<p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#aa55cc">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Modules</strong></big></font></td></tr>
|
|
||||||
|
|
||||||
<tr><td bgcolor="#aa55cc"><tt> </tt></td><td> </td>
|
|
||||||
<td width="100%"><table width="100%" summary="list"><tr><td width="25%" valign=top><a href="re.html">re</a><br>
|
|
||||||
</td><td width="25%" valign=top></td><td width="25%" valign=top></td><td width="25%" valign=top></td></tr></table></td></tr></table><p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#ee77aa">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#ffffff" face="helvetica, arial"><big><strong>Classes</strong></big></font></td></tr>
|
|
||||||
|
|
||||||
<tr><td bgcolor="#ee77aa"><tt> </tt></td><td> </td>
|
|
||||||
<td width="100%"><dl>
|
|
||||||
<dt><font face="helvetica, arial"><a href="template.html#Template">Template</a>
|
|
||||||
</font></dt><dd>
|
|
||||||
<dl>
|
|
||||||
<dt><font face="helvetica, arial"><a href="template.html#TemplateV2">TemplateV2</a>
|
|
||||||
</font></dt></dl>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
<p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#ffc8d8">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#000000" face="helvetica, arial"><a name="Template">class <strong>Template</strong></a></font></td></tr>
|
|
||||||
|
|
||||||
<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td>
|
|
||||||
<td colspan=2><tt>Empty template class. Generates empty text.<br> </tt></td></tr>
|
|
||||||
<tr><td> </td>
|
|
||||||
<td width="100%">Methods defined here:<br>
|
|
||||||
<dl><dt><a name="Template-log"><strong>log</strong></a>(self, text)</dt><dd><tt>Print information</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="Template-process"><strong>process</strong></a>(self, data)</dt><dd><tt>Replace this method in subclasses.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="Template-warning"><strong>warning</strong></a>(self, text)</dt><dd><tt>Prints a warning</tt></dd></dl>
|
|
||||||
|
|
||||||
</td></tr></table> <p>
|
|
||||||
<table width="100%" cellspacing=0 cellpadding=2 border=0 summary="section">
|
|
||||||
<tr bgcolor="#ffc8d8">
|
|
||||||
<td colspan=3 valign=bottom> <br>
|
|
||||||
<font color="#000000" face="helvetica, arial"><a name="TemplateV2">class <strong>TemplateV2</strong></a>(<a href="template.html#Template">Template</a>)</font></td></tr>
|
|
||||||
|
|
||||||
<tr bgcolor="#ffc8d8"><td rowspan=2><tt> </tt></td>
|
|
||||||
<td colspan=2><tt>Class for reading ATGv2 templates.<br>
|
|
||||||
<br>
|
|
||||||
ATGv2 template file should be a plain text file, starting with the line<br>
|
|
||||||
ATGV2<br>
|
|
||||||
followed by the info line:<br>
|
|
||||||
[$KeyField$Extension$Prefix$Encoding$]<br>
|
|
||||||
where<br>
|
|
||||||
KeyField - is a name of a data column, that contains an identifier.<br>
|
|
||||||
Extension - is the desired extension for the generated files.<br>
|
|
||||||
Prefix - is the desired filename prefix for the generated files<br>
|
|
||||||
Encoding - is the desired encoding for the generated files.<br>
|
|
||||||
The line may also have some optional keywords before the closing bracket:<br>
|
|
||||||
oneFile$ - place all generated text into a single file instead of<br>
|
|
||||||
generating a file for each table row.<br>
|
|
||||||
After the info line, you can put your text.<br>
|
|
||||||
You can use following commands to handle the data:<br>
|
|
||||||
* [$Name$], where Name is the column header,<br>
|
|
||||||
will be replaced with value from the current row.<br>
|
|
||||||
* [$ATGLINDEX$] will be replaced with the number of a current row.<br>
|
|
||||||
* [$ATGHEADER$Text$] and [$ATGFOOTER$Text$] will place the given text<br>
|
|
||||||
at the begining or at the end of the file. You can't use other<br>
|
|
||||||
commands in this text.<br>
|
|
||||||
* [$ATGLIST$Name$Text$], where Name is a multi-column header<br>
|
|
||||||
(i.e. 'Col' will represent 'Col1', 'Col2', 'Col3' etc)<br>
|
|
||||||
will repeat the given text for each non-empty value.<br>
|
|
||||||
You can use other commands in Text. Also [$Name$] inside the list<br>
|
|
||||||
will be replaced with the value for the current row and column.<br>
|
|
||||||
* [$ATGLINDEX$] can be used only inside the ATGLIST text,<br>
|
|
||||||
will be replaced with the current column index.<br>
|
|
||||||
* [$ATGLISTCUT$Name$Text$] - same as ATGLIST, but the last symbol<br>
|
|
||||||
will be removed. Useful for removing unnecessary newlines.<br>
|
|
||||||
* [$ATGIF$Name$Value$Text$] will be replaced with the given text<br>
|
|
||||||
only if the the given column's value is the same as the given one.<br>
|
|
||||||
Will be replaced with the empty text otherwise. You can use other<br>
|
|
||||||
commands in Text.<br>
|
|
||||||
* [$ATGIFNOT$Name$Value$Text$] - same as ATGIF, but the column's value<br>
|
|
||||||
should not be equal to the given one.<br>
|
|
||||||
* [$ATGGREATER$Name$Value$Text$] - same as ATGIF, but the value should<br>
|
|
||||||
be the number and it should be greater then the given one.<br>
|
|
||||||
* [$ATGGREATER$Name$Value$Text$] - same as ATGGREATER, but the value<br>
|
|
||||||
should be less then the given one.<br>
|
|
||||||
* [$ATGREPLACE$Text1$Text2$] - Will replace Text1 with Text2. Replacements<br>
|
|
||||||
will be done after all other commands. You can't use regular expressions or<br>
|
|
||||||
other commands in the text.<br>
|
|
||||||
* [$ATGPREFIX$Text$] - Will add the given text to the filename prefix.<br>
|
|
||||||
You can use other commands in text, but do it carefully.<br>
|
|
||||||
* [$ATGSKIP$] - Skip the current row. Use only in combination with the<br>
|
|
||||||
ATGIF/ATGIFNOT, or you will generate nothing.<br>
|
|
||||||
* [$ATGPREV$Name$], where Name is the column header,<br>
|
|
||||||
will be replaced with the with the value of the given header from the<br>
|
|
||||||
previous row. ATGSKIP will be used for the first row.<br> </tt></td></tr>
|
|
||||||
<tr><td> </td>
|
|
||||||
<td width="100%">Methods defined here:<br>
|
|
||||||
<dl><dt><a name="TemplateV2-__init__"><strong>__init__</strong></a>(self, filename<font color="#909090">=None</font>, encoding<font color="#909090">='utf-8'</font>, text<font color="#909090">=''</font>)</dt><dd><tt>Constructor.<br>
|
|
||||||
<br>
|
|
||||||
filename - name of the ATGv2 template file.<br>
|
|
||||||
encoding - encoding of the template file.<br>
|
|
||||||
text - text to use if no filename has been provided.</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="TemplateV2-process"><strong>process</strong></a>(self, data)</dt><dd><tt>Generate text for the given data.</tt></dd></dl>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
Static methods defined here:<br>
|
|
||||||
<dl><dt><a name="TemplateV2-express"><strong>express</strong></a>(cls, text, **kwargs)</dt></dl>
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
Methods inherited from <a href="template.html#Template">Template</a>:<br>
|
|
||||||
<dl><dt><a name="TemplateV2-log"><strong>log</strong></a>(self, text)</dt><dd><tt>Print information</tt></dd></dl>
|
|
||||||
|
|
||||||
<dl><dt><a name="TemplateV2-warning"><strong>warning</strong></a>(self, text)</dt><dd><tt>Prints a warning</tt></dd></dl>
|
|
||||||
|
|
||||||
</td></tr></table></td></tr></table>
|
|
||||||
</body></html>
|
|
||||||
@@ -10,10 +10,8 @@ Part of KaiSD Text Tools
|
|||||||
License: GPLv3
|
License: GPLv3
|
||||||
'''
|
'''
|
||||||
from sys import argv
|
from sys import argv
|
||||||
from atg import ATG
|
|
||||||
from data import CSVData
|
|
||||||
from template import TemplateV2
|
|
||||||
from os.path import split
|
from os.path import split
|
||||||
|
from ktt import ATG, CSVData, TemplateV2
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
if len(argv) == 3:
|
if len(argv) == 3:
|
||||||
|
|||||||
482
template.py
482
template.py
@@ -1,482 +0,0 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
'''
|
|
||||||
Template module for a KaiSD Text Tools.
|
|
||||||
|
|
||||||
(c) 2013 Ivan "Kai SD" Korystin
|
|
||||||
|
|
||||||
License: GPLv3
|
|
||||||
'''
|
|
||||||
|
|
||||||
import re
|
|
||||||
class Template:
|
|
||||||
'''
|
|
||||||
Empty template class. Generates empty text.
|
|
||||||
'''
|
|
||||||
def process(self, data):
|
|
||||||
'''
|
|
||||||
Replace this method in subclasses.
|
|
||||||
'''
|
|
||||||
return ''
|
|
||||||
|
|
||||||
def warning(self, text):
|
|
||||||
'''
|
|
||||||
Prints a warning
|
|
||||||
'''
|
|
||||||
print text
|
|
||||||
|
|
||||||
def log(self, text):
|
|
||||||
'''
|
|
||||||
Print information
|
|
||||||
'''
|
|
||||||
pass
|
|
||||||
|
|
||||||
class TemplateV2(Template):
|
|
||||||
'''
|
|
||||||
Class for reading ATGv2 templates.
|
|
||||||
|
|
||||||
ATGv2 template file should be a plain text file, starting with the line
|
|
||||||
ATGV2
|
|
||||||
followed by the info line:
|
|
||||||
[$KeyField$Extension$Prefix$Encoding$]
|
|
||||||
where
|
|
||||||
KeyField - is a name of a data column, that contains an identifier.
|
|
||||||
Extension - is the desired extension for the generated files.
|
|
||||||
Prefix - is the desired filename prefix for the generated files
|
|
||||||
Encoding - is the desired encoding for the generated files.
|
|
||||||
The line may also have some optional keywords before the closing bracket:
|
|
||||||
oneFile$ - place all generated text into a single file instead of
|
|
||||||
generating a file for each table row.
|
|
||||||
After the info line, you can put your text.
|
|
||||||
You can use following commands to handle the data:
|
|
||||||
* [$Name$], where Name is the column header,
|
|
||||||
will be replaced with value from the current row.
|
|
||||||
* [$ATGLINDEX$] will be replaced with the number of a current row.
|
|
||||||
* [$ATGHEADER$Text$] and [$ATGFOOTER$Text$] will place the given text
|
|
||||||
at the begining or at the end of the file. You can't use other
|
|
||||||
commands in this text.
|
|
||||||
* [$ATGLIST$Name$Text$], where Name is a multi-column header
|
|
||||||
(i.e. 'Col' will represent 'Col1', 'Col2', 'Col3' etc)
|
|
||||||
will repeat the given text for each non-empty value.
|
|
||||||
You can use other commands in Text. Also [$Name$] inside the list
|
|
||||||
will be replaced with the value for the current row and column.
|
|
||||||
* [$ATGLINDEX$] can be used only inside the ATGLIST text,
|
|
||||||
will be replaced with the current column index.
|
|
||||||
* [$ATGLISTCUT$Name$Text$] - same as ATGLIST, but the last symbol
|
|
||||||
will be removed. Useful for removing unnecessary newlines.
|
|
||||||
* [$ATGIF$Name$Value$Text$] will be replaced with the given text
|
|
||||||
only if the the given column's value is the same as the given one.
|
|
||||||
Will be replaced with the empty text otherwise. You can use other
|
|
||||||
commands in Text.
|
|
||||||
* [$ATGIFNOT$Name$Value$Text$] - same as ATGIF, but the column's value
|
|
||||||
should not be equal to the given one.
|
|
||||||
* [$ATGGREATER$Name$Value$Text$] - same as ATGIF, but the value should
|
|
||||||
be the number and it should be greater then the given one.
|
|
||||||
* [$ATGGREATER$Name$Value$Text$] - same as ATGGREATER, but the value
|
|
||||||
should be less then the given one.
|
|
||||||
* [$ATGREPLACE$Text1$Text2$] - Will replace Text1 with Text2. Replacements
|
|
||||||
will be done after all other commands. You can't use regular expressions or
|
|
||||||
other commands in the text.
|
|
||||||
* [$ATGPREFIX$Text$] - Will add the given text to the filename prefix.
|
|
||||||
You can use other commands in text, but do it carefully.
|
|
||||||
* [$ATGSKIP$] - Skip the current row. Use only in combination with the
|
|
||||||
ATGIF/ATGIFNOT, or you will generate nothing.
|
|
||||||
* [$ATGPREV$Name$], where Name is the column header,
|
|
||||||
will be replaced with the with the value of the given header from the
|
|
||||||
previous row. ATGSKIP will be used for the first row.
|
|
||||||
'''
|
|
||||||
|
|
||||||
def __init__(self, filename=None, encoding='utf-8', text=''):
|
|
||||||
'''
|
|
||||||
Constructor.
|
|
||||||
|
|
||||||
filename - name of the ATGv2 template file.
|
|
||||||
encoding - encoding of the template file.
|
|
||||||
text - text to use if no filename has been provided.
|
|
||||||
'''
|
|
||||||
if filename:
|
|
||||||
with open(filename, 'r') as templateFile:
|
|
||||||
topline = templateFile.readline().decode(encoding)
|
|
||||||
if not topline.startswith('ATGV2'):
|
|
||||||
raise BaseException('%s is not an ATGv2 template' % (filename))
|
|
||||||
|
|
||||||
key = templateFile.readline().decode(encoding)
|
|
||||||
if key[:2] == '[$' and key[-3:-1] == '$]':
|
|
||||||
keyInfo = key[2:-2].split('$')
|
|
||||||
if len(keyInfo) < 4:
|
|
||||||
raise BaseException('%s has bad ATGv2 key' % (filename))
|
|
||||||
self.keyField = keyInfo[0]
|
|
||||||
self.extension = keyInfo[1]
|
|
||||||
self.prefix = keyInfo[2]
|
|
||||||
self.encoding = keyInfo[3]
|
|
||||||
if 'oneFile' in keyInfo[4:]:
|
|
||||||
self.oneFile = True
|
|
||||||
else:
|
|
||||||
self.oneFile = False
|
|
||||||
self.text = u''
|
|
||||||
else:
|
|
||||||
raise BaseException('%s has bad ATGv2 key' % (filename))
|
|
||||||
|
|
||||||
for i in templateFile.readlines():
|
|
||||||
self.text += i.decode(encoding)
|
|
||||||
else:
|
|
||||||
self.text = text
|
|
||||||
|
|
||||||
self.header = u''
|
|
||||||
self.footer = u''
|
|
||||||
self.replacement = {}
|
|
||||||
self._data = None
|
|
||||||
self._multiWords = None
|
|
||||||
|
|
||||||
def parse(text):
|
|
||||||
topParts = []
|
|
||||||
matches = {}
|
|
||||||
|
|
||||||
openers = re.finditer('\[\$.*?\$', text)
|
|
||||||
closers = re.finditer('\$\]', text)
|
|
||||||
ops = []
|
|
||||||
try:
|
|
||||||
cl = closers.next()
|
|
||||||
while not cl is None:
|
|
||||||
try:
|
|
||||||
op = openers.next()
|
|
||||||
if op.start() < cl.start():
|
|
||||||
ops.append(op)
|
|
||||||
else:
|
|
||||||
idx = -1
|
|
||||||
try:
|
|
||||||
while ops[idx].start() > cl.start():
|
|
||||||
idx -= 1
|
|
||||||
except:
|
|
||||||
raise BaseException('Template parsing error: can not find the opener for '+str(cl.start()))
|
|
||||||
matches[ops[idx]] = cl
|
|
||||||
if len(ops) == 1 or idx == -len(ops):
|
|
||||||
topParts.append(ops[idx])
|
|
||||||
del ops[idx]
|
|
||||||
ops.append(op)
|
|
||||||
try:
|
|
||||||
cl = closers.next()
|
|
||||||
except StopIteration:
|
|
||||||
cl = None
|
|
||||||
except StopIteration:
|
|
||||||
idx = -1
|
|
||||||
try:
|
|
||||||
while ops[idx].start() > cl.start():
|
|
||||||
idx -= 1
|
|
||||||
except:
|
|
||||||
raise BaseException('Template parsing error: can not find the opener for '+str(cl.start()))
|
|
||||||
matches[ops[idx]] = cl
|
|
||||||
if len(ops) == 1 or idx == -len(ops):
|
|
||||||
topParts.append(ops[idx])
|
|
||||||
del ops[idx]
|
|
||||||
try:
|
|
||||||
cl = closers.next()
|
|
||||||
except StopIteration:
|
|
||||||
cl = None
|
|
||||||
except StopIteration:
|
|
||||||
pass
|
|
||||||
parts = []
|
|
||||||
for i in topParts:
|
|
||||||
startPoint = i.end()
|
|
||||||
endPoint = matches[i].start()
|
|
||||||
p = (i.group()[2:-1], text[startPoint:endPoint])
|
|
||||||
if p[0].startswith('ATG'):
|
|
||||||
parts.insert(0, p)
|
|
||||||
else:
|
|
||||||
parts.append(p)
|
|
||||||
return parts
|
|
||||||
|
|
||||||
partCommands = {}
|
|
||||||
|
|
||||||
def plain(index, flow, keytag):
|
|
||||||
if not keytag in self._data.keys:
|
|
||||||
self.warning('WARNING: keyword not found in table - %s' % (keytag))
|
|
||||||
return flow
|
|
||||||
return flow.replace('[$%s$]' % (keytag), unicode(self._data[keytag, index]))
|
|
||||||
partCommands['_ATGPLAIN'] = plain
|
|
||||||
|
|
||||||
def nPlain(index, flow, keytag, number):
|
|
||||||
if not keytag+str(number) in self._data.keys:
|
|
||||||
self.warning('WARNING: keyword not found in table - %s' % (keytag+str(number)))
|
|
||||||
return flow
|
|
||||||
return flow.replace('[$%s$]' % (keytag), unicode(self._data[keytag+str(number), index]))
|
|
||||||
|
|
||||||
def lIndex(index, flow, keytag, number):
|
|
||||||
return flow.replace('[$ATGLINDEX$]', str(number))
|
|
||||||
|
|
||||||
def addHeader(index, flow, text):
|
|
||||||
if self.header.find(text) < 0:
|
|
||||||
self.header += text
|
|
||||||
key = '[$ATGHEADER$' + text + '$]'
|
|
||||||
return flow.replace(key,'')
|
|
||||||
partCommands['ATGHEADER'] = addHeader
|
|
||||||
|
|
||||||
def addFooter(index, flow, text):
|
|
||||||
if self.footer.find(text) < 0:
|
|
||||||
self.footer += text
|
|
||||||
key = '[$ATGFOOTER$' + text + '$]'
|
|
||||||
return flow.replace(key,'')
|
|
||||||
partCommands['ATGFOOTER'] = addFooter
|
|
||||||
|
|
||||||
def addList(index, flow, string):
|
|
||||||
key = '[$ATGLIST$%s$%s$]' % (string.split('$')[0], string[len(string.split('$')[0])+1:])
|
|
||||||
sub = string[len(string.split('$')[0])+1:]
|
|
||||||
keyTag = string.split('$')[0]
|
|
||||||
subparts = parse(sub)
|
|
||||||
myText = u''
|
|
||||||
if not keyTag in self._multiWords:
|
|
||||||
self.warning('Keytag %s is not multiple!' % (keyTag))
|
|
||||||
return flow
|
|
||||||
for j in xrange(1, self._multiWords[keyTag]+1):
|
|
||||||
subText = sub
|
|
||||||
for sp in subparts:
|
|
||||||
if sp[0] in self._multiWords:
|
|
||||||
subText = nPlain(index, subText, sp[0], j)
|
|
||||||
elif sp[0] == 'ATGLINDEX':
|
|
||||||
subText = lIndex(index, subText, sp[0], j)
|
|
||||||
elif sp[0] in partCommands:
|
|
||||||
subText = partCommands[sp[0]](index, subText, sp[1])
|
|
||||||
elif sp[1] == '':
|
|
||||||
subText = plain(index, subText, sp[0])
|
|
||||||
else:
|
|
||||||
self.warning('Warning: unknown command '+sp[0])
|
|
||||||
if not self._data[keyTag+str(j), index] == u'':
|
|
||||||
myText += subText
|
|
||||||
return flow.replace(key, myText)
|
|
||||||
partCommands['ATGLIST'] = addList
|
|
||||||
|
|
||||||
def addListCut(index, flow, string):
|
|
||||||
key = '[$ATGLISTCUT$%s$%s$]' % (string.split('$')[0], string[len(string.split('$')[0])+1:])
|
|
||||||
sub = string[len(string.split('$')[0])+1:]
|
|
||||||
keyTag = string.split('$')[0]
|
|
||||||
subparts = parse(sub)
|
|
||||||
myText = u''
|
|
||||||
if not keyTag in self._multiWords:
|
|
||||||
self.warning('Keytag %s is not multiple!' % (keyTag))
|
|
||||||
return flow
|
|
||||||
for j in xrange(1, self._multiWords[keyTag]+1):
|
|
||||||
subText = sub
|
|
||||||
for sp in subparts:
|
|
||||||
if sp[0] in self._multiWords:
|
|
||||||
subText = nPlain(index, subText, sp[0], j)
|
|
||||||
elif sp[0] == 'ATGLINDEX':
|
|
||||||
subText = lIndex(index, subText, sp[0], j)
|
|
||||||
elif sp[0] in partCommands:
|
|
||||||
subText = partCommands[sp[0]](index, subText, sp[1])
|
|
||||||
elif sp[1] == '':
|
|
||||||
subText = plain(index, subText, sp[0])
|
|
||||||
else:
|
|
||||||
self.warning('Warning: unknown command '+sp[0])
|
|
||||||
if not self._data[keyTag+str(j), index] == u'':
|
|
||||||
myText += subText
|
|
||||||
return flow.replace(key, myText[:-1])
|
|
||||||
partCommands['ATGLISTCUT'] = addListCut
|
|
||||||
|
|
||||||
def addIf(index, flow, string):
|
|
||||||
key = '[$ATGIF$%s$%s$]' % (string.split('$')[0], string[len(string.split('$')[0])+1:])
|
|
||||||
sub = string[len(string.split('$')[0])+len(string.split('$')[1])+2:]
|
|
||||||
keyTag = string.split('$')[0]
|
|
||||||
targetValue = string.split('$')[1]
|
|
||||||
subparts = parse(sub)
|
|
||||||
myText = u''
|
|
||||||
if self._data[keyTag, 0] == []:
|
|
||||||
self.warning('WARNING: keyword not found in table - %s' % (keyTag))
|
|
||||||
return flow
|
|
||||||
if unicode(self._data[keyTag, index]) == unicode(targetValue):
|
|
||||||
subText = sub
|
|
||||||
for sp in subparts:
|
|
||||||
if sp[0] in partCommands:
|
|
||||||
subText = partCommands[sp[0]](index, subText, sp[1])
|
|
||||||
elif sp[1] == '':
|
|
||||||
subText = plain(index, subText, sp[0])
|
|
||||||
else:
|
|
||||||
self.warning('Warning: unknown command '+sp[0])
|
|
||||||
myText += subText
|
|
||||||
return flow.replace(key, myText)
|
|
||||||
partCommands['ATGIF'] = addIf
|
|
||||||
|
|
||||||
def addIfNot(index, flow, string):
|
|
||||||
key = '[$ATGIFNOT$%s$%s$]' % (string.split('$')[0], string[len(string.split('$')[0])+1:])
|
|
||||||
sub = string[len(string.split('$')[0])+len(string.split('$')[1])+2:]
|
|
||||||
keyTag = string.split('$')[0]
|
|
||||||
targetValue = string.split('$')[1]
|
|
||||||
subparts = parse(sub)
|
|
||||||
myText = u''
|
|
||||||
if self._data[keyTag, 0] == []:
|
|
||||||
self.warning('WARNING: keyword not found in table - %s' % (keyTag))
|
|
||||||
return flow
|
|
||||||
if not unicode(self._data[keyTag, index]) == unicode(targetValue):
|
|
||||||
subText = sub
|
|
||||||
for sp in subparts:
|
|
||||||
if sp[0] in partCommands:
|
|
||||||
subText = partCommands[sp[0]](index, subText, sp[1])
|
|
||||||
elif sp[1] == '':
|
|
||||||
subText = plain(index, subText, sp[0])
|
|
||||||
else:
|
|
||||||
self.warning('Warning: unknown command '+sp[0])
|
|
||||||
myText += subText
|
|
||||||
return flow.replace(key, myText)
|
|
||||||
partCommands['ATGIFNOT'] = addIfNot
|
|
||||||
|
|
||||||
def addGreater(index, flow, string):
|
|
||||||
key = '[$ATGGREATER$%s$%s$]' % (string.split('$')[0], string[len(string.split('$')[0])+1:])
|
|
||||||
sub = string[len(string.split('$')[0])+len(string.split('$')[1])+2:]
|
|
||||||
keyTag = string.split('$')[0]
|
|
||||||
targetValue = string.split('$')[1]
|
|
||||||
subparts = parse(sub)
|
|
||||||
myText = u''
|
|
||||||
if self._data[keyTag, 0] == []:
|
|
||||||
self.warning('WARNING: keyword not found in table - %s' % (keyTag))
|
|
||||||
return flow
|
|
||||||
try:
|
|
||||||
if float(self._data[keyTag, index]) > float(targetValue):
|
|
||||||
subText = sub
|
|
||||||
for sp in subparts:
|
|
||||||
if sp[0] in partCommands:
|
|
||||||
subText = partCommands[sp[0]](index, subText, sp[1])
|
|
||||||
elif sp[1] == '':
|
|
||||||
subText = plain(index, subText, sp[0])
|
|
||||||
else:
|
|
||||||
self.warning('Warning: unknown command '+sp[0])
|
|
||||||
myText += subText
|
|
||||||
except:
|
|
||||||
self.warning('ERROR: trying to compare uncomparable values!')
|
|
||||||
return flow.replace(key, myText)
|
|
||||||
partCommands['ATGGREATER'] = addGreater
|
|
||||||
|
|
||||||
def addLess(index, flow, string):
|
|
||||||
key = '[$ATGLESS$%s$%s$]' % (string.split('$')[0], string[len(string.split('$')[0])+1:])
|
|
||||||
sub = string[len(string.split('$')[0])+len(string.split('$')[1])+2:]
|
|
||||||
keyTag = string.split('$')[0]
|
|
||||||
targetValue = string.split('$')[1]
|
|
||||||
subparts = parse(sub)
|
|
||||||
myText = u''
|
|
||||||
if self._data[keyTag, 0] == []:
|
|
||||||
self.warning('WARNING: keyword not found in table - %s' % (keyTag))
|
|
||||||
return flow
|
|
||||||
try:
|
|
||||||
if float(self._data[keyTag, index]) < float(targetValue):
|
|
||||||
subText = sub
|
|
||||||
for sp in subparts:
|
|
||||||
if sp[0] in partCommands:
|
|
||||||
subText = partCommands[sp[0]](index, subText, sp[1])
|
|
||||||
elif sp[1] == '':
|
|
||||||
subText = plain(index, subText, sp[0])
|
|
||||||
else:
|
|
||||||
self.warning('Warning: unknown command '+sp[0])
|
|
||||||
myText += subText
|
|
||||||
except:
|
|
||||||
self.warning('ERROR: trying to compare uncomparable values!')
|
|
||||||
return flow.replace(key, myText)
|
|
||||||
partCommands['ATGLESS'] = addLess
|
|
||||||
|
|
||||||
def addReplace(index, flow, string):
|
|
||||||
key = '[$ATGREPLACE$%s$%s$]' % (string.split('$')[0], string[len(string.split('$')[0])+1:])
|
|
||||||
targetString = string[len(string.split('$')[0])+1:]
|
|
||||||
srcString = string.split('$')[0]
|
|
||||||
self.replacement[srcString] = targetString
|
|
||||||
key = '[$ATGREPLACE$' + string + '$]'
|
|
||||||
return flow.replace(key,'')
|
|
||||||
partCommands['ATGREPLACE'] = addReplace
|
|
||||||
|
|
||||||
def addPrefix(index, flow, string):
|
|
||||||
key = '[$ATGPREFIX$%s$%s$]' % (string.split('$')[0], string[len(string.split('$')[0])+1:])
|
|
||||||
sub = string
|
|
||||||
subparts = parse(sub)
|
|
||||||
for sp in subparts:
|
|
||||||
if sp[0] in partCommands:
|
|
||||||
sub = partCommands[sp[0]](index, sub, sp[1])
|
|
||||||
elif sp[1] == '':
|
|
||||||
sub = plain(index, sub, sp[0])
|
|
||||||
else:
|
|
||||||
self.warning('Warning: unknown command '+sp[0])
|
|
||||||
self.bonusPrefix += sub
|
|
||||||
key = '[$ATGPREFIX$' + string + '$]'
|
|
||||||
return flow.replace(key,'')
|
|
||||||
partCommands['ATGPREFIX'] = addPrefix
|
|
||||||
|
|
||||||
def skip(index, flow, string):
|
|
||||||
return u'[$ATGSKIP_DO$]'
|
|
||||||
partCommands['ATGSKIP'] = skip
|
|
||||||
|
|
||||||
def prev(index, flow, string):
|
|
||||||
key = '[$ATGPREV$%s$]' % (string.split('$')[0])
|
|
||||||
keytag = string.split('$')[0]
|
|
||||||
if self._data[keytag, 0] == []:
|
|
||||||
self.warning('WARNING: keyword not found in table - %s' % (keytag))
|
|
||||||
return flow
|
|
||||||
if index == 0:
|
|
||||||
self.log('INFORMATION: Skipping ATGPREV tag for entry with index = 0')
|
|
||||||
return u'[$ATGSKIP_DO$]'
|
|
||||||
return flow.replace('[$ATGPREV$%s$]' % (keytag), unicode(self._data.col_by_key(keytag)[index-1]))
|
|
||||||
partCommands['ATGPREV'] = prev
|
|
||||||
|
|
||||||
self.commands = partCommands
|
|
||||||
self.parts = parse(self.text)
|
|
||||||
|
|
||||||
def process(self, data):
|
|
||||||
'''
|
|
||||||
Generate text for the given data.
|
|
||||||
'''
|
|
||||||
self._data = data
|
|
||||||
|
|
||||||
multiWords = {}
|
|
||||||
numbs = ('1','2','3','4','5','6','7','8','9','0')
|
|
||||||
|
|
||||||
for i in data.keys:
|
|
||||||
multi = False
|
|
||||||
while i[-1] in numbs:
|
|
||||||
i = i[:-1]
|
|
||||||
multi = True
|
|
||||||
if multi:
|
|
||||||
if i in multiWords:
|
|
||||||
multiWords[i] += 1
|
|
||||||
else:
|
|
||||||
multiWords[i] = 1
|
|
||||||
self._multiWords = multiWords
|
|
||||||
|
|
||||||
if self.oneFile:
|
|
||||||
out = ''
|
|
||||||
else:
|
|
||||||
out = {}
|
|
||||||
index = 0
|
|
||||||
partCommands = self.commands
|
|
||||||
for element in data.col_by_key(self.keyField):
|
|
||||||
self.bonusPrefix = self.prefix
|
|
||||||
text = self.text
|
|
||||||
for i in self.parts:
|
|
||||||
if i[0] in partCommands:
|
|
||||||
text = partCommands[i[0]](index, text, i[1])
|
|
||||||
elif i[1] == u'':
|
|
||||||
text = partCommands['_ATGPLAIN'](index, text, i[0])
|
|
||||||
else:
|
|
||||||
self.warning('Warning: unknown command '+i[0])
|
|
||||||
for i in self.replacement:
|
|
||||||
text = text.replace(i, self.replacement[i])
|
|
||||||
self.replacement = {}
|
|
||||||
index += 1
|
|
||||||
|
|
||||||
if u'[$ATGSKIP_DO$]' in text:
|
|
||||||
self.log('ATGSKIP Tag found. Skipping ' + unicode(element) + '.')
|
|
||||||
else:
|
|
||||||
if self.oneFile:
|
|
||||||
out += text
|
|
||||||
else:
|
|
||||||
name = self.bonusPrefix + unicode(element)
|
|
||||||
out[name] = self.header + text + self.footer
|
|
||||||
self.log('Created %s' % (element))
|
|
||||||
|
|
||||||
if self.oneFile:
|
|
||||||
out = self.header + out + self.footer
|
|
||||||
|
|
||||||
return out
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def express(cls, text, **kwargs):
|
|
||||||
obj = cls()
|
|
||||||
obj.text = text
|
|
||||||
obj.keyField = kwargs.get('keyField', 'Index')
|
|
||||||
obj.extension = kwargs.get('extension', '')
|
|
||||||
obj.prefix = kwargs.get('prefix', '')
|
|
||||||
obj.encoding = kwargs.get('encoding', 'utf-8')
|
|
||||||
return obj
|
|
||||||
Reference in New Issue
Block a user