diff --git a/xls2ddl.egg-info/PKG-INFO b/xls2ddl.egg-info/PKG-INFO index 7069482..5e9c8ec 100644 --- a/xls2ddl.egg-info/PKG-INFO +++ b/xls2ddl.egg-info/PKG-INFO @@ -1,8 +1,12 @@ -Metadata-Version: 2.4 +Metadata-Version: 2.1 Name: xls2ddl Version: 1.1.3 Summary: a xlsx file to database ddl converter +Home-page: UNKNOWN Author: "yu moqing" Author-email: "yumoqing@gmail.com" License: "MIT" -Requires-Dist: apppublic +Platform: UNKNOWN + +UNKNOWN + diff --git a/xls2ddl.egg-info/entry_points.txt b/xls2ddl.egg-info/entry_points.txt index f00b600..bfbe0c8 100644 --- a/xls2ddl.egg-info/entry_points.txt +++ b/xls2ddl.egg-info/entry_points.txt @@ -3,3 +3,4 @@ json2ddl = xls2ddl.json2ddl:main json2xlsx = xls2ddl.json2xlsx:main xls2ddl = xls2ddl.xls2ddl:main xls2ui = xls2ddl.xls2ui:main + diff --git a/xls2ddl/__pycache__/__init__.cpython-312.pyc b/xls2ddl/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..95b72b0 Binary files /dev/null and b/xls2ddl/__pycache__/__init__.cpython-312.pyc differ diff --git a/xls2ddl/__pycache__/json2ddl.cpython-310.pyc b/xls2ddl/__pycache__/json2ddl.cpython-310.pyc new file mode 100644 index 0000000..5ef1a05 Binary files /dev/null and b/xls2ddl/__pycache__/json2ddl.cpython-310.pyc differ diff --git a/xls2ddl/__pycache__/json2ddl.cpython-312.pyc b/xls2ddl/__pycache__/json2ddl.cpython-312.pyc new file mode 100644 index 0000000..e0267ff Binary files /dev/null and b/xls2ddl/__pycache__/json2ddl.cpython-312.pyc differ diff --git a/xls2ddl/__pycache__/singletree.cpython-310.pyc b/xls2ddl/__pycache__/singletree.cpython-310.pyc new file mode 100644 index 0000000..562afa3 Binary files /dev/null and b/xls2ddl/__pycache__/singletree.cpython-310.pyc differ diff --git a/xls2ddl/__pycache__/singletree.cpython-312.pyc b/xls2ddl/__pycache__/singletree.cpython-312.pyc new file mode 100644 index 0000000..581e52b Binary files /dev/null and b/xls2ddl/__pycache__/singletree.cpython-312.pyc differ diff --git a/xls2ddl/__pycache__/tmpls.cpython-310.pyc b/xls2ddl/__pycache__/tmpls.cpython-310.pyc index eaacf68..4bf4668 100644 Binary files a/xls2ddl/__pycache__/tmpls.cpython-310.pyc and b/xls2ddl/__pycache__/tmpls.cpython-310.pyc differ diff --git a/xls2ddl/__pycache__/tmpls.cpython-312.pyc b/xls2ddl/__pycache__/tmpls.cpython-312.pyc new file mode 100644 index 0000000..9b46479 Binary files /dev/null and b/xls2ddl/__pycache__/tmpls.cpython-312.pyc differ diff --git a/xls2ddl/__pycache__/xls2crud.cpython-312.pyc b/xls2ddl/__pycache__/xls2crud.cpython-312.pyc new file mode 100644 index 0000000..1bc03c3 Binary files /dev/null and b/xls2ddl/__pycache__/xls2crud.cpython-312.pyc differ diff --git a/xls2ddl/__pycache__/xls2ddl.cpython-310.pyc b/xls2ddl/__pycache__/xls2ddl.cpython-310.pyc new file mode 100644 index 0000000..3b5595e Binary files /dev/null and b/xls2ddl/__pycache__/xls2ddl.cpython-310.pyc differ diff --git a/xls2ddl/__pycache__/xls2ui.cpython-310.pyc b/xls2ddl/__pycache__/xls2ui.cpython-310.pyc new file mode 100644 index 0000000..050c965 Binary files /dev/null and b/xls2ddl/__pycache__/xls2ui.cpython-310.pyc differ diff --git a/xls2ddl/__pycache__/xlsxData.cpython-312.pyc b/xls2ddl/__pycache__/xlsxData.cpython-312.pyc new file mode 100644 index 0000000..f811395 Binary files /dev/null and b/xls2ddl/__pycache__/xlsxData.cpython-312.pyc differ diff --git a/xls2ddl/tmpls.py b/xls2ddl/tmpls.py index 577af52..744a70f 100644 --- a/xls2ddl/tmpls.py +++ b/xls2ddl/tmpls.py @@ -244,6 +244,54 @@ if not userorgid: } ns['{{logined_userorgid}}'] = userorgid {% endif %} +{% if validation_rules %} +_validation_rules = json.loads(r'''{{validation_rules}}''') +import re as _re +_errors = [] +for _fname, _rules in _validation_rules.items(): + _val = params_kw.get(_fname, '') + if _val is None: _val = '' + _val = str(_val) + for _rule in _rules: + _rt = _rule.get('type', '') + _rm = _rule.get('message', _fname) + _rv = _rule.get('value') + if _rt == 'required': + if not _val or _val.strip() == '': + _errors.append(_rm) + break + elif _rt == 'minlength': + if _val and len(_val) < int(_rv): + _errors.append(_rm) + break + elif _rt == 'maxlength': + if len(_val) > int(_rv): + _errors.append(_rm) + break + elif _rt in ('min', 'max'): + if _val: + try: + _n = float(_val) + if _rt == 'min' and _n < float(_rv): _errors.append(_rm); break + if _rt == 'max' and _n > float(_rv): _errors.append(_rm); break + except (ValueError, TypeError): + _errors.append(_rm) + break + elif _rt == 'pattern': + if _val and not _re.match(_rv, _val): + _errors.append(_rm) + break + elif _rt == 'email': + if _val and not _re.match(r'^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$', _val): + _errors.append(_rm) + break + elif _rt == 'number': + if _val: + try: float(_val) + except (ValueError, TypeError): _errors.append(_rm); break +if _errors: + return {"widgettype":"Error","options":{"title":"Validation Failed","cwidth":16,"cheight":9,"timeout":3,"message":"; ".join(_errors)}} +{% endif %} db = DBPools() dbname = get_module_dbname('{{modulename}}') async with db.sqlorContext(dbname) as sor: @@ -309,6 +357,54 @@ ns['{{logined_userorgid}}'] = userorgid if params_kw.get('{{f}}'): ns['{{f}}'] = password_encode(params_kw.get('{{f}}')) {% endfor %} +{% if validation_rules %} +_validation_rules = json.loads(r'''{{validation_rules}}''') +import re as _re +_errors = [] +for _fname, _rules in _validation_rules.items(): +\t_val = params_kw.get(_fname, '') +\tif _val is None: _val = '' +\t_val = str(_val) +\tfor _rule in _rules: +\t\t_rt = _rule.get('type', '') +\t\t_rm = _rule.get('message', _fname) +\t\t_rv = _rule.get('value') +\t\tif _rt == 'required': +\t\t\tif not _val or _val.strip() == '': +\t\t\t\t_errors.append(_rm) +\t\t\t\tbreak +\t\telif _rt == 'minlength': +\t\t\tif _val and len(_val) < int(_rv): +\t\t\t\t_errors.append(_rm) +\t\t\t\tbreak +\t\telif _rt == 'maxlength': +\t\t\tif len(_val) > int(_rv): +\t\t\t\t_errors.append(_rm) +\t\t\t\tbreak +\t\telif _rt in ('min', 'max'): +\t\t\tif _val: +\t\t\t\ttry: +\t\t\t\t\t_n = float(_val) +\t\t\t\t\tif _rt == 'min' and _n < float(_rv): _errors.append(_rm); break +\t\t\t\t\tif _rt == 'max' and _n > float(_rv): _errors.append(_rm); break +\t\t\t\texcept (ValueError, TypeError): +\t\t\t\t\t_errors.append(_rm) +\t\t\t\t\tbreak +\t\telif _rt == 'pattern': +\t\t\tif _val and not _re.match(_rv, _val): +\t\t\t\t_errors.append(_rm) +\t\t\t\tbreak +\t\telif _rt == 'email': +\t\t\tif _val and not _re.match(r'^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$', _val): +\t\t\t\t_errors.append(_rm) +\t\t\t\tbreak +\t\telif _rt == 'number': +\t\t\tif _val: +\t\t\t\ttry: float(_val) +\t\t\t\texcept (ValueError, TypeError): _errors.append(_rm); break +if _errors: +\treturn {"widgettype":"Error","options":{"title":"Validation Failed","cwidth":16,"cheight":9,"timeout":3,"message":"; ".join(_errors)}} +{% endif %} db = DBPools() dbname = get_module_dbname('{{modulename}}') diff --git a/xls2ddl/xls2crud.py b/xls2ddl/xls2crud.py index fcc0819..37bd184 100644 --- a/xls2ddl/xls2crud.py +++ b/xls2ddl/xls2crud.py @@ -263,9 +263,23 @@ def build_data_browser(pat: str, desc: dict): with codecs.open(os.path.join(pat, f'index.ui'), 'w', "utf-8") as f: f.write(filter_backslash(s)) +def extract_validation_rules(desc) -> str: + """Extract validation rules from field alters. Returns JSON string or empty.""" + rules = {} + alters = desc.get('browserfields', {}).get('alters', {}) + if not alters: + return '' + for fname, alter in alters.items(): + if 'rules' in alter and alter['rules']: + rules[fname] = alter['rules'] + if not rules: + return '' + return json.dumps(rules, ensure_ascii=False) + def build_data_new(pat: str, desc: dict): e = MyTemplateEngine([]) desc = desc.copy() + desc['validation_rules'] = extract_validation_rules(desc) s = e.renders(data_new_tmpl, desc) with codecs.open(os.path.join(pat, f'add_{desc.tblname}.dspy'), 'w', "utf-8") as f: f.write(filter_backslash(s)) @@ -273,6 +287,7 @@ def build_data_new(pat: str, desc: dict): def build_data_update(pat: str, desc: dict): e = MyTemplateEngine([]) desc = desc.copy() + desc['validation_rules'] = extract_validation_rules(desc) s = e.renders(data_update_tmpl, desc) with codecs.open(os.path.join(pat, f'update_{desc.tblname}.dspy'), 'w', "utf-8") as f: f.write(filter_backslash(s))