Source code for zsl.utils.deploy.js_model_generator

"""
:mod:`zsl.utils.deploy.js_model_generator`
------------------------------------------

.. moduleauthor:: Peter Morihladko
"""
import hashlib
import importlib
import json
import sys
from typing import Union

import sqlalchemy.exc
from sqlalchemy.orm import class_mapper

from zsl.utils.deploy.integrator import integrate_to_file
from zsl.utils.string_helper import camelcase_to_underscore, underscore_to_camelcase

model_tpl = """    {model_prefix}{model_name} = {model_fn}.extend({{
        urlRoot: App.service_url + 'resource/{resource_name}',
        schema: {schema}
    }});

    {collection_prefix}{model_name} = {collection_fn}.extend({{
        model: {model_prefix}{model_name},
        url: {model_prefix}{model_name}.prototype.urlRoot
    }});
"""

list_opts_tpl = """function(callback, field) {{
                    field.setOptions(new {collection_prefix}{model_name}([],{{limit: 'unlimited'}}));
                }}"""


[docs] class ModelGenerator: def __init__(self, module, model_prefix="", collection_prefix="", model_fn="Atteq.bb.Model", collection_fn="Atteq.bb.Collection"): self.model_prefix = model_prefix self.collection_prefix = collection_prefix self.model_fn = model_fn self.collection_fn = collection_fn self.table_to_class = {} self.models = importlib.import_module(module) def _get_list_options(self, column): fk = list(column.foreign_keys)[0] table_name = underscore_to_camelcase(fk.column.table.name) return list_opts_tpl.format(collection_prefix=self.collection_prefix, model_name=table_name) def _map_table_name(self, model_names): """ Pre foregin_keys potrbejeme pre z nazvu tabulky zistit class, tak si to namapujme """ for model in model_names: if isinstance(model, tuple): model = model[0] try: model_cls = getattr(self.models, model) self.table_to_class[class_mapper(model_cls).tables[0].name] = model except AttributeError: pass
[docs] def generate_model(self, model_name, model_plural=None): if model_name not in dir(self.models): raise ImportError( "Model [{name}] couldn't be found in {module}\n".format(name=model_name, module=self.models.__name__)) if model_plural is None: model_plural = model_name + 's' model = getattr(self.models, model_name) schema = {} mapper = class_mapper(model) callbacks = [] for column in mapper.columns: col_type = column.type.__class__.__name__ attrs = {} if column.primary_key: continue if column.foreign_keys: try: attrs['type'] = 'AtteqSelect' attrs['options'] = '__CALLBACK__%d' % len(callbacks) callbacks.append(self._get_list_options(column)) # TODO uf uf uuuuf fk_table = list(column.foreign_keys)[0].target_fullname.split('.')[0] if fk_table in self.table_to_class: attrs['foreign_model'] = '%s%s' % (self.model_prefix, self.table_to_class[fk_table]) except sqlalchemy.exc.NoReferencedTableError: attrs['type'] = 'Text' elif col_type == 'TEXT': attrs['type'] = "TextArea" elif col_type == 'Enum': attrs['type'] = 'AtteqSelect' if column.nullable else 'Select' attrs['options'] = column.type.enums elif col_type == 'INTEGER': attrs['type'] = 'Number' else: attrs['type'] = "Text" if column.nullable: attrs['nullable'] = True schema[column.name] = attrs schema = "\n ".join(json.dumps(schema, indent=4).split("\n")) for i in range(len(callbacks)): schema = schema.replace('"__CALLBACK__%d"' % i, callbacks[i]) return model_tpl.format( model_name=model_name, model_prefix=self.model_prefix, collection_prefix=self.collection_prefix, resource_name=camelcase_to_underscore(model_plural), model_fn=self.model_fn, collection_fn=self.collection_fn, schema=schema )
[docs] def generate_models(self, models): js_models = [] self._map_table_name(models) for model in models: if isinstance(model, tuple): model_name = model[0] model_plural = model[1] else: model_name = model model_plural = None js_model = self.generate_model(model_name, model_plural) js_models.append(js_model) return js_models
[docs] def parse_model_arg(models): # type: (list[str]) -> list[str] """Parse the model argument definition.""" return [tuple(m.split('/')) if '/' in m else m for m in models]
[docs] def generate_js_models(module, models, collection_prefix, model_prefix, model_fn, collection_fn, marker, integrate, js_file): # type: (str, str, str, str, str, str, str, bool, str) -> Union[str, None] """Generate models for Backbone Javascript applications. :param module: module from which models are imported :param models: model name, can be a tuple WineCountry/WineCountries as singular/plural :param model_prefix: namespace prefix for models (app.models.) :param collection_prefix: namespace prefix for collection (App.collections.) :param model_fn: name of model constructor (MyApp.bb.Model) :param collection_fn: name of collection constructor (MyApp.bb.Collection) :param marker: marker to indicate the auto generated code :param integrate: integrate to file :param js_file: file to integrate :return: generated models or nothing if writing into a file """ options = { 'model_prefix': model_prefix, 'collection_prefix': collection_prefix, 'model_fn': model_fn, 'collection_fn': collection_fn } generator = ModelGenerator(module, **{o: options[o] for o in options if options[o] is not None}) models = generator.generate_models(parse_model_arg(models)) if integrate: sys.stderr.write("Integrate is really experimental") if not marker: marker = hashlib.md5("{0}{1}".format(module, models)).hexdigest() start = "// * -- START AUTOGENERATED %s -- * //\n" % marker end = "// * -- END AUTOGENERATED %s -- * //\n" % marker return integrate_to_file("\n".join(models), js_file, start, end) else: return "\n".join(models)