From 73758d7883a0f8bcffa50593d42bd0fbaa153225 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 12 Oct 2022 16:33:26 +0530 Subject: [PATCH] feat: search in translated title, if we show title (#17828) (#18395) * refactor: use meta.translated_doctype * refactor: get_title_field_query * feat: search in title, if we show title * refactor: build_for_autosuggest * style: black * fix: don't order translated doctypes by untranslated relevance * feat: match all fields for translated doctypes * feat: translate all fields in description, remove redundant title * refactor: title in link * fix: show name in description for title links (cherry picked from commit 3d17e1589e30e659985afa0ab70f3408bea08eb9) Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- frappe/core/doctype/doctype/doctype.json | 5 +- frappe/desk/search.py | 106 +++++++----------- frappe/public/js/frappe/form/controls/link.js | 12 +- 3 files changed, 56 insertions(+), 67 deletions(-) diff --git a/frappe/core/doctype/doctype/doctype.json b/frappe/core/doctype/doctype/doctype.json index bfa91cea75..0b2ced43da 100644 --- a/frappe/core/doctype/doctype/doctype.json +++ b/frappe/core/doctype/doctype/doctype.json @@ -320,7 +320,8 @@ "depends_on": "eval:!doc.istable", "fieldname": "title_field", "fieldtype": "Data", - "label": "Title Field" + "label": "Title Field", + "mandatory_depends_on": "eval:doc.show_title_field_in_link" }, { "depends_on": "eval:!doc.istable", @@ -687,7 +688,7 @@ "link_fieldname": "reference_doctype" } ], - "modified": "2022-08-24 06:42:27.779699", + "modified": "2022-09-02 12:05:59.589751", "modified_by": "Administrator", "module": "Core", "name": "DocType", diff --git a/frappe/desk/search.py b/frappe/desk/search.py index 53a8c39ee5..c4a94074ba 100644 --- a/frappe/desk/search.py +++ b/frappe/desk/search.py @@ -8,7 +8,6 @@ import re import frappe from frappe import _, is_whitelisted from frappe.permissions import has_permission -from frappe.translate import get_translated_doctypes from frappe.utils import cint, cstr, unique @@ -150,10 +149,6 @@ def search_widget( filters = [] or_filters = [] - translated_doctypes = frappe.cache().hget( - "translated_doctypes", "doctypes", get_translated_doctypes - ) - # build from doctype if txt: field_types = [ @@ -175,7 +170,7 @@ def search_widget( for f in search_fields: fmeta = meta.get_field(f.strip()) - if (doctype not in translated_doctypes) and ( + if not meta.translated_doctype and ( f == "name" or (fmeta and fmeta.fieldtype in field_types) ): or_filters.append([doctype, f.strip(), "like", f"%{txt}%"]) @@ -191,26 +186,25 @@ def search_widget( fields = list(set(fields + json.loads(filter_fields))) formatted_fields = [f"`tab{meta.name}`.`{f.strip()}`" for f in fields] - title_field_query = get_title_field_query(meta) - # Insert title field query after name - if title_field_query: - formatted_fields.insert(1, title_field_query) - - # find relevance as location of search term from the beginning of string `name`. used for sorting results. - formatted_fields.append( - """locate({_txt}, `tab{doctype}`.`name`) as `_relevance`""".format( - _txt=frappe.db.escape((txt or "").replace("%", "").replace("@", "")), - doctype=doctype, - ) - ) + if meta.show_title_field_in_link: + formatted_fields.insert(1, f"`tab{meta.name}`.{meta.title_field} as `label`") # In order_by, `idx` gets second priority, because it stores link count from frappe.model.db_query import get_order_by order_by_based_on_meta = get_order_by(doctype, meta) # 2 is the index of _relevance column - order_by = f"_relevance, {order_by_based_on_meta}, `tab{doctype}`.idx desc" + order_by = f"{order_by_based_on_meta}, `tab{doctype}`.idx desc" + + if not meta.translated_doctype: + formatted_fields.append( + """locate({_txt}, `tab{doctype}`.`name`) as `_relevance`""".format( + _txt=frappe.db.escape((txt or "").replace("%", "").replace("@", "")), + doctype=doctype, + ) + ) + order_by = f"_relevance, {order_by}" ptype = "select" if frappe.only_has_select_perm(doctype) else "read" ignore_permissions = ( @@ -219,16 +213,13 @@ def search_widget( else (cint(ignore_user_permissions) and has_permission(doctype, ptype=ptype)) ) - if doctype in translated_doctypes: - page_length = None - values = frappe.get_list( doctype, filters=filters, fields=formatted_fields, or_filters=or_filters, limit_start=start, - limit_page_length=page_length, + limit_page_length=None if meta.translated_doctype else page_length, order_by=order_by, ignore_permissions=ignore_permissions, reference_doctype=reference_doctype, @@ -236,12 +227,15 @@ def search_widget( strict=False, ) - if doctype in translated_doctypes: + if meta.translated_doctype: # Filtering the values array so that query is included in very element values = ( - v - for v in values - if re.search(f"{re.escape(txt)}.*", _(v.name if as_dict else v[0]), re.IGNORECASE) + result + for result in values + if any( + re.search(f"{re.escape(txt)}.*", _(cstr(value)) or "", re.IGNORECASE) + for value in (result.values() if as_dict else result) + ) ) # Sorting the values array so that relevant results always come first @@ -250,12 +244,14 @@ def search_widget( values = sorted(values, key=lambda x: relevance_sorter(x, txt, as_dict)) # remove _relevance from results - if as_dict: - for r in values: - r.pop("_relevance") - frappe.response["values"] = values - else: - frappe.response["values"] = [r[:-1] for r in values] + if not meta.translated_doctype: + if as_dict: + for r in values: + r.pop("_relevance") + else: + values = [r[:-1] for r in values] + + frappe.response["values"] = values def get_std_fields_list(meta, key): @@ -275,39 +271,23 @@ def get_std_fields_list(meta, key): return sflist -def get_title_field_query(meta): - title_field = meta.title_field if meta.title_field else None - show_title_field_in_link = ( - meta.show_title_field_in_link if meta.show_title_field_in_link else None - ) - field = None - - if title_field and show_title_field_in_link: - field = f"`tab{meta.name}`.{title_field} as `label`" - - return field - +def build_for_autosuggest(res: list[tuple], doctype: str) -> list[dict]: + def to_string(parts): + return ", ".join( + unique(_(cstr(part)) if meta.translated_doctype else cstr(part) for part in parts if part) + ) -def build_for_autosuggest(res, doctype): results = [] meta = frappe.get_meta(doctype) - if not (meta.title_field and meta.show_title_field_in_link): - for r in res: - r = list(r) - results.append({"value": r[0], "description": ", ".join(unique(cstr(d) for d in r[1:] if d))}) - + if meta.show_title_field_in_link: + for item in res: + item = list(item) + label = item[1] # use title as label + item[1] = item[0] # show name in description instead of title + del item[2] # remove redundant title ("label") value + results.append({"value": item[0], "label": label, "description": to_string(item[1:])}) else: - title_field_exists = meta.title_field and meta.show_title_field_in_link - _from = 2 if title_field_exists else 1 # to exclude title from description if title_field_exists - for r in res: - r = list(r) - results.append( - { - "value": r[0], - "label": r[1] if title_field_exists else None, - "description": ", ".join(unique(cstr(d) for d in r[_from:] if d)), - } - ) + results.extend({"value": item[0], "description": to_string(item[1:])} for item in res) return results @@ -383,7 +363,7 @@ def get_user_groups(): def get_link_title(doctype, docname): meta = frappe.get_meta(doctype) - if meta.title_field and meta.show_title_field_in_link: + if meta.show_title_field_in_link: return frappe.db.get_value(doctype, docname, meta.title_field) return docname diff --git a/frappe/public/js/frappe/form/controls/link.js b/frappe/public/js/frappe/form/controls/link.js index 9a7cc0f2c4..076e343f22 100644 --- a/frappe/public/js/frappe/form/controls/link.js +++ b/frappe/public/js/frappe/form/controls/link.js @@ -89,10 +89,13 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat is_translatable() { return in_list(frappe.boot?.translated_doctypes || [], this.get_options()); } + is_title_link() { + return in_list(frappe.boot.link_title_doctypes, this.get_options()); + } async set_link_title(value) { const doctype = this.get_options(); - if (!doctype || !in_list(frappe.boot.link_title_doctypes, doctype)) { + if (!doctype || !this.is_title_link()) { this.translate_and_set_input_value(value, value); return; } @@ -207,7 +210,12 @@ frappe.ui.form.ControlLink = class ControlLink extends frappe.ui.form.ControlDat let _label = me.get_translated(d.label); let html = d.html || "" + _label + ""; - if (d.description && d.value !== d.description) { + if ( + d.description && + // for title links, we want to inlude the value in the description + // because it will not visible otherwise + (me.is_title_link() || d.value !== d.description) + ) { html += '
' + __(d.description) + ""; } return $("
  • ")