def generate(self): for file_path, lst_info in self.dct_cb.items(): cw = CodeWriter() lst_header = [b for a in lst_info for b in a[0]] # Fix divergence import try: index_pos = lst_header.index("import odoo.http as http") lst_header.pop(index_pos) lst_header.append("from odoo import http") except: pass set_header = set(lst_header) lst_cb = [a[1] for a in lst_info] enable_logger = any([a[3] for a in lst_info]) lst_inherit_class = list(set([a[2] for a in lst_info])) if len(lst_inherit_class) > 1: _logger.error( "Cannot support multiple class in the same python file:" f" '{lst_inherit_class}', filepath: '{file_path}'") continue str_inherit_class = lst_inherit_class[0] for line in set_header: cw.emit(line) if enable_logger: cw.emit("import logging") cw.emit("_logger = logging.getLogger(__name__)") cw.emit( "class" f" {self._module.name.replace('_', ' ').title().replace(' ', '')}Controller({str_inherit_class}):" ) with cw.indent(): for cb in lst_cb: cb(self._module, cw) out = cw.render() l_model = out.split("\n") self._code_generator_data.write_file_lst_content( file_path, l_model)
def generate_python_init_file(self, cg_module): for component, lst_module in self._dct_import_dir.items(): init_path = os.path.join(component, "__init__.py") if not component: lst_module = [a for a in self._dct_import_dir.keys() if a] lst_module.sort() cw = CodeWriter() if cg_module.license == "AGPL-3": cw.emit("# License AGPL-3.0 or later" " (https://www.gnu.org/licenses/agpl)") cw.emit() elif cg_module.license == "LGPL-3": cw.emit("# License LGPL-3.0 or later" " (https://www.gnu.org/licenses/lgpl)") cw.emit() else: _logger.warning(f"License {cg_module.license} not supported.") if component: for module in lst_module: cw.emit(f"from . import {module}") elif lst_module: cw.emit(f"from . import {', '.join(lst_module)}") for extra_import in self._dct_extra_module_init_path.get( component, []): cw.emit(extra_import) self.write_file_str(init_path, cw.render())
def _set_website_snippet_static_javascript_file(self, module): """ Function to set the module hook file :param module: :return: """ cw = CodeWriter() cw.cur_indent = 4 * cw.default_dent if module.generate_website_snippet_generic_model: lst_model_search = ( module.generate_website_snippet_generic_model.split(";") ) lst_model_id_search = [] for s_model in lst_model_search: model_id = self.env["ir.model"].search( [("model", "=", s_model)] ) if model_id: lst_model_id_search.append(model_id[0]) else: _logger.warning(f"Model not existing : {s_model}") for model_id in lst_model_id_search: for field_id in model_id.field_id: if field_id.name not in MAGIC_FIELDS: cw.emit(f'if (data["{field_id.name}"]) {{') with cw.indent(): with cw.indent(): cw.emit( f'self.$(".{field_id.name}_value").text(data["{field_id.name}"]);' ) cw.emit("}") else: cw.emit("var data_json = data;") cw.emit('var hello = data_json["hello"];') cw.emit(f'self.$(".{module.name}_value").text(hello);') code = cw.render() content = ( f"odoo.define('{module.name}.animation', function (require)" """ { 'use strict'; var sAnimation = require('website.content.snippets.animation'); sAnimation.registry.""" f"{module.name}" """ = sAnimation.Class.extend({ """ f"selector: '.o_{module.name}'," """ start: function () { var self = this; var def = this._rpc({route: '""" f"/{module.name}/helloworld" """'}).then(function (data) { if (data.error) { return; } if (_.isEmpty(data)) { return; } """ + code + """ }); return $.when(this._super.apply(this, arguments), def); } }) }); """ ) file_path = os.path.join( "static", "src", "js", f"website.{module.name}.animation.js" ) self.code_generator_data.write_file_str(file_path, content)
def _set_website_snippet_controller_file(self, module): """ Function to set the module hook file :param module: :return: """ cw = CodeWriter() cw.emit("from odoo import http") cw.emit("from odoo.http import request") cw.emit("") cw.emit("") cw.emit("class HelloWorldController(http.Controller):") cw.emit("") with cw.indent(): cw.emit( f"@http.route(['/{module.name}/helloworld'], type='json', auth=\"public\", website=True," ) with cw.indent(): with cw.indent(): with cw.indent(): with cw.indent(): cw.emit("methods=['POST', 'GET'], csrf=False)") with cw.indent(): cw.emit("def hello_world(self):") with cw.indent(): cw.emit('return {"hello": "Hello World!"}') out = cw.render() l_model = out.split("\n") file_path = f"{self.code_generator_data.controllers_path}/main.py" self.code_generator_data.write_file_lst_content(file_path, l_model)
def main(): config = get_config() cw = CodeWriter() mydoc = minidom.parse(config.file) if not mydoc: print(f"Error, cannot parse {config.file}") sys.exit(1) cw.emit("from lxml.builder import E") cw.emit("from lxml import etree as ET") cw.emit("from code_writer import CodeWriter") cw.emit("") cw.emit('print(\'<?xml version="1.0" encoding="utf-8"?>\')') cw.emit('print("<odoo>")') lst_function = [] comment_for_next_group = None for odoo in mydoc.getElementsByTagName("odoo"): for ir_view_item in odoo.childNodes: if ir_view_item.nodeType == Node.ELEMENT_NODE: # Show part of xml fct_name = "ma_fonction" lst_function.append(fct_name) cw.emit(f"def {fct_name}():") with cw.indent(): cw.emit('"""') for line in transform_string_to_list( ir_view_item.toprettyxml() ): cw.emit(line) cw.emit('"""') # Show comment if comment_for_next_group: cw.emit( "print('<!--" f" {comment_for_next_group.strip()} -->')" ) comment_for_next_group = None attributes_root = dict(ir_view_item.attributes.items()) lst_out = code_writer_deep_xml(ir_view_item) generate_code = GenerateCode() generate_code.generate_code(lst_out) child_root = generate_code.result code = ( "root =" f" E.{ir_view_item.tagName}({str(attributes_root)}," f" {child_root})" ) for line in code.split("\n"): cw.emit(line) cw.emit("content = ET.tostring(root, pretty_print=True)") cw.emit() cw.emit("cw = CodeWriter()") cw.emit( 'for line in content.decode("utf-8").split("\\n"):' ) with cw.indent(): cw.emit("with cw.indent():") with cw.indent(): cw.emit("cw.emit(line)") cw.emit("print(cw.render())") cw.emit(f"{fct_name}()") elif ir_view_item.nodeType == Node.COMMENT_NODE: comment_for_next_group = ir_view_item.data else: # print(ir_view_item) pass cw.emit('print("</odoo>")') output = cw.render() if config.output: with open(config.output, "w") as file: file.write(output) else: print(output)
def _set_hook_file(self, module): """ Function to set the module hook file :param module: :return: """ cw = CodeWriter() lst_keep_f2exports = [] for line in MODEL_SUPERUSER_HEAD: str_line = line.strip() cw.emit(str_line) is_generator_demo = module.name == "code_generator_demo" # Add constant if module.hook_constant_code: if module.enable_template_code_generator_demo: cw.emit( "# TODO HUMAN: change my module_name to create a specific demo functionality" ) for line in module.hook_constant_code.split("\n"): cw.emit(line) def _add_hook( cw, hook_show, hook_code, hook_feature_gen_conf, post_init_hook_feature_code_generator, uninstall_hook_feature_code_generator, method_name, has_second_arg, ): if hook_show: cw.emit() cw.emit() if has_second_arg: cw.emit(f"def {method_name}(cr, e):") else: cw.emit(f"def {method_name}(cr):") with cw.indent(): for line in hook_code.split("\n"): cw.emit(line) if hook_feature_gen_conf: with cw.indent(): cw.emit("# General configuration") with cw.block(before="values =", delim=("{", "}")): pass cw.emit( "event_config = env['res.config.settings'].sudo().create(values)" ) cw.emit("event_config.execute()") if post_init_hook_feature_code_generator: with cw.indent(): cw.emit() cw.emit("# The path of the actual file") if module.template_module_path_generated_extension: cw.emit( "path_module_generate = os.path.normpath(os.path.join(os.path.dirname" f"(__file__), '..', {module.template_module_path_generated_extension}))" ) else: cw.emit( "# path_module_generate = os.path.normpath(os.path.join(os.path.dirname" "(__file__), '..'))") cw.emit() cw.emit( 'short_name = MODULE_NAME.replace("_", " ").title()' ) cw.emit() cw.emit("# Add code generator") cw.emit("value = {") with cw.indent(): cw.emit('"shortdesc": short_name,') cw.emit('"name": MODULE_NAME,') cw.emit('"license": "AGPL-3",') cw.emit('"author": "TechnoLibre",') cw.emit('"website": "https://technolibre.ca",') cw.emit('"application": True,') # with cw.block(before='"depends" :', delim=('[', '],')): # cw.emit('"code_generator",') # cw.emit('"code_generator_hook",') cw.emit('"enable_sync_code": True,') if module.template_module_path_generated_extension: cw.emit( '"path_sync_code": path_module_generate,' ) else: cw.emit( '# "path_sync_code": path_module_generate,' ) cw.emit("}") cw.emit() cw.emit( "# TODO HUMAN: enable your functionality to generate" ) enable_template_code_generator_demo = ( module.enable_template_code_generator_demo if module.name == "code_generator_demo" else False) if module.enable_template_code_generator_demo: cw.emit( 'value["enable_template_code_generator_demo"] = ' f"{enable_template_code_generator_demo}") cw.emit('value["template_model_name"] = ""') cw.emit( 'value["enable_template_wizard_view"] = False' ) cw.emit( 'value["enable_template_website_snippet_view"] = ' f"{module.enable_template_website_snippet_view}" ) elif module.enable_template_website_snippet_view: cw.emit( 'value["enable_generate_website_snippet"] = True' ) cw.emit( 'value["enable_generate_website_snippet_javascript"] = True' ) cw.emit( 'value["generate_website_snippet_type"] = "effect"' " # content,effect,feature,structure") cw.emit( f'value["enable_sync_template"] = {module.enable_sync_template}' ) cw.emit(f'value["ignore_fields"] = ""') cw.emit( f'value["post_init_hook_show"] = {module.enable_template_code_generator_demo}' ) cw.emit( f'value["uninstall_hook_show"] = {module.enable_template_code_generator_demo}' ) cw.emit( 'value["post_init_hook_feature_code_generator"] = ' f"{module.enable_template_code_generator_demo}" ) cw.emit( 'value["uninstall_hook_feature_code_generator"] = ' f"{module.enable_template_code_generator_demo}" ) cw.emit() if module.enable_template_code_generator_demo: cw.emit("new_module_name = MODULE_NAME") cw.emit( 'if MODULE_NAME != "code_generator_demo" and "code_generator_" in MODULE_NAME:' ) with cw.indent(): cw.emit( 'if "code_generator_template" in MODULE_NAME:' ) with cw.indent(): cw.emit( 'if value["enable_template_code_generator_demo"]:' ) with cw.indent(): cw.emit( 'new_module_name = f"code_generator_{MODULE_NAME[' "len('code_generator_template_'):]}\"" ) cw.emit("else:") with cw.indent(): cw.emit( 'new_module_name = MODULE_NAME[len("code_generator_template_"):]' ) cw.emit("else:") with cw.indent(): cw.emit( 'new_module_name = MODULE_NAME[len("code_generator_"):]' ) cw.emit( 'value["template_module_name"] = new_module_name' ) cw.emit( 'value["hook_constant_code"] = f\'MODULE_NAME = "{new_module_name}"\'' ) else: cw.emit( 'value["hook_constant_code"] = f\'MODULE_NAME = "{MODULE_NAME}"\'' ) cw.emit() cw.emit( 'code_generator_id = env["code.generator.module"].create(value)' ) cw.emit() if module.dependencies_template_id: cw.emit("# Add dependencies") cw.emit( "# TODO HUMAN: update your dependencies") with cw.block(before="lst_depend =", delim=("[", "]")): for depend in module.dependencies_template_id: cw.emit(f'"{depend.depend_id.name}",') cw.emit( 'lst_dependencies = env["ir.module.module"]' '.search([("name", "in", lst_depend)])') cw.emit("for depend in lst_dependencies:") with cw.indent(): with cw.block(before="value =", delim=("{", "}")): cw.emit( '"module_id": code_generator_id.id,' ) cw.emit('"depend_id": depend.id,') cw.emit('"name": depend.display_name,') cw.emit( 'env["code.generator.module.dependency"].create(value)' ) cw.emit() if is_generator_demo: with cw.block(before="lst_depend =", delim=("[", "]")): for depend in module.dependencies_template_id: cw.emit( f'"{depend.depend_id.name}",') cw.emit( 'lst_dependencies = env["ir.module.module"]' '.search([("name", "in", lst_depend)])' ) cw.emit("for depend in lst_dependencies:") with cw.indent(): with cw.block(before="value =", delim=("{", "}")): cw.emit( '"module_id": code_generator_id.id,' ) cw.emit('"depend_id": depend.id,') cw.emit( '"name": depend.display_name,') cw.emit( 'env["code.generator.module.template.dependency"].create(value)' ) cw.emit() if module.template_model_name: lst_model = module.template_model_name.split( ";") len_model = len(lst_model) i = -1 for model_model in lst_model: i += 1 model_name = model_model.replace(".", "_") title_model_model = model_name.replace( "_", " ").title() variable_model_model = f"model_{model_name}" cw.emit(f"# Add {title_model_model}") cw.emit("value = {") with cw.indent(): cw.emit(f'"name": "{model_name}",') cw.emit(f'"model": "{model_model}",') cw.emit( '"m2o_module": code_generator_id.id,' ) cw.emit('"rec_name": None,') cw.emit('"nomenclator": True,') cw.emit("}") cw.emit( f'{variable_model_model} = env["ir.model"].create(value)' ) cw.emit("") self._write_generated_template( module, model_model, cw) cw.emit("##### Begin Field") if module.enable_sync_template: module_file_sync = ExtractorModule( module, model_model) self._write_sync_template( module, model_model, cw, variable_model_model, lst_keep_f2exports, module_file_sync, ) else: cw.emit("value_field_boolean = {") with cw.indent(): cw.emit('"name": "field_boolean",') cw.emit('"model": "demo.model",') cw.emit( '"field_description": "field description",' ) cw.emit('"ttype": "boolean",') cw.emit( f'"model_id": {variable_model_model}.id,' ) cw.emit("}") cw.emit( 'env["ir.model.fields"].create(value_field_boolean)' ) cw.emit() cw.emit("# FIELD TYPE Many2one") cw.emit("#value_field_many2one = {") with cw.indent(): cw.emit( '#"name": "field_many2one",') cw.emit('#"model": "demo.model",') cw.emit( '#"field_description": "field description",' ) cw.emit('#"ttype": "many2one",') cw.emit( '#"comodel_name": "model.name",' ) cw.emit( '#"relation": "model.name",') cw.emit( f'#"model_id": {variable_model_model}.id,' ) cw.emit("#}") cw.emit( '#env["ir.model.fields"].create(value_field_many2one)' ) cw.emit("") cw.emit("# Hack to solve field name") cw.emit( "field_x_name = env[\"ir.model.fields\"].search([('model_id', '=', " f"{variable_model_model}.id), ('name', '=', 'x_name')])" ) cw.emit('field_x_name.name = "name"') cw.emit( f'{variable_model_model}.rec_name = "name"' ) cw.emit("") if i >= len_model - 1 and lst_keep_f2exports: cw.emit("") cw.emit( "# Added one2many field, many2many need to be creat before add " "one2many") for ( field_id, model_model, variable_model_model, ) in lst_keep_f2exports: # Finish to print one2many move at the end self._write_sync_template( module, model_model, cw, variable_model_model, lst_keep_f2exports, None, lst_force_f2exports=[field_id], ) cw.emit("##### End Field") cw.emit() # TODO add data nomenclator, research data from model # TODO By default, no data will be nomenclator # cw.emit("# Add data nomenclator") # cw.emit("value = {") # with cw.indent(): # cw.emit("\"field_boolean\": True,") # cw.emit("\"name\": \"demo\",") # cw.emit("}") # cw.emit(f"env[\"{model_model}\"].create(value)") # cw.emit() if module.enable_template_wizard_view: cw.emit("# Generate view") cw.emit( "wizard_view = env['code.generator.generate.views.wizard'].create({" ) with cw.indent(): cw.emit( "'code_generator_id': code_generator_id.id," ) cw.emit("'enable_generate_all': False,") if module.enable_generate_portal: cw.emit( f"'enable_generate_portal': {module.enable_generate_portal}," ) cw.emit("})") cw.emit("") cw.emit("wizard_view.button_generate_views()") cw.emit() cw.emit("# Generate module") cw.emit("value = {") with cw.indent(): cw.emit( '"code_generator_ids": code_generator_id.ids' ) cw.emit("}") cw.emit( 'code_generator_writer = env["code.generator.writer"].create(value)' ) if uninstall_hook_feature_code_generator: with cw.indent(): cw.emit( 'code_generator_id = env["code.generator.module"].search([("name", "=", MODULE_NAME)])' ) cw.emit("if code_generator_id:") with cw.indent(): cw.emit("code_generator_id.unlink()") _add_hook( cw, module.pre_init_hook_show, module.pre_init_hook_code, module.pre_init_hook_feature_general_conf, False, False, "pre_init_hook", False, ) _add_hook( cw, module.post_init_hook_show, module.post_init_hook_code, module.post_init_hook_feature_general_conf, module.post_init_hook_feature_code_generator, False, "post_init_hook", True, ) _add_hook( cw, module.uninstall_hook_show, module.uninstall_hook_code, module.uninstall_hook_feature_general_conf, False, module.post_init_hook_feature_code_generator, "uninstall_hook", True, ) hook_file_path = "hooks.py" self.code_generator_data.write_file_str(hook_file_path, cw.render())
def _set_website_leaflet_controller_file(self, module): """ Function to set the module hook file :param module: :return: """ lst_fields = [] lst_model = [] for a in module.o2m_models: active_id = a.field_id.filtered(lambda key: key.name == "active") open_popup_id = a.field_id.filtered( lambda key: key.name == "open_popup") html_text_id = a.field_id.filtered( lambda key: key.name == "html_text") fields_id = a.field_id.filtered( lambda key: "geo_" in key.ttype).sorted(key="name") if fields_id: lst_model.append(a) lst_fields = [b for b in fields_id] # Find right model break if not len(lst_fields): return # Cannot support multiple model with field geo assert len(lst_model) == 1 model_id = lst_model[0] cw = CodeWriter() cw.emit("from odoo import http") cw.emit("from operator import attrgetter") cw.emit("import json") cw.emit("import numpy") cw.emit("from pyproj import Transformer") cw.emit("from odoo.http import request") cw.emit("from collections import defaultdict") cw.emit("") cw.emit("") cw.emit("class MapFeatureController(http.Controller):") cw.emit("") with cw.indent(): cw.emit( f"@http.route(['/{module.name}/map/config'], type='json', auth=\"public\", website=True," ) with cw.indent(): with cw.indent(): with cw.indent(): with cw.indent(): cw.emit("methods=['POST', 'GET'], csrf=False)") with cw.indent(): cw.emit("def map_detail(self):") with cw.indent(): cw.emit('name = "test"') cw.emit("lat = 45.587134") cw.emit("lng = -73.733368") cw.emit("enable = True") cw.emit("size_width = 800") cw.emit("size_height = 600") cw.emit('provider = "CartoDB"') cw.emit("zoom = 13") cw.emit("categories = {}") # cw.emit(f"for i in http.request.env['{model_id.model}'].search([[\"active\", \"=\", True]]):") # with cw.indent(): # cw.emit("categories[i.id] = {") # with cw.indent(): # cw.emit("\"name\": i.name,") # cw.emit("\"description\": i.description,") # with cw.indent(): # cw.emit("}") with cw.indent(): cw.emit("features = defaultdict(list)") cw.emit( 'transformer = Transformer.from_crs("epsg:3857", "epsg:4326")' ) cw.emit("") with cw.indent(): str_search = "" if active_id: str_search = '("active", "=", True)' cw.emit( f'map_feature_ids = request.env["{model_id.model}"].sudo().search([{str_search}])' ) cw.emit("for feature in map_feature_ids:") with cw.indent(): cw.emit("value = {}") cw.emit("# Help robot, ignore this") cw.emit("if False:") with cw.indent(): cw.emit("pass") for field_id in lst_fields: cw.emit(f'elif feature.type == "{field_id.name}":') with cw.indent(): cw.emit(f"if not feature.{field_id.name}:") with cw.indent(): cw.emit(f"continue") if field_id.ttype == "geo_polygon": cw.emit( f"xy = feature.{field_id.name}.exterior.coords.xy" ) else: cw.emit(f"xy = feature.{field_id.name}.xy") cw.emit("") with cw.indent(): with cw.indent(): cw.emit("coord_UTM = numpy.column_stack(xy).tolist()") cw.emit( "coord_lat_long = [transformer.transform(*i) for i in coord_UTM]" ) # cw.emit("") with cw.indent(): # with cw.indent(): # cw.emit("if feature.category_id:") # with cw.indent(): # cw.emit("value[\"category_id\"] = feature.category_id.id") if open_popup_id: with cw.indent(): cw.emit("if feature.open_popup:") with cw.indent(): cw.emit('value["open_popup"] = feature.open_popup') if html_text_id: with cw.indent(): cw.emit("if feature.html_text:") with cw.indent(): cw.emit('value["html_popup"] = feature.html_text') cw.emit("") with cw.indent(): with cw.indent(): cw.emit("# Help robot, ignore this") cw.emit("if False:") with cw.indent(): cw.emit("pass") for field_id in lst_fields: with cw.indent(): cw.emit(f'elif feature.type == "{field_id.name}":') with cw.indent(): if field_id.ttype == "geo_point": cw.emit( 'value["coordinates"] = coord_lat_long[0]') cw.emit('features["markers"].append(value)') else: cw.emit( 'value["coordinates"] = coord_lat_long') if field_id.ttype == "geo_polygon": cw.emit('features["areas"].append(value)') elif field_id.ttype == "geo_line": cw.emit('features["lines"].append(value)') cw.emit("") with cw.indent(): with cw.indent(): cw.emit("return {") with cw.indent(): cw.emit('"name": name,') cw.emit('"lat": lat,') cw.emit('"lng": lng,') cw.emit('"enable": enable,') cw.emit('"size_width": size_width,') cw.emit('"size_height": size_height,') cw.emit('"zoom": zoom,') cw.emit('"provider": provider,') cw.emit('"features": features,') cw.emit('"categories": categories,') with cw.indent(): cw.emit("}") out = cw.render() l_model = out.split("\n") file_path = f"{self.code_generator_data.controllers_path}/main.py" self.code_generator_data.write_file_lst_content(file_path, l_model)
def set_module_css_file(self, module): super(CodeGeneratorWriter, self).set_module_css_file(module) if not module.theme_website: return # _variables.scss files cw = CodeWriter(default_width=80) # cw.emit(f"$primary: {module.theme_website_primary_color} !default;") # cw.emit(f"$secondary: {module.theme_website_secondary_color} !default;") # cw.emit(f"$body-color: {module.theme_website_body_color} !default;") file_path = os.path.join(self.code_generator_data.css_path, "_variables.scss") self.code_generator_data.write_file_str(file_path, cw.render()) # custom.scss files cw = CodeWriter() file_path = os.path.join(self.code_generator_data.css_path, "custom.scss") self.code_generator_data.write_file_str(file_path, cw.render()) # primary_variables.scss files cw = CodeWriter() file_path = os.path.join(self.code_generator_data.css_path, "primary_variables.scss") cw.emit("$o-theme-layout: 'full';") cw.emit("//$o-theme-navbar-height: 300px;") cw.emit() cw.emit("//" + "-" * 78) cw.emit_wrapped_text( "Colors", prefix="// ", indent_after_first=True, ) cw.emit("//" + "-" * 78) cw.emit() cw.emit("// Extend default color palettes with website-related colors") cw.emit("$-palettes: ();") cw.emit("@each $palette in $o-color-palettes {") with cw.indent(): cw.emit("$-palettes: append($-palettes, map-merge((") with cw.indent(): cw.emit(f"'body': {module.theme_website_body_color},") cw.emit(f"'menu': {module.theme_website_menu_color},") cw.emit(f"'footer': {module.theme_website_footer_color},") cw.emit(f"'text': {module.theme_website_text_color},") cw.emit(f"'alpha': {module.theme_website_primary_color},") cw.emit(f"'beta': {module.theme_website_secondary_color},") cw.emit(f"'gamma': {module.theme_website_extra_1_color},") cw.emit(f"'delta': {module.theme_website_extra_2_color},") cw.emit(f"'epsilon': {module.theme_website_extra_3_color},") cw.emit(f"'h1': null, // Default to text") cw.emit(f"'h2': null, // Default to h1") cw.emit(f"'h3': null, // Default to h2") cw.emit(f"'h4': null, // Default to h3") cw.emit(f"'h5': null, // Default to h4") cw.emit(f"'h6': null, // Default to h5") cw.emit("), $palette));") cw.emit("}") cw.emit() cw.emit("$o-color-palettes: $-palettes;") cw.emit() cw.emit("$o-theme-color-palettes: ();") cw.emit("@each $-palette in $-palettes {") with cw.indent(): cw.emit( "$o-theme-color-palettes: append($o-theme-color-palettes, map-merge($-palette, (" ) with cw.indent(): cw.emit("'primary': map-get($-palette, 'alpha'),") cw.emit("'secondary': map-get($-palette, 'beta'),") cw.emit(")));") cw.emit("}") cw.emit() cw.emit("// By default, all user color palette values are null. Each null value is") cw.emit("// automatically replaced with corresponding color of chosen color palette.") cw.emit("$o-user-color-palette: () !default;") cw.emit() cw.emit("// By default, all user theme color palette values are null. Each null value") cw.emit("// is automatically replaced with corresponding color of chosen theme color") cw.emit("// palette.") cw.emit("$o-user-theme-color-palette: () !default;") cw.emit() cw.emit("//" + "-" * 78) cw.emit_wrapped_text( "Fonts", prefix="// ", indent_after_first=True, ) cw.emit("//" + "-" * 78) cw.emit() cw.emit("$o-theme-fonts: (") with cw.indent(): cw.emit( '(-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Noto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"), // This is BS default' ) cw.emit("('Open Sans', sans-serif),") cw.emit("('Source Sans Pro', sans-serif),") cw.emit("('Raleway', sans-serif),") cw.emit("('Noto Serif', serif),") cw.emit("('Arvo', Times, serif),") cw.emit(") !default;") cw.emit("$o-theme-font-urls: (") with cw.indent(): cw.emit("null,") cw.emit("'Open+Sans:400,400i,700,700i',") cw.emit("'Source+Sans+Pro:400,400i,700,700i',") cw.emit("'Raleway:400,400i,700,700i',") cw.emit("'Noto+Serif:400,400i,700,700i',") cw.emit("'Arvo:400,400i,700,700i',") cw.emit(") !default;") cw.emit("$o-theme-font-names: (") with cw.indent(): cw.emit("'Bootstrap',") cw.emit("'Open Sans',") cw.emit("'Source Sans Pro',") cw.emit("'Raleway',") cw.emit("'Noto Serif',") cw.emit("'Arvo',") cw.emit(") !default;") cw.emit("$o-theme-font-number: 1 !default;") cw.emit("$o-theme-headings-font-number: 1 !default;") cw.emit("$o-theme-buttons-font-number: 1 !default;") cw.emit("$o-theme-navbar-font-number: 1 !default;") self.code_generator_data.write_file_str(file_path, cw.render())
def set_module_python_file(self, module): super(CodeGeneratorWriter, self).set_module_python_file(module) if not module.theme_website: return cw = CodeWriter() for line in MODEL_HEAD: str_line = line.strip() cw.emit(str_line) cw.emit() cw.emit(f"class {self._get_class_name(module.name)}(models.AbstractModel):") with cw.indent(): cw.emit("_inherit = 'theme.utils'") cw.emit() with cw.indent(): cw.emit(f"def _{module.name}_post_copy(self, mod):") with cw.indent(): cw.emit("self.disable_view('website_theme_install.customize_modal')") file_path = os.path.join(self.code_generator_data.models_path, f"{module.name}.py") self.code_generator_data.write_file_str(file_path, cw.render())
def _set_portal_controller_file(self, module): """ Function to set the module hook file :param module: :return: """ cw = CodeWriter() cw.emit("from collections import OrderedDict") cw.emit("from operator import itemgetter") cw.emit("") cw.emit("from odoo import http, _") cw.emit("from odoo.exceptions import AccessError, MissingError") cw.emit("from odoo.http import request") cw.emit( "from odoo.addons.portal.controllers.portal import CustomerPortal, pager as portal_pager" ) cw.emit("from odoo.tools import groupby as groupbyelem") cw.emit("") cw.emit("from odoo.osv.expression import OR") cw.emit("") cw.emit("") cw.emit("class CustomerPortal(CustomerPortal):") cw.emit("") with cw.indent(): cw.emit("def _prepare_portal_layout_values(self):") with cw.indent(): cw.emit("values = super(CustomerPortal, self)._prepare_portal_layout_values()") for model in module.o2m_models: cw.emit( f"values['{self._fmt_underscores(model.model)}_count'] = request.env['{model.model}']." f"search_count([])" ) cw.emit("return values") cw.emit("") for model in module.o2m_models: has_group_by = False with cw.indent(): cw.emit("# ------------------------------------------------------------") cw.emit(f"# My {self._fmt_title(model.model)}") cw.emit("# ------------------------------------------------------------") cw.emit( f"def _{self._fmt_underscores(model.model)}_get_page_view_values(self, {self._fmt_underscores(model.model)}, " f"access_token, **kwargs):" ) with cw.indent(): cw.emit("values = {") with cw.indent(): cw.emit(f"'page_name': '{self._fmt_underscores(model.model)}',") cw.emit( f"'{self._fmt_underscores(model.model)}': {self._fmt_underscores(model.model)}," ) # MATHBEN ADDED cw.emit("'user': request.env.user") with cw.indent(): cw.emit("}") cw.emit( f"return self._get_page_view_values({self._fmt_underscores(model.model)}, access_token, values, " f"'my_{self._fmt_underscores(model.model)}s_history', False, **kwargs)" ) cw.emit("") with cw.indent(): cw.emit( f"@http.route(['/my/{self._fmt_underscores(model.model)}s', " f"'/my/{self._fmt_underscores(model.model)}s/page/<int:page>'], type='http', auth=\"user\", " f"website=True)" ) # cw.emit(f"def portal_my_{_fmt_underscores(model.model)}s(self, page=1, date_begin=None, date_end=None, " # f"sortby=None, **kw):") # MATHBEN ADDED cw.emit( f"def portal_my_{self._fmt_underscores(model.model)}s(self, page=1, date_begin=None, date_end=None, " f"sortby=None, filterby=None, search=None, search_in='content', **kw):" ) # MATHBEN NEED THIS FOR NEXT MODEL IF ONE DEPEND TO ANOTHER ONE # f"sortby=None, filterby=None, search=None, search_in='content', groupby='project', **kw):") with cw.indent(): cw.emit("values = self._prepare_portal_layout_values()") cw.emit(f"{self._fmt_camel(model.model)} = request.env['{model.model}']") cw.emit("domain = []") cw.emit("") with cw.indent(): with cw.indent(): cw.emit("searchbar_sortings = {") with cw.indent(): cw.emit("'date': {'label': _('Newest'), 'order': 'create_date desc'},") cw.emit("'name': {'label': _('Name'), 'order': 'name'},") # MATHBEN NEEDED BY TASK # cw.emit("'name': {'label': _('Title'), 'order': 'name'},") # cw.emit("'stage': {'label': _('Stage'), 'order': 'stage_id'},") # cw.emit("'update': {'label': _('Last Stage Update'), 'order': 'date_last_stage_update desc'},") with cw.indent(): cw.emit("}") cw.emit("searchbar_filters = {") with cw.indent(): cw.emit("'all': {'label': _('All'), 'domain': []},") with cw.indent(): cw.emit("}") cw.emit("searchbar_inputs = {") # MATHBEN REMOVED, WAS in task # with cw.indent(): # cw.emit( # "'content': {'input': 'content', 'label': _('Search <span class=\"nolabel\"> (in Content)</span>')},") # cw.emit("'message': {'input': 'message', 'label': _('Search in Messages')},") # cw.emit("'customer': {'input': 'customer', 'label': _('Search in Customer')},") # cw.emit("'stage': {'input': 'stage', 'label': _('Search in Stages')},") # cw.emit("'all': {'input': 'all', 'label': _('Search in All')},") with cw.indent(): cw.emit("}") cw.emit("searchbar_groupby = {") # MATHBEN REMOVED, WAS in task # with cw.indent(): # cw.emit("'none': {'input': 'none', 'label': _('None')},") # cw.emit("'project': {'input': 'project', 'label': _('Project')},") with cw.indent(): cw.emit("}") cw.emit("") with cw.indent(): with cw.indent(): pass # MATHBEN WAS FOR TASK NOT USE IN PROJECT # cw.emit("# extends filterby criteria with project the customer has access to") # cw.emit("projects = request.env['project.project'].search([])") # cw.emit("for project in projects:") # with cw.indent(): # cw.emit("searchbar_filters.update({") # with cw.indent(): # cw.emit( # "str(project.id): {'label': project.name, 'domain': [('project_id', '=', project.id)]}") # with cw.indent(): # cw.emit("})") pass # cw.emit("") # with cw.indent(): # with cw.indent(): # cw.emit("# extends filterby criteria with project (criteria name is the project id)") # cw.emit("# Note: portal users can't view projects they don't follow") # cw.emit( # "project_groups = request.env['project.task'].read_group([('project_id', 'not in', projects.ids)], " # "['project_id'], ['project_id'])") # with cw.indent(): # cw.emit("for group in project_groups:") # with cw.indent(): # cw.emit("proj_id = group['project_id'][0] if group['project_id'] else False") # cw.emit("proj_name = group['project_id'][1] if group['project_id'] else _('Others')") # cw.emit("searchbar_filters.update({") # with cw.indent(): # cw.emit("str(proj_id): {'label': proj_name, 'domain': [('project_id', '=', proj_id)]}") # with cw.indent(): # cw.emit("})") # cw.emit("") with cw.indent(): with cw.indent(): cw.emit("# default sort by value") cw.emit("if not sortby:") with cw.indent(): cw.emit("sortby = 'date'") with cw.indent(): cw.emit("order = searchbar_sortings[sortby]['order']") cw.emit("# default filter by value") cw.emit("if not filterby:") with cw.indent(): cw.emit("filterby = 'all'") with cw.indent(): cw.emit("domain = searchbar_filters[filterby]['domain']") cw.emit("") with cw.indent(): with cw.indent(): cw.emit("# search") cw.emit("if search and search_in:") with cw.indent(): cw.emit("search_domain = []") pass # MATHBEN REMOVE IT, NOT USED IN PROJECT # cw.emit("if search_in in ('content', 'all'):") # with cw.indent(): # cw.emit( # "search_domain = OR([search_domain, ['|', ('name', 'ilike', search), ('description', 'ilike', search)]])") # with cw.indent(): # cw.emit("if search_in in ('customer', 'all'):") # with cw.indent(): # cw.emit("search_domain = OR([search_domain, [('partner_id', 'ilike', search)]])") # with cw.indent(): # cw.emit("if search_in in ('message', 'all'):") # with cw.indent(): # cw.emit("search_domain = OR([search_domain, [('message_ids.body', 'ilike', search)]])") # with cw.indent(): # cw.emit("if search_in in ('stage', 'all'):") # with cw.indent(): # cw.emit("search_domain = OR([search_domain, [('stage_id', 'ilike', search)]])") with cw.indent(): cw.emit("domain += search_domain") with cw.indent(): with cw.indent(): cw.emit("# archive groups - Default Group By 'create_date'") cw.emit(f"archive_groups = self._get_archive_groups('{model.model}', domain)") cw.emit("if date_begin and date_end:") with cw.indent(): cw.emit( "domain += [('create_date', '>', date_begin), ('create_date', '<=', date_end)]" ) with cw.indent(): cw.emit(f"# {self._fmt_underscores(model.model)}s count") cw.emit( f"{self._fmt_underscores(model.model)}_count = {self._fmt_camel(model.model)}.search_count(domain)" ) cw.emit("# pager") cw.emit("pager = portal_pager(") with cw.indent(): cw.emit(f'url="/my/{self._fmt_underscores(model.model)}s",') cw.emit( "url_args={'date_begin': date_begin, 'date_end': date_end, 'sortby': sortby, 'filterby': " "filterby, 'search_in': search_in, 'search': search}," ) cw.emit(f"total={self._fmt_underscores(model.model)}_count,") cw.emit("page=page,") cw.emit("step=self._items_per_page") with cw.indent(): cw.emit(")") cw.emit("# content according to pager and archive selected") # MATHBEN NOT IN PROJECT, BUT TASK # cw.emit("if groupby == 'project':") # with cw.indent(): # cw.emit("order = \"project_id, %s\" % order " # "# force sort on project first to group by project in view") cw.emit("") with cw.indent(): with cw.indent(): cw.emit("# content according to pager and archive selected") cw.emit( f"{self._fmt_underscores(model.model)}s = {self._fmt_camel(model.model)}.search(domain, order=order, " f"limit=self._items_per_page, offset=pager['offset'])" ) # MATHBEN LAST LINE, TASK WAS offset=(page - 1) * self._items_per_page cw.emit( f"request.session['my_{self._fmt_underscores(model.model)}s_history'] = " f"{self._fmt_underscores(model.model)}s.ids[:100]" ) # MATHBEN NEXT BLOCK 43 COMMENT TO NEXT LINE # cw.emit("if groupby == 'project':") # with cw.indent(): # cw.emit( # "grouped_tasks = [request.env['project.task'].concat(*g) for k, g in groupbyelem(tasks, itemgetter('project_id'))]") pass # with cw.indent(): # cw.emit("else:") # with cw.indent(): # cw.emit("grouped_tasks = [tasks]") # MATHBEN END BLOCK 43 cw.emit("") with cw.indent(): with cw.indent(): cw.emit("values.update({") with cw.indent(): cw.emit("'date': date_begin,") cw.emit("'date_end': date_end,") # MATHBEN WAS IN TASK # cw.emit("'grouped_tasks': grouped_tasks,") # GROUPED_TASKS CAN REPLACE PROJECTS cw.emit( f"'{self._fmt_underscores(model.model)}s': {self._fmt_underscores(model.model)}s," ) cw.emit(f"'page_name': '{self._fmt_underscores(model.model)}',") cw.emit("'archive_groups': archive_groups,") cw.emit(f"'default_url': '/my/{self._fmt_underscores(model.model)}s',") cw.emit("'pager': pager,") cw.emit("'searchbar_sortings': searchbar_sortings,") cw.emit("'searchbar_groupby': searchbar_groupby,") cw.emit("'searchbar_inputs': searchbar_inputs,") cw.emit("'search_in': search_in,") if has_group_by: cw.emit("'groupby': groupby,") cw.emit( "'searchbar_filters': OrderedDict(sorted(searchbar_filters.items()))," ) cw.emit("'sortby': sortby,") cw.emit("'filterby': filterby,") with cw.indent(): cw.emit("})") cw.emit( f'return request.render("{module.name}.portal_my_{self._fmt_underscores(model.model)}s", values)' ) cw.emit("") with cw.indent(): cw.emit( f"@http.route(['/my/{self._fmt_underscores(model.model)}/<int:{self._fmt_underscores(model.model)}_id>'], " f"type='http', auth=\"public\", website=True)" ) cw.emit( f"def portal_my_{self._fmt_underscores(model.model)}(self, {self._fmt_underscores(model.model)}_id=None, " f"access_token=None, **kw):" ) with cw.indent(): cw.emit("try:") with cw.indent(): cw.emit( f"{self._fmt_underscores(model.model)}_sudo = self._document_check_access('{model.model}', " f"{self._fmt_underscores(model.model)}_id, access_token)" ) with cw.indent(): cw.emit("except (AccessError, MissingError):") with cw.indent(): cw.emit("return request.redirect('/my')") cw.emit("") with cw.indent(): with cw.indent(): if "attachment_ids" in [a.name for a in model.field_id]: cw.emit( "# ensure attachment are accessible with access token inside template" ) cw.emit( f"for attachment in {self._fmt_underscores(model.model)}_sudo.attachment_ids:" ) with cw.indent(): cw.emit("attachment.generate_access_token()") with cw.indent(): cw.emit( f"values = self._{self._fmt_underscores(model.model)}_get_page_view_values(" f"{self._fmt_underscores(model.model)}_sudo, access_token, **kw)" ) cw.emit( f'return request.render("{module.name}.portal_my_{self._fmt_underscores(model.model)}", values)' ) cw.emit("") out = cw.render() l_model = out.split("\n") file_path = f"{self.code_generator_data.controllers_path}/portal.py" self.code_generator_data.write_file_lst_content(file_path, l_model)