Tool for creating JSON from configuration file
This commit is contained in:
parent
1d05c2b5d4
commit
cbc0c09d65
|
@ -0,0 +1,172 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
"""
|
||||
Convert config file to JSON
|
||||
|
||||
The JSON contains array of single fields. There is no hierarchy.
|
||||
Fields are being grouped in GUI with "category".
|
||||
A group is shown if a minimum of one field is visible.
|
||||
|
||||
Hints
|
||||
capabilities is a dictionary
|
||||
field:[true | false]
|
||||
optional comma separated multiple values
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import getopt
|
||||
import configparser
|
||||
from io import StringIO
|
||||
from pyparsing import alphas, alphanums, Word, Literal, Combine, Group, Forward, ZeroOrMore, delimitedList
|
||||
|
||||
__author__ = "Thomas Hooge"
|
||||
__copyright__ = "Copyleft 2025, all rights reversed"
|
||||
__version__ = "0.1"
|
||||
__email__ = "thomas@hoogi.de"
|
||||
__status__ = "Development"
|
||||
|
||||
infile = None
|
||||
outfile = ""
|
||||
force = False # overwrite outfile
|
||||
|
||||
# Variables for condition parsing
|
||||
fieldname = Combine(Word(alphas, exact=1) + Word(alphanums, max=15))
|
||||
fieldvalue = Word(alphanums, max=16)
|
||||
equals = Literal("=")
|
||||
in_op = Literal("IN")
|
||||
and_op = Literal("AND")
|
||||
or_op = Literal("OR")
|
||||
comparison = Group(fieldname + equals + fieldvalue) \
|
||||
| Group(fieldname + in_op + delimitedList(fieldvalue))
|
||||
expr = Forward()
|
||||
expr <<= comparison + ZeroOrMore((and_op | or_op) + comparison)
|
||||
|
||||
def parse_condition(condition):
|
||||
try:
|
||||
result = expr.parseString(condition, parseAll=True)
|
||||
except Exception as e:
|
||||
return ""
|
||||
out = StringIO()
|
||||
andlist = []
|
||||
for token in result:
|
||||
# list: field = value or field IN value [, value ...]
|
||||
# str: AND, OR
|
||||
# combine ANDs and output reaching OR
|
||||
if type(token) == str:
|
||||
if token == "OR":
|
||||
andstr = ",\n".join(andlist)
|
||||
out.write(f'\t\t{{ {andstr} }},\n')
|
||||
andlist = []
|
||||
else:
|
||||
if token[1] == '=':
|
||||
andlist.append(f'"{token[0]}": "{token[2]}"')
|
||||
elif token[1] == 'IN':
|
||||
n = len(token) - 2
|
||||
if n == 1:
|
||||
# no list, write single value
|
||||
andlist.append(f'"{token[0]}": "{token[2]}"')
|
||||
else:
|
||||
# write list
|
||||
inlist = '", "'.join(token[2:])
|
||||
andlist.append(f'"{token[0]}": [ "{inlist}" ]\n')
|
||||
|
||||
if len(andlist) > 0:
|
||||
out.write("\t\t{{ {} }}".format(", ".join(andlist)))
|
||||
return out.getvalue()
|
||||
|
||||
def create_flist():
|
||||
flist = []
|
||||
for field in config.sections():
|
||||
properties = [f'\t"name": "{field}"']
|
||||
for prop, val in config.items(field):
|
||||
if prop in ["label", "type", "default", "description", "category", "check"]:
|
||||
properties.append(f'\t"{prop}": "{val}"')
|
||||
elif prop == "capabilities":
|
||||
# multiple values possible
|
||||
capas = []
|
||||
for capa in val.split(','):
|
||||
k, v = capa.split(':')
|
||||
capas.append(f'"{k.strip()}":"{v.strip()}"')
|
||||
capalist = ','.join(capas)
|
||||
properties.append(f'\t"{prop}": {{{capalist}}}')
|
||||
elif prop in ("min", "max"):
|
||||
properties.append(f'\t"{prop}": {val}')
|
||||
elif prop == "list":
|
||||
entries = '", "'.join([x.strip() for x in val.split(',')])
|
||||
properties.append(f'\t"list": ["{entries}"]')
|
||||
elif prop == "dict":
|
||||
d = {}
|
||||
for l in val.splitlines():
|
||||
if len(l) < 3:
|
||||
continue
|
||||
k, v = l.split(':')
|
||||
d[k.strip()] = v.strip()
|
||||
lines = []
|
||||
for k,v in d.items():
|
||||
lines.append(f'\t\t{{"l":"{v}","v":"{k}"}}')
|
||||
entries = ",\n".join(lines)
|
||||
properties.append(f'\t"list": [\n{entries}\n\t]')
|
||||
elif prop == "condition":
|
||||
jsoncond = parse_condition(val)
|
||||
properties.append(f'\t"{prop}": [\n{jsoncond}\n\t]\n')
|
||||
else:
|
||||
pass # ignore unknown stuff
|
||||
fieldprops = ",\n".join(properties)
|
||||
flist.append(f'{{\n{fieldprops}\n}}')
|
||||
return flist
|
||||
|
||||
def usage():
|
||||
print("{} v{}".format(os.path.basename(__file__), __version__))
|
||||
print(__copyright__)
|
||||
print()
|
||||
print("Command line options")
|
||||
print(" -c --config config file name to use")
|
||||
print(" -j --json json file name to generate")
|
||||
print(" -f force overwrite of existing json file")
|
||||
print(" -h show this help")
|
||||
print()
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
options, remainder = getopt.getopt(sys.argv[1:], 'c:j:fh', ['config=', 'json='])
|
||||
except getopt.GetoptError as e:
|
||||
print(e)
|
||||
sys.exit(1)
|
||||
filename = None
|
||||
for opt, arg in options:
|
||||
if opt in ('-c', '--config'):
|
||||
infile = arg
|
||||
elif opt in ('-j', '--json'):
|
||||
outfile = arg
|
||||
elif opt == '-h':
|
||||
usage()
|
||||
sys.exit(0)
|
||||
elif opt == '-f':
|
||||
force = True
|
||||
if not infile:
|
||||
print("Error: config filename missing")
|
||||
sys.exit(1)
|
||||
if not os.path.isfile(infile):
|
||||
print(f"Error: configuration file '{filename} not found'")
|
||||
sys.exit(1)
|
||||
if os.path.isfile(outfile) and not force:
|
||||
print(f"Error: json file '{outfile}' already exists")
|
||||
sys.exit(1)
|
||||
|
||||
config = configparser.ConfigParser()
|
||||
ret = config.read(infile)
|
||||
if len(ret) == 0:
|
||||
print(f"ERROR: Config file '{infile}' not found")
|
||||
sys.exit(1)
|
||||
|
||||
flist = create_flist()
|
||||
out = "[\n{}\n]\n".format(",\n".join(flist))
|
||||
if not outfile:
|
||||
# print to console
|
||||
print(out)
|
||||
else:
|
||||
# write to file
|
||||
with open(outfile, "w") as fh:
|
||||
fh.write(out)
|
Loading…
Reference in New Issue