pdfformfill/fillform.py
2018-05-30 16:42:18 +02:00

266 lines
9.3 KiB
Python
Executable file

#! /usr/bin/python3
# -*- coding: UTF-8 -*-
"""Fill a pdf form.
Input are:
the pdf
the config file for this pdf
the data (= config-file) for other pdfs that have useful information
user input
Outputs are:
a filled pdf
the config file including the created data for use in other forms.
TODOs are:
fix in Beginn Dienstgeschäft Datum
es gibt Fälle ohne Erstattung -> Dann z.B. Teil 15. leer lassen
bei choice: zusätzlich Zahlen als Eingabeoption
-> Ja ist immer 1, nein immer 2
statt "nicht möglich" Optionen nicht anzeigen
bei Backspace: vorige Frage nochmal
Option für alles nochmal fragen
Reiseziel -> Leerzeichen entfernen in output file names
Antrag:
18: Abschlag bisher nichts eintragen
Texte verkürzen (z.B. bei Meilengutschrifte) -> nach Möglichkeit
max. 50 Zeichen (besser 45) pro Zeile
erste Abrechnung im Juni
readline benutzen
(im Antrag) überall Zukunft statt Vergangenheit, da es ja ein Antrag ist
"""
import os.path
import argparse
import itertools
from subprocess import CalledProcessError as ExternalError
import commands
import formfield
import readformdata
from constants import (NON_FORMFIELD, CONFIGFIELD, OUTPUTFIELD,
FORMOUTPUTFIELD, SUBINFO_SEP, ConfigError,
GENERALADVICE, EPILOG)
import writetopdf
def parse():
"""Create the argument parser (including the help) and parse the arguments.
Options/ Arguments:
pdffile
config
other data ("data")
outputpdf
fdf tmp file ("fdf")
"""
parser = argparse.ArgumentParser(
description="Fill out a pdf.",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog=EPILOG)
parser.add_argument("pdffile",
help="The pdf file with the form.")
parser.add_argument("--outputpdf", default=None, # nargs=1,
# None = file.pdf -> file-filled.pdf
# todo: more sophisticated with name of person/ date
help="The output file with the filled form. " +
"(Default: pdffile[without .pdf]-filled.pdf or" +
" as configured in the config file.)")
parser.add_argument("--config", default=None, # nargs=1,
# None = file.pdf -> file.form
help="The config file used to know how to fill the " +
"pdf. (Default: pdffile[without .pdf].form")
parser.add_argument("--form", default=None, # nargs=1,
# None = file.pdf -> file-filled.form
help="The file where all the inputed data is saved" +
" to. (Default: pdffile[without .pdf]-filled.form" +
" or as configured in the config file.)" +
"\nThis can be used in other forms and to finish" +
" the input later.")
parser.add_argument("--data", default=None, nargs=2, action='append',
metavar=("REF", "DATAFILE"),
help="Data from other forms can be supplied via" +
" this option. " +
"REF is the reference used in the config" +
" file, the DATAFILE is the .form-file.")
parser.add_argument("--fdf", default="tmp.fdf", # nargs=1,
help="To fill the pdf a temporary fdf is created. " +
"Specify with FDF the file name for this. " +
"(Default: tmp.fdf).")
args = vars(parser.parse_args())
if not os.path.isfile(args["pdffile"]):
parser.print_help()
raise argparse.ArgumentTypeError(args["pdffile"] +
" is not a valid file.")
if args["config"] is None:
args["config"] = os.path.splitext(args["pdffile"])[0] + ".form"
if not os.path.isfile(args["config"]):
parser.print_help()
raise argparse.ArgumentTypeError(args["config"] +
" is not a valid file.")
# if args["form"] is None:
# args["form"] = os.path.splitext(args["pdffile"])[0] + "-filled.form"
# if args["outputpdf"] is None:
# args["outputpdf"] =
# os.path.splitext(args["pdffile"])[0] + "-filled.pdf"
for arg in [args["outputpdf"], args["form"]]:
if arg is not None and os.path.isfile(arg):
try:
input(arg + " is an existing file. For aborting " +
"hit Ctrl+C. For overwriting hit Enter.")
except KeyboardInterrupt:
parser.print_help()
raise argparse.ArgumentTypeError(arg + " is already an " +
"existing file.")
if args["data"] is None:
args["data"] = []
for datafile in args["data"]:
if not os.path.isfile(datafile[1]):
parser.print_help()
raise argparse.ArgumentTypeError(datafile[1] +
" is not a valid file.")
return args
def readDataPaths(configs):
"""Read which data files should be read.
Configs is a list of FormFields.
In the configs there can be a field with the FieldName "Mainconfig".
This can specify with the info Data and the data specifier-Path.
Returns:
a dict with {dataspecifier: path}, to be merged with parsing result.
"""
try:
mainconfig = formfield.FormField.findByFieldName(
configs,
CONFIGFIELD,
include=lambda f: NON_FORMFIELD in f)
except KeyError:
# nothing found, OK
return {}
else:
try:
specs = mainconfig["Data"] # specifier
except KeyError:
# nothing found, OK
return {}
else:
try:
return {spec: mainconfig[
spec + SUBINFO_SEP +
"Path"] for spec in specs}
except KeyError as e:
raise readformdata.ConfigError(
"A data file was specified but a path is missing: " +
str(e))
def getConfigvalue(fieldname, fields, argument, default):
"""Get the name of a config.
If specified on the commandline (argument is not None)
take this.
If a NON-field with the FieldName fieldname exists in fields,
take the value of this field.
If such a field does not exist or it has no value, take default.
"""
if argument is not None:
return argument
try:
field = formfield.FormField.findByFieldName(
fields, fieldname, include=lambda f: NON_FORMFIELD in f)
except KeyError:
return default
else:
try:
return field["Value"]
except KeyError:
return default
def getOutput(fields, args):
"""Generate the name of the output pdf.
If specified on the commandline (arguments["Output"] is not None, take
this.)
If a field with the name Outputconfig exists, take its value.
Otherwise generate outputfilename out of input file.
"""
default = os.path.splitext(args["pdffile"])[0] + "-filled.pdf"
return getConfigvalue(OUTPUTFIELD, fields, args["outputpdf"], default)
def getFormoutput(fields, args):
"""Generate the name of the output form file.
If specified on the commandline (arguments["form"] is not None, take
this.)
If a field with the name Formoutputconfig exists, take its value.
Otherwise generate output form filename out of input file.
"""
default = os.path.splitext(args["pdffile"])[0] + "-filled.form"
return getConfigvalue(FORMOUTPUTFIELD, fields, args["form"], default)
if __name__ == "__main__":
try:
arguments = parse()
except argparse.ArgumentTypeError as e:
print("Error while parsing the command line arguments:\n", e)
else:
config = readformdata.listFields([], arguments["config"])
# make dict out of [[]] list + dict from config file
data = readDataPaths(config)
for f in arguments["data"]:
data[f[0]] = f[1]
try:
data = {f: readformdata.listFields([], data[f])
for f in data}
except FileNotFoundError as e:
raise ConfigError("A data file specified in the config file or " +
"on the command line does not exist: " + str(e))
# f[0] is REF, f[1] is the file
comms = itertools.chain(*[commands.Command.extractCommands(
field, config, data) for field in config])
# comms = commands.Command.extractCommands(config[0], config, data)
# print(comms)
print(GENERALADVICE)
for command in sorted(comms):
try:
command()
except KeyboardInterrupt:
break
else:
# print(config[0])
pass
# in else: fillpdf since we do not need a pdf that is not done yet
# for testing always create
try:
writetopdf.fillpdf(config, arguments["pdffile"],
getOutput(config, arguments),
arguments["fdf"])
except ExternalError as e:
print(e)
if e.output is not None:
print(e.output)
readformdata.writeFields(config, getFormoutput(config, arguments))