oshipka/vm_gen/vm_gen.py

277 lines
10 KiB
Python
Raw Normal View History

2020-06-03 16:00:51 +02:00
import os
2020-06-05 12:38:25 +02:00
import shutil
2020-06-03 16:00:51 +02:00
import sys
2020-06-03 18:11:37 +02:00
import autopep8
2020-06-03 16:00:51 +02:00
import inflect
import yaml
2020-06-04 15:24:18 +02:00
from jinja2 import Environment, FileSystemLoader
2020-06-03 16:00:51 +02:00
2020-06-07 20:29:51 +02:00
from shared.shared import order_from_process_order
2020-06-04 15:24:18 +02:00
from oshipka.util.strings import snake_case_to_camel_case, camel_case_to_snake_case
2020-06-03 16:00:51 +02:00
2020-06-03 18:11:37 +02:00
pep_options = {'max_line_length': 120}
2020-06-03 16:00:51 +02:00
def _process_choice(column):
column_name = column.get('name', '')
column_upper = column_name.upper()
types_name = "{}_TYPES".format(column_upper)
choices = column.get('choices', {})
return {
'name': types_name,
'choices': choices,
}
def process_secondary(view_model, column_name):
model_name = camel_case_to_snake_case(view_model.get('name'))
secondary = "{}__{}".format(model_name, column_name)
2020-06-07 20:29:51 +02:00
return {
'name': secondary,
'columns': [{
'name': model_name
2020-06-07 20:29:51 +02:00
}, {
'name': column_name
2020-06-07 20:29:51 +02:00
}]
}
2021-05-08 12:24:13 +02:00
def _process_file(column):
column_type = column.get('type', '')
column_accept = column.get('accept', '')
if column_accept:
accept = column_accept
elif column_type in ['audio']:
accept = 'audio/*'
elif column_type in ['video']:
accept = 'video/*'
elif column_type in ['video']:
accept = 'video/*'
else:
accept = '*'
column["accept"] = accept
column["is_file"] = True
return column
2020-06-25 14:28:20 +02:00
VERBS = {
'get': {
'per_item': 'True',
'methods': ['GET'],
},
'list': {
'per_item': 'False',
'methods': ['GET'],
},
'table': {
'per_item': 'False',
'methods': ['GET'],
},
'search': {
'per_item': 'False',
'methods': ['GET'],
},
'create': {
'per_item': 'False',
'methods': ['GET', 'POST'],
},
'update': {
'per_item': 'True',
'methods': ['GET', 'POST'],
},
'delete': {
'per_item': 'True',
'methods': ['GET', 'POST'],
},
}
2020-06-03 16:00:51 +02:00
def enrich_view_model(view_model):
columns = []
for column in view_model.get('columns', {}):
2020-06-03 18:11:37 +02:00
column_name = column.get('name')
2020-06-03 16:00:51 +02:00
column_type = column.get('type')
if column_type in ['text', 'long_text', ]:
_column_type = 'db.UnicodeText'
2021-05-09 13:43:51 +02:00
elif column_type in ['password', ]:
_column_type = 'db.UnicodeText'
view_model['_password_types'] = True
elif column_type in ['number', 'int', 'integer', ]:
_column_type = 'db.Integer'
2020-08-11 19:02:29 +02:00
elif column_type in ['bool', 'boolean', ] or column_name.startswith('is_'):
2020-06-08 16:32:39 +02:00
_column_type = 'LiberalBoolean'
2020-06-25 14:28:20 +02:00
elif column_type in ['datetime', ] or column_name.endswith('_dt'):
_column_type = 'db.UnicodeText'
2020-06-03 16:00:51 +02:00
elif column_type in ['relationship', ]:
_column_type = 'relationship'
multiple = column.get('multiple')
if multiple:
2020-06-03 18:11:37 +02:00
if '_secondaries' not in view_model:
view_model['_secondaries'] = []
secondary_model = process_secondary(view_model, column_name)
column.update({'secondary': secondary_model})
2020-06-07 20:29:51 +02:00
view_model['_secondaries'].append(secondary_model)
2020-06-03 16:00:51 +02:00
elif column_type in ['choice', ]:
if '_choice_types' not in view_model:
view_model['_choice_types'] = []
_choices = _process_choice(column)
_column_type = 'ChoiceType({})'.format(_choices.get('name'))
view_model['_choice_types'].append(_choices)
2021-05-08 12:24:13 +02:00
elif column_type in ['file', 'audio', 'video', 'image', 'img', 'picture', ]:
column = _process_file(column)
_column_type = 'db.UnicodeText'.format()
2020-06-03 16:00:51 +02:00
else:
_column_type = 'db.UnicodeText'
column.update({'_type': _column_type})
columns.append(column)
view_model['columns'] = columns
2020-06-25 14:28:20 +02:00
view_model['_verbs'] = {}
for verb, verb_default in VERBS.items():
view_model['_verbs'][verb] = verb_default
access = view_model.get('access')
if access:
for acl in access:
verb = acl.get('verb')
if verb == 'all':
for verb in VERBS:
view_model['_verbs'][verb]['is_login_required'] = acl.get('login_required')
2021-05-08 15:52:52 +02:00
view_model['_verbs'][verb]['the_roles_required'] = acl.get('roles_required', [])
2020-06-25 14:28:20 +02:00
else:
is_login_required = acl.get('login_required')
if is_login_required is False: # overrides all
view_model['_verbs'][verb]['the_roles_required'] = acl.get('login_required')
view_model['_verbs'][verb]['is_login_required'] = is_login_required
2021-05-08 15:52:52 +02:00
view_model['_verbs'][verb]['the_roles_required'] = acl.get('roles_required', [])
2020-06-03 16:00:51 +02:00
return view_model
2021-05-08 12:24:13 +02:00
def process_navigation(view_models):
navigation_items = []
for view_model in view_models:
if view_model.get('show_in_nav'):
navigation_items.append(view_model)
template = env.get_template(os.path.join('html', 'navigation.html'))
rv = template.render(**{"navigation_items": navigation_items})
filepath = os.path.join(HTML_TEMPLATES_PATH, "navigation.html")
with open(filepath, 'w+') as f:
f.write(rv)
2020-06-03 16:00:51 +02:00
def process_model(view_model):
template = env.get_template('model_py')
2021-05-08 15:52:52 +02:00
view_model['acls'] = {}
for verb, acl in view_model['_verbs'].items():
view_model['acls'][verb] = {'authn': acl['is_login_required'], 'authz': acl['the_roles_required']}
2020-06-03 18:11:37 +02:00
model = autopep8.fix_code(template.render(**view_model), options=pep_options)
2020-06-03 16:00:51 +02:00
_model_name = view_model.get('name')
2020-06-08 16:32:39 +02:00
2021-05-08 20:53:50 +02:00
template_acl = env.get_template('model_acl_py')
model_acl = autopep8.fix_code(template_acl.render(**view_model), options=pep_options)
2020-06-08 16:32:39 +02:00
model_camel = _model_name.split('.yaml')[0]
model_snake = camel_case_to_snake_case(_model_name.split('.yaml')[0])
with open(os.path.join(MODELS_PATH, "_{}.py".format(model_snake)), 'w+') as f:
2020-06-03 18:11:37 +02:00
f.write(model)
2021-05-08 20:53:50 +02:00
with open(os.path.join(MODELS_PATH, "_{}_acl.py".format(model_snake)), 'w+') as f:
f.write(model_acl)
2020-06-08 16:32:39 +02:00
public_model = os.path.join(MODELS_PATH, "{}.py".format(model_snake))
if not os.path.exists(public_model):
with open(public_model, 'w+') as f:
2021-05-08 20:53:50 +02:00
f.write("from webapp.models._{}_acl import {}Acl\n".format(model_snake, model_camel))
f.write("from webapp.models._{} import {}\n".format(model_snake, model_camel))
2020-06-03 16:00:51 +02:00
2020-06-05 12:38:25 +02:00
def process_routes(view_model):
template = env.get_template('routes_py')
model = autopep8.fix_code(template.render(**view_model), options=pep_options)
_model_name = view_model.get('name')
model_name_snake = camel_case_to_snake_case(_model_name.split('.yaml')[0])
filename = "{}.py".format(model_name_snake)
with open(os.path.join(ROUTES_PATH, filename), 'w+') as f:
f.write(model)
route_hooks_filename = os.path.join(ROUTES_PATH, "{}_hooks.py".format(model_name_snake))
if not os.path.exists(route_hooks_filename):
src_path = os.path.join(VM_TEMPLATES_PATH, "_hooks.py")
shutil.copy(src_path, route_hooks_filename)
2020-06-03 16:00:51 +02:00
def process_html_templates(view_model):
_model_name_snake = camel_case_to_snake_case(view_model.get('name'))
2020-06-07 13:13:14 +02:00
templates_dir = os.path.join(HTML_TEMPLATES_PATH, _model_name_snake)
if not os.path.exists(templates_dir):
os.makedirs(templates_dir)
2020-06-03 16:00:51 +02:00
for filename in os.listdir(os.path.join(VM_TEMPLATES_PATH, "html")):
2020-06-07 13:13:14 +02:00
filepath = os.path.join(templates_dir, filename)
if not filename.startswith("_") and os.path.exists(filepath):
continue
2020-06-03 16:00:51 +02:00
template = env.get_template(os.path.join('html', filename))
rv = template.render(**view_model)
2020-06-07 13:13:14 +02:00
with open(filepath, 'w+') as f:
2020-06-03 16:00:51 +02:00
f.write(rv)
2020-06-03 18:11:37 +02:00
def main():
2020-06-05 12:38:25 +02:00
all_model_imports = ['from oshipka.persistance import db']
2021-05-08 12:24:13 +02:00
all_route_imports, all_view_models = [], []
2020-06-07 20:29:51 +02:00
view_model_names = order_from_process_order('yaml', VIEW_MODELS_PATH)
2020-06-03 16:00:51 +02:00
for view_model_name in view_model_names:
2020-06-07 20:29:51 +02:00
with open(os.path.join(VIEW_MODELS_PATH, "{}.yaml".format(view_model_name)), 'r') as stream:
2020-06-03 16:00:51 +02:00
try:
view_models = yaml.safe_load_all(stream)
for view_model in view_models:
2021-05-08 12:24:13 +02:00
all_view_models.append(view_model)
2020-06-03 16:00:51 +02:00
view_model = enrich_view_model(view_model)
process_model(view_model)
2020-06-05 12:38:25 +02:00
process_routes(view_model)
2020-06-03 18:11:37 +02:00
view_model_name = view_model.get('name', '')
2020-06-05 12:38:25 +02:00
model_snake_name = camel_case_to_snake_case(view_model_name)
2021-05-08 20:53:50 +02:00
all_model_imports.append('from webapp.models.{s} import {c}, {c}Acl'.format(
s=model_snake_name, c=view_model_name))
2020-06-03 16:00:51 +02:00
process_html_templates(view_model)
2020-06-05 12:38:25 +02:00
all_route_imports.append('from webapp.routes.{} import *'.format(
model_snake_name
))
2020-06-03 16:00:51 +02:00
except yaml.YAMLError as e:
breakpoint()
2021-05-08 12:24:13 +02:00
process_navigation(all_view_models)
2020-06-05 12:38:25 +02:00
all_model_imports = autopep8.fix_code('\n'.join(all_model_imports), options=pep_options)
all_route_imports = autopep8.fix_code('\n'.join(all_route_imports), options=pep_options)
2020-06-03 18:11:37 +02:00
with open(os.path.join(MODELS_PATH, "__init__.py"), 'w+') as f:
2020-06-05 12:38:25 +02:00
f.write(all_model_imports)
with open(os.path.join(ROUTES_PATH, "__init__.py"), 'w+') as f:
f.write(all_route_imports)
2020-06-03 16:00:51 +02:00
if __name__ == "__main__":
2020-06-03 18:11:37 +02:00
basepath = sys.argv[1]
2020-06-03 16:00:51 +02:00
oshipka_path = os.environ.get('OSHIPKA_PATH')
VM_TEMPLATES_PATH = os.path.join(oshipka_path, "vm_gen", "templates")
WEBAPP_PATH = os.path.join(basepath, "webapp")
VIEW_MODELS_PATH = os.path.join(WEBAPP_PATH, "view_models")
2020-06-03 18:11:37 +02:00
MODELS_PATH = os.path.join(WEBAPP_PATH, "models")
2020-06-05 12:38:25 +02:00
ROUTES_PATH = os.path.join(WEBAPP_PATH, "routes")
for d in [VIEW_MODELS_PATH, MODELS_PATH, ROUTES_PATH, ]:
2020-06-04 22:11:57 +02:00
os.makedirs(d, exist_ok=True)
2020-06-04 18:02:34 +02:00
HTML_TEMPLATES_PATH = os.path.join(WEBAPP_PATH, "templates")
2020-06-03 16:00:51 +02:00
env = Environment(
loader=FileSystemLoader(searchpath=VM_TEMPLATES_PATH),
block_start_string='[%',
block_end_string='%]',
variable_start_string='[[',
variable_end_string=']]'
)
env.filters['snake_to_camel'] = snake_case_to_camel_case
env.filters['camel_to_snake'] = camel_case_to_snake_case
p = inflect.engine()
env.filters['pluralize'] = p.plural
2020-06-03 18:11:37 +02:00
main()