Exemple #1
0
def access_repr(access):
    # type: (odoo.models.IrModelAccess) -> t.Text
    parts = []
    parts.append(color.record_header(access))
    parts.append(color.display_name(access.display_name))
    parts.append(
        color.record(access.group_id.name) +
        util.xml_id_tag(access.group_id) if access.group_id else color.green.
        bold("Everyone"))
    parts.append(_crud_format(access))
    return "\n".join(parts)
Exemple #2
0
def rule_repr(rule):
    # type: (odoo.models.IrRule) -> t.Text
    parts = []
    parts.append(color.record_header(rule))
    parts.append(color.display_name(rule.display_name))
    groups = ", ".join(
        color.record(group.name) + util.xml_id_tag(group)
        for group in rule.groups)
    if not groups:
        parts.append(
            color.green.bold("Everyone") if rule["global"] else color.red.
            bold("No-one"))
    else:
        parts.append(groups)
    parts.append(_crud_format(rule))
    if rule.domain_force not in {
            None, False, "[]", "[(1, '=', 1)]", '[(1, "=", 1)]'
    }:
        assert rule.domain_force
        parts.append(
            color.highlight(_domain_format(rule.env, rule.domain_force)))
    parts.extend(sources.format_sources(sources.find_source(rule)))
    return "\n".join(parts)
Exemple #3
0
def record_repr(obj):
    # type: (BaseModel) -> t.Text
    """Display all of a record's fields."""
    obj = util.unwrap(obj)

    if not hasattr(obj, "_ids"):
        return repr(obj)
    elif not obj:
        return u"{}[]".format(obj._name)
    elif len(obj) > 1:
        return color.basic_render_record(obj)

    if obj.env.cr.closed:
        return color.basic_render_record(obj) + " (closed cursor)"

    field_names = sorted(field for field in obj._fields
                         if field not in odoo_repl.models.FIELD_BLACKLIST
                         and not obj._fields[field].related)
    max_len = max(len(f) for f in field_names) if field_names else 0
    parts = []

    parts.append(color.record_header(obj))
    name = obj.sudo().display_name
    default_name = "{},{}".format(obj._name, obj.id)
    if name and name != default_name:
        parts.append(color.display_name(name))

    if not obj.exists():
        parts.append(color.missing("Missing"))
        return "\n".join(parts)

    # Odoo precomputes a field for up to 200 records at a time.
    # This can be a problem if we're only interested in one of them.
    # So we do our best to disable it.

    # For Odoo 8, we do everything in a separate env where the ID cache is
    # empty. We make a separate env by changing the context. This has the added
    # advantage of informing models that they're running in odoo_repl, in case
    # they care. In _color_repr we clear the cache in case it got filled.

    # For Odoo 10-13, we slice the record. Odoo tries to be smart and narrows
    # the prefetch cache if we slice while keeping it when iterating.

    # I don't know what Odoo 9 does but I hope it's one of the above.

    # TODO: When .print_()ing a recordset we do want prefetching.

    if isinstance(obj.id, odoo.models.NewId):
        # Cache gets wonky if we start a new environment
        no_prefetch_obj = obj
    else:
        no_prefetch_obj = obj.with_context(odoo_repl=True)[:]

    for field in field_names:
        parts.append("{}: ".format(color.field(field)) +
                     (max_len - len(field)) * " " +
                     _color_repr(no_prefetch_obj, field))

    history_lines = _get_create_write_history(obj.sudo())
    if history_lines:
        parts.append("")
        parts.extend(history_lines)

    src = sources.find_source(obj)
    if src:
        parts.append("")
        parts.extend(sources.format_sources(src))

    return "\n".join(parts)
