|
Code:
- &
-
- Written by John Yeung. Last modified 2012-05-21.
-
- Usage (from CL):
- python233/python &
-
- (The above assumes this program is located in &
- Python 2.3.3 is installed. If you are at V5R3 or later, you should be using
- iSeries Python 2.7 instead.)
-
- Some features/caveats:
-
- - Column headings come from the COLHDG values in the DDS. Multiple
- values for a single field are joined by spaces, not newlines. For
- any fields without a COLHDG, or with only blanks in the COLHDG (these
- two situations are indistinguishable), the field name is used as the
- heading (the TEXT keyword is not checked). To specify a blank column
- heading rather than the field name, use COLHDG(&
- - Column headings wrap and are displayed in bold.
- - Each column is sized approximately according to its longest data,
- assuming that the default font is Arial 10, unless a width is
- specified in the field text (using &
- purpose, the length of numeric data is assumed to always include
- commas and fixed decimal places.]
- - Each column may be formatted using an Excel format string in the
- field text (using &
- - Character fields with no format string are set to Excel text format.
- - Columns with a supported EDTCDE value but no format string are
- formatted according to the edit code.
- - Columns may specify &
- a cell empty when its value is zero. (This is different than using
- a format string or edit code to hide zero values. See the ISBLANK
- and ISNUMBER functions in Excel.)
- - Columns may be skipped entirely by specifying COLHDG(&
- - Numeric fields that are 8 digits long with no decimal places are
- automatically converted to dates if they have a suitable edit word.
- - Numeric fields that are 6 digits long with no decimal places are
- automatically converted to times if they have a suitable edit word.
- - Free-form data may be inserted at the top using additional parameters,
- one parameter for each row. The data will be in bold. Any number of
- parameters may be specified, up to the limits of the operating system.
-
- The motivation for this program is to provide a tool for easy generation
- of formatted spreadsheets.
-
- Nice-to-have features not yet implemented include general edit word
- support (not just for date detection), more available edit codes, more
- comprehensive date support, the ability to choose fonts, and automatic
- population of multiple sheets given multiple file members.
-
- Also, it would be nice to wrap this in a command for even greater ease of
- use, including meaningful promptability.
- &
-
- import sys
- import re
- from os import system
- from datetime import date, time
-
- import xlwt
-
- charwidths = {
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
- &
-
- ezxf = xlwt.easyxf
-
- def sndmsg(msg):
- print msg
-
- def _integer_digits(n):
- &
- if n == 0:
- return 1
- digits = 0
- while n:
- digits += 1
- n //= 10
- return digits
-
- def number_analysis(n, dp=0):
- &
- digits, thousands, points, signs = 0, 0, 0, 0
- if n < 0:
- signs = 1
- n = -n
- if dp > 0:
- points = 1
- if isinstance(n, float):
- idigits = _integer_digits(int(n) + 1)
- elif isinstance(n, (int, long)):
- idigits = _integer_digits(n)
- else:
- return None
- digits = idigits + dp
- thousands = (idigits - 1) // 3
- return digits, thousands, points, signs
-
- def colwidth(n):
- &
- if n <= 0:
- return 0
- if n <= 1:
- return n * 456
- return 200 + n * 256
-
- def fitwidth(data, bold=False):
- &
- units = 220
- for char in str(data):
- if char in charwidths:
- units += charwidths[char]
- else:
- units += charwidths[&
- if bold:
- units *= 1.1
- return max(units, 700)
-
- def numwidth(data, dp, use_commas=False):
- &
- units = 220
- digits, commas, points, signs = number_analysis(data, dp)
- units += digits * charwidths[&
- if use_commas:
- units += commas * charwidths[&
- units += points * charwidths[&
- units += signs * charwidths[&
- return max(units, 700)
-
- def datewidth():
- return 220 + 8 * charwidths[&
-
- def timewidth():
- digits_width = 6 * charwidths[&
- separators_width = 2 * charwidths[&
- space_width = charwidths[&
- am_pm_width = max(charwidths[&
- return 220 + digits_width + separators_width + space_width + am_pm_width
-
- def default_numformat(dp=0, use_commas=False):
- &
- integers, decimals = &
- if use_commas:
- integers = &
- if dp > 0:
- decimals = &
- combined = integers + decimals
- return ezxf(num_format_str=combined)
-
- def editcode(code, dp=0):
- &
- code = code.lower()
- if len(code) != 1 or code not in (&
- return default_numformat(dp)
- sign, integers, decimals, zero = &
- if code in &
- sign = &
- if code in &
- integers = &
- if dp > 0:
- decimals = &
- positive = integers + decimals
- negative = sign + positive
- if code in &
- zero = positive[:-1] + &
- return ezxf(num_format_str=&
-
- def is_numeric_date(size, editword):
- return size == (8, 0) and editword in ("&
-
- def is_numeric_time(size, editword):
- return size == (6, 0) and editword in ("&
-
- parameters = len(sys.argv) - 1
- if parameters < 2:
- sndmsg(&
- sys.exit(2)
- pf = sys.argv[1].split(&
- if len(pf) == 1:
- libname = &
- filename = pf[0].upper()
- elif len(pf) == 2:
- libname = pf[0].upper()
- filename = pf[1].upper()
- else:
- sndmsg(&
- sys.exit(2)
- sndmsg(&
-
- infile = File400(filename, &
- if libname.startswith(&
- libname = infile.libName()
- sndmsg(&
-
- fieldlist = []
- headings = {}
- numformats = {}
- dateflags = {}
- timeflags = {}
- commaflags = {}
- decplaces = {}
- colwidths = {}
- blankzeros = {}
- template = "dspffd %s/%s output(*outfile) outfile(qtemp/dspffdpf)"
- system(template % (libname, filename))
- ddsfile = File400(&
-
- ddsfile.posf()
- while not ddsfile.readn():
- fieldname = ddsfile[&
- fieldtext = ddsfile[&
-
-
- headertuple = (ddsfile[&
- text = &
- if not text:
- text = fieldname
- elif text.upper() in (&
- text = &
- elif text.upper() == &
- continue
- fieldlist.append(fieldname)
- headings[fieldname] = text
-
-
- if ddsfile[&
- fieldsize = (ddsfile[&
- decplaces[fieldname] = fieldsize[1]
- numeric = True
- else:
- fieldsize = ddsfile[&
- numeric = False
-
-
- match = re.search(r&
- if match:
- numformat = ezxf(num_format_str=match.group(1))
- elif numeric:
- numformat = editcode(ddsfile[&
- else:
- numformat = None
- if numformat:
- numformats[fieldname] = numformat
- commaflags[fieldname] = &
-
-
- dateflags[fieldname] = is_numeric_date(fieldsize, ddsfile[&
- timeflags[fieldname] = is_numeric_time(fieldsize, ddsfile[&
-
-
- match = re.search(r&
- if match:
- colwidths[fieldname] = colwidth(int(match.group(1)))
-
-
- match = re.search(r&
- if match:
- blankzeros[fieldname] = True
-
- ddsfile.close()
-
- wb = xlwt.Workbook()
- ws = wb.add_sheet(infile.fileName())
- row = 0
-
- title_style = ezxf(&
- header_style = ezxf(&
- date_style = ezxf(num_format_str=&
- time_style = ezxf(num_format_str=&
- text_style = ezxf(num_format_str=&
-
- for arg in sys.argv[3:]:
- ws.write(row, 0, arg, title_style)
- row += 1
-
- maxwidths = [0] * len(fieldlist)
-
- if parameters > 2:
- row += 1
-
- for col, name in enumerate(fieldlist):
- desc = headings[name]
- ws.write(row, col, desc, header_style)
- if name not in colwidths:
- maxwidths[col] = fitwidth(desc, bold=True)
-
- infile.posf()
- while not infile.readn():
- row += 1
- for col, data in enumerate(infile.get(fieldlist)):
- fieldname = fieldlist[col]
- nativedate = False
- nativetime = False
- if infile.fieldType(fieldname) == &
- year, month, day = [int(x) for x in data.split(&
- if year > 1904:
- ws.write(row, col, date(year, month, day), date_style)
- nativedate = True
- elif infile.fieldType(fieldname) == &
- hour, minute, second = [int(x) for x in data.split(&
- ws.write(row, col, time(hour, minute, second), time_style)
- nativetime = True
- elif dateflags[fieldname]:
- if data:
- year, md = divmod(data, 10000)
- month, day = divmod(md, 100)
- ws.write(row, col, date(year, month, day), date_style)
- elif timeflags[fieldname]:
- if data:
- hour, minsec = divmod(data, 10000)
- minute, second = divmod(minsec, 100)
- ws.write(row, col, time(hour, minute, second), time_style)
- elif data == 0 and fieldname in blankzeros:
- pass
- elif fieldname in numformats:
- ws.write(row, col, data, numformats[fieldname])
- elif infile.fieldType(fieldname) == &
- ws.write(row, col, data, text_style)
- else:
- ws.write(row, col, data)
- if fieldname not in colwidths:
- if nativedate or dateflags[fieldname]:
- maxwidths[col] = datewidth()
- elif nativetime or timeflags[fieldname]:
- maxwidths[col] = timewidth()
- if fieldname in decplaces:
- dp = decplaces[fieldname]
- cf = commaflags[fieldname]
- maxwidths[col] = max(maxwidths[col], numwidth(data, dp, cf))
- else:
- maxwidths[col] = max(maxwidths[col], fitwidth(data))
- infile.close()
-
- for col in range(len(fieldlist)):
- if fieldlist[col] in colwidths:
- ws.col(col).width = colwidths[fieldlist[col]]
- else:
- ws.col(col).width = maxwidths[col]
-
- wb.save(sys.argv[2])
- sndmsg(&
|
|