def _extract_decorator_node(self, model_name: str, model: ModelDefinition, node: ast.Call): decorator_name = next(iter_name(node.func, reverse=True), None) if decorator_name not in FIELD_DECORATORS and decorator_name != "returns": return if (decorator_name == "returns" and len(node.args) >= 1 and is_string_node(node.args[0])): model_name = get_string_node_value(node.args[0]) if model_name != "self": yield _model_ref( model.addon, model.path, ASTNodePosition.from_node(node.args[0]).start_pos_col_1, model_name, ) return for arg in node.args: if not is_string_node(arg): continue field_name = get_string_node_value(arg).split(".")[0] if field_name: yield ExternalIDReference( model.addon, UNKNOWN, _field_record_id(model.addon.odoo_version, model_name, field_name), "ir.model.fields", Location(model.path, ASTNodePosition.from_node(arg).start_pos_col_1), )
def from_collection(node: typing.Union[ast.List, ast.Tuple]): for key, position in _iter_selection_keys(node): yield ExternalID( field.model.addon, field.model.addon.name, _selection_key_id(model_name, field.name, key), "ir.model.fields.selection", Location(field.model.path, position), )
def on_addon(self, addon: Addon): # This is a special case, automatically created for all addons: # https://git.io/JeqKX. yield ExternalID( addon, "base", f"module_{addon.name}", "ir.module.module", Location(addon.path), )
def on_csv_row(self, csv_row: CSVRow): model = csv_row.path.stem addon, path, row = csv_row.addon, csv_row.path, csv_row.row yield ExternalIDReference(addon, UNKNOWN, _model_record_id(model), "ir.model", Location(path)) if path not in self._csv_external_id_fields: for field_name in row: if (field_name.split(":")[-1] == "id" or field_name.split("/")[-1] == "id"): self._csv_external_id_fields[path].add(field_name) # Add references to fields from the CSV file header. field_external_id = _field_record_id( addon.odoo_version, model, (field_name[:-3] if field_name.endswith( (":id", "/id")) else field_name), ) yield ExternalIDReference( addon, UNKNOWN, field_external_id, "ir.model.fields", Location(path, Position(1)), ) for field_name in self._csv_external_id_fields[path]: # TODO: Add support for KNOWN_FIELD_MODELS. for external_id in _csv_split_external_id(csv_row.row[field_name]): addon_name, record_id = split_external_id(external_id) cls_ = ExternalID if field_name == "id" else ExternalIDReference yield cls_( csv_row.addon, addon_name, record_id, model if field_name == "id" else UNKNOWN, Location(csv_row.path, Position(csv_row.line_no)), )
def _ref_or_def(addon, filename, position: typing.Union[int, Position], external_id, model) -> typing.Union[ExternalID, ExternalIDReference]: addon_name, record_id = split_external_id(external_id) cls_ = ExternalIDReference if not addon_name or addon_name == addon.name: cls_ = ExternalID return cls_( addon, addon_name, record_id, model, Location( filename, Position(position) if isinstance(position, int) else position), )
def _ref( addon, filename, position: typing.Union[int, Position], external_id, model, unknown_addon=False, ) -> ExternalIDReference: addon_name, record_id = split_external_id(external_id) return ExternalIDReference( addon, UNKNOWN if unknown_addon else addon_name, record_id, model, Location( filename, Position(position) if isinstance(position, int) else position), )
def _extract_sql_constraint(self, model_name: str, model: ModelDefinition): for name, node in iter_model_params(model.node): if name != "_sql_constraints" or not isinstance( node, SQL_CONSTRAINT_NODES): continue for constraint in node.elts: if (isinstance(constraint, SQL_CONSTRAINT_NODES) and len(constraint.elts) == 3 and is_string_node(constraint.elts[0])): constraint_name = get_string_node_value(constraint.elts[0]) position = ASTNodePosition.from_node( constraint.elts[0]).start_pos_col_1 yield ExternalID( model.addon, model.addon.name, _constraint_id(model_name, constraint_name), "ir.model.constraint", Location(model.path, position), )
def _extract_xml_record(self, addon, filename, tree): # <record> operation. for record in tree.xpath("//record"): record_model, record_id = record.attrib["model"], record.get("id") if record_model: yield _model_ref(addon, filename, record.sourceline, record_model) if record_id: yield _ref_or_def(addon, filename, record.sourceline, record_id, record_model) # <field> operation. for field in record.iterchildren(tag="field"): field_name = field.attrib["name"] field_external_id = _field_record_id(addon.odoo_version, record_model, field_name) yield ExternalIDReference( addon, UNKNOWN, field_external_id, "ir.model.fields", Location(filename, Position(field.sourceline)), ) ref = field.get("ref") if ref: ref_model = KNOWN_FIELD_MODELS.get(record_model, {}).get( field_name, UNKNOWN) yield _ref(addon, filename, field.sourceline, ref, ref_model) for attr_name in ("eval", "search"): yield from self._get_ref_from_eval(addon, filename, field.sourceline, field.get(attr_name)) # View-specific tags. if record_model != "ir.ui.view": continue arch = _get_view_arch(record) if arch is None: continue for button in arch.xpath(".//button[@type='action' and @name]"): button_name = button.get("name") if button_name: yield _ref( addon, filename, button.sourceline, remove_old_style_format(button_name), # Find out if we can have a model here. UNKNOWN, ) for el in arch.xpath(".//*[@groups]"): groups = el.get("groups") for group in split_groups(groups): yield _ref(addon, filename, el.sourceline, group, "res.groups")