Exemple #4
0
def model_repr(obj):
    # type: (t.Union[ModelProxy, BaseModel]) -> t.Text
    """Summarize a model's fields."""
    if isinstance(obj, ModelProxy) and obj._real is None:
        return repr(obj)
    obj = util.unwrap(obj)

    field_names = []
    delegated = []
    for field in sorted(obj._fields):
        if field in FIELD_BLACKLIST:
            continue
        if getattr(obj._fields[field], "related", False):
            delegated.append(obj._fields[field])
            continue
        field_names.append(field)
    max_len = max(len(f) for f in field_names) if field_names else 0
    parts = []

    original_module = obj._module
    for parent in type(obj).__bases__:
        if getattr(parent, "_name", None) == obj._name:
            original_module = getattr(parent, "_module", original_module)

    parts.append(color.header(obj._name))
    if obj._transient:
        parts[-1] += " (transient)"
    if getattr(obj, "_abstract", False):
        parts[-1] += " (abstract)"
    elif not obj._auto:
        parts[-1] += " (no automatic table)"
    if getattr(obj, "_description", False) and obj._description != obj._name:
        parts.append(color.display_name(obj._description))
    if getattr(obj, "_inherits", False):
        for model_name, field_name in obj._inherits.items():
            parts.append("Inherits from {} through {}".format(
                color.model(model_name), color.field(field_name)))
    inherits = _find_inheritance(obj)
    if inherits:
        # Giving this a very similar message to the one for _inherits feels dirty
        # But then _inherit is already very similar to _inherits so maybe it's ok
        parts.append("Inherits from {}".format(", ".join(
            color.model(inherit) for inherit in sorted(inherits))))
    docs = list(
        sources.find_docs((util.module(cls), cls)
                          for cls in type(obj).__bases__
                          if getattr(cls, "_name", obj._name) == obj._name))
    parts.extend(sources.format_docs(docs))

    src = sources.find_source(obj)

    by_module = collections.defaultdict(list)
    for field in field_names:
        f_obj = obj._fields[field]
        rep = format_single_field(f_obj, max_len=max_len)
        f_module = sources.find_field_module(f_obj) or original_module
        by_module[f_module].append(rep)

    ordered_modules = [original_module]
    for src_item in reversed(src):
        if src_item.module not in ordered_modules:
            ordered_modules.append(src_item.module)
    for module in by_module:
        if module not in ordered_modules:
            ordered_modules.append(module)

    for module in ordered_modules:
        if module not in by_module:
            continue
        parts.append("")
        parts.append("{}:".format(color.module(module)))
        parts.extend(by_module[module])

    if delegated:
        buckets = collections.defaultdict(
            list)  # type: t.DefaultDict[t.Tuple[t.Text, ...], t.List[t.Text]]
        for f_obj in delegated:
            assert f_obj.related
            buckets[tuple(f_obj.related[:-1])].append(
                color.field(f_obj.name) if f_obj.related[-1] == f_obj.name else
                "{} (.{})".format(color.field(f_obj.name), f_obj.related[-1]))
        parts.append("")
        for related_field, field_names in buckets.items():
            # TODO: figure out name of model of real field
            parts.append("Delegated to {}: {}".format(
                color.yellow.bold(".".join(related_field)),
                ", ".join(field_names)))
    parts.append("")
    parts.extend(sources.format_sources(src))
    return "\n".join(parts)
Exemple #5
0
def addon_repr(addon):
    # type: (Addon) -> t.Text
    # TODO: A lot of the most interesting information is at the top so you have
    # to scroll up
    # Ideas:
    # - Put it at the bottom instead
    # - Don't show the README by default

    try:
        addon.manifest
    except RuntimeError:
        return repr(addon)

    defined_models = (addon._env["ir.model"].browse(
        addon._env["ir.model.data"].search([
            ("model", "=", "ir.model"), ("module", "=", addon._module)
        ]).mapped("res_id")).mapped("model"))

    state = addon.record.state
    if (state == "installed"
            and addon.record.installed_version != addon.manifest.version):
        state += " (out of date)"

    if state == "installed":
        state = color.green.bold(state.capitalize())
    elif not state:
        state = color.yellow.bold("???")
    elif state in ("uninstallable", "uninstalled"):
        state = color.red.bold(state.capitalize())
    else:
        state = color.yellow.bold(state.capitalize())

    description = addon.manifest.description
    if isinstance(addon.manifest.author, Text):
        author = addon.manifest.author
    else:
        author = ", ".join(addon.manifest.author)

    parts = []
    parts.append("{} {} by {}".format(color.module(addon._module),
                                      addon.manifest.version, author))
    parts.append(util.link_for_record(addon.record))
    parts.append(addon.path)
    parts.append(state)
    parts.append(color.display_name(addon.manifest.name))
    parts.append(addon.manifest.summary)

    def format_depends(pretext, modules):
        # type: (t.Text, odoo.models.IrModuleModule) -> None
        if modules:
            names = map(_color_state, sorted(modules,
                                             key=lambda mod: mod.name))
            parts.append("{}: {}".format(pretext, ", ".join(names)))

    # TODO: Indirect dependencies are a bit noisy, when/how do we show them?
    direct, _indirect = addon._get_depends()
    format_depends("Depends", direct)
    # format_depends("Indirectly depends", indirect)
    r_direct, _r_indirect = addon._get_rdepends()
    format_depends("Dependents", r_direct)
    # format_depends("Indirect dependents", r_indirect)

    if defined_models:
        parts.append("Defines: {}".format(", ".join(
            map(color.model, defined_models))))
    if description:
        parts.append("")
        # rst2ansi might be better here
        # (https://pypi.org/project/rst2ansi/)
        parts.append(color.highlight(description, "rst"))

    return "\n".join(parts)