From 02cebdd7d34bf3be21108df183b75c13c8ba60f9 Mon Sep 17 00:00:00 2001 From: Himanshu Warekar Date: Thu, 3 Oct 2019 16:43:36 +0530 Subject: [PATCH] fix: use frappe.tags --- frappe/database/database.py | 20 +++++ frappe/desk/doctype/tag/tag.py | 86 +++++++++++++++++-- frappe/desk/doctype/tag_link/tag_link.json | 45 +++++----- frappe/desk/form/load.py | 6 +- frappe/desk/reportview.py | 4 +- frappe/model/delete_doc.py | 4 +- frappe/patches.txt | 2 +- frappe/patches/v12_0/setup_global_tags.py | 24 +----- frappe/public/js/frappe/desk.js | 2 +- frappe/public/js/frappe/ui/filters/filter.js | 1 - frappe/public/js/frappe/ui/tag_editor.js | 4 +- .../js/frappe/ui/toolbar/awesome_bar.js | 4 +- .../js/frappe/ui/toolbar/global_tags.js | 18 ++-- frappe/public/js/frappe/ui/toolbar/search.js | 6 +- frappe/utils/global_tags.py | 80 ----------------- 15 files changed, 153 insertions(+), 153 deletions(-) delete mode 100644 frappe/utils/global_tags.py diff --git a/frappe/database/database.py b/frappe/database/database.py index a1b8d390a9..2ff8094035 100644 --- a/frappe/database/database.py +++ b/frappe/database/database.py @@ -961,6 +961,26 @@ class Database(object): frappe.flags.touched_tables = set() frappe.flags.touched_tables.update(tables) + def bulk_insert(self, doctype, fields, values): + """ + Insert multiple records at a time + + :param doctype: Doctype name + :param fields: list of fields + :params values: list of list of values + """ + insert_list = [] + fields = ", ".join(["`"+field+"`" for field in fields]) + + for idx, value in enumerate(values): + insert_list.append(tuple(value)) + if idx and (idx%10000 == 0 or idx < len(values)-1): + self.sql("""INSERT INTO `tab{doctype}` ({fields}) VALUES {values}""".format( + doctype=doctype, + fields=fields, + values=", ".join(['%s'] * len(insert_list)) + ), tuple(insert_list)) + insert_list = [] def enqueue_jobs_after_commit(): if frappe.flags.enqueue_after_commit and len(frappe.flags.enqueue_after_commit) > 0: diff --git a/frappe/desk/doctype/tag/tag.py b/frappe/desk/doctype/tag/tag.py index f03bf153f3..e48b6d4f9b 100644 --- a/frappe/desk/doctype/tag/tag.py +++ b/frappe/desk/doctype/tag/tag.py @@ -6,17 +6,10 @@ from __future__ import unicode_literals import frappe import json from frappe.model.document import Document -from frappe.utils.global_tags import update_global_tags from frappe import _ class Tag(Document): - - def on_trash(self): - if check_if_tag_is_linked(self.name): - frappe.throw(_("Cannot delete Tag {0} since it is linked to Documents.").format(frappe.bold(self.name))) - -def check_if_tag_is_linked(tag): - return frappe.db.count("Tag Link", {"tag": frappe.db.escape('%{0}%'.format(tag), False)}) + pass def check_user_tags(dt): "if the user does not have a tags column, then it creates one" @@ -72,7 +65,7 @@ class DocTags: if not tag in tl: tl.append(tag) if not frappe.db.exists("Tag", tag): - frappe.get_doc({"doctype": "Tag", "name": tag, "count": 1}).insert(ignore_permissions=True) + frappe.get_doc({"doctype": "Tag", "name": tag}).insert(ignore_permissions=True) self.update(dn, tl) def remove(self, dn, tag): @@ -111,3 +104,78 @@ class DocTags: """adds the _user_tags column if not exists""" from frappe.database.schema import add_column add_column(self.dt, "_user_tags", "Data") + +def delete_tags_for_document(doc): + """ + Delete the __global_tags entry of a document that has + been deleted + :param doc: Deleted document + """ + if not frappe.db.table_exists("Tag Link"): + return + + frappe.db.sql("""DELETE FROM `tabTag Link` WHERE `document_type`=%s AND `document_name`=%s""", (doc.doctype, doc.name)) + +def update_global_tags(doc, tags): + """ + Adds tags for documents + :param doc: Document to be added to global tags + """ + + new_tags = list(set([tag.strip() for tag in tags.split(",") if tag])) + + for tag in new_tags: + if not frappe.db.exists("Tag Link", {"parenttype": doc.doctype, "parent": doc.name, "tag": tag}): + frappe.get_doc({ + "doctype": "Tag Link", + "document_type": doc.doctype, + "document_name": doc.name, + "parenttype": doc.doctype, + "parent": doc.name, + "title": doc.get_title() or '', + "tag": tag + }).insert(ignore_permissions=True) + + existing_tags = [tag.tag for tag in frappe.get_list("Tag Link", filters={ + "document_type": doc.doctype, + "document_name": doc.name + }, fields=["tag"])] + + deleted_tags = get_deleted_tags(new_tags, existing_tags) + + if deleted_tags: + for tag in deleted_tags: + delete_tag_for_document(doc.doctype, doc.name, tag) + +def get_deleted_tags(new_tags, existing_tags): + + return list(set(existing_tags) - set(new_tags)) + +def delete_tag_for_document(dt, dn, tag): + frappe.db.sql("""DELETE FROM `tabTag Link` WHERE `document_type`=%s, `document_name`=%s, tag=%s""", (dt, dn, tag)) + +@frappe.whitelist() +def get_documents_for_tag(tag): + """ + Search for given text in Tag Link + :param tag: tag to be searched + """ + # remove hastag `#` from tag + tag = tag[1:] + results = [] + + result = frappe.get_list("Tag Link", filters={"tag": tag}, fields=["document_type", "document_name", "title", "tag"]) + + for res in result: + results.append({ + "doctype": res.document_type, + "name": res.document_name, + "content": res.title + }) + + print(results) + return results + +@frappe.whitelist() +def get_tags_list_for_awesomebar(): + return [t.name for t in frappe.get_list("Tag")] \ No newline at end of file diff --git a/frappe/desk/doctype/tag_link/tag_link.json b/frappe/desk/doctype/tag_link/tag_link.json index ce389415e5..00a7349c5c 100644 --- a/frappe/desk/doctype/tag_link/tag_link.json +++ b/frappe/desk/doctype/tag_link/tag_link.json @@ -4,30 +4,12 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ - "dt", - "dn", + "document_type", + "document_name", "tag", "title" ], "fields": [ - { - "fieldname": "dt", - "fieldtype": "Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Document Type", - "options": "DocType", - "read_only": 1 - }, - { - "fieldname": "dn", - "fieldtype": "Dynamic Link", - "in_list_view": 1, - "in_standard_filter": 1, - "label": "Document Name", - "options": "dt", - "read_only": 1 - }, { "fieldname": "title", "fieldtype": "Data", @@ -36,14 +18,33 @@ }, { "fieldname": "tag", - "fieldtype": "Data", + "fieldtype": "Link", "in_list_view": 1, "in_standard_filter": 1, "label": "Document Tag", + "options": "Tag", + "read_only": 1 + }, + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Document Type", + "options": "DocType", + "read_only": 1 + }, + { + "fieldname": "document_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Document Name", + "options": "document_type", "read_only": 1 } ], - "modified": "2019-09-25 22:10:47.671304", + "modified": "2019-10-03 16:42:35.932409", "modified_by": "Administrator", "module": "Desk", "name": "Tag Link", diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py index fc1f7e1f4f..4044a3dcfc 100644 --- a/frappe/desk/form/load.py +++ b/frappe/desk/form/load.py @@ -258,5 +258,9 @@ def get_view_logs(doctype, docname): return logs def get_tags(doctype, name): - tags = [tag.tag for tag in frappe.get_all("Tag Link", filters={"dt": doctype, "dn": name}, fields=["tag"])] + tags = [tag.tag for tag in frappe.get_all("Tag Link", filters={ + "document_type": doctype, + "document_name": name + }, fields=["tag"])] + return ",".join([tag for tag in tags]) \ No newline at end of file diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py index 3273bd65d7..d5b43807a8 100644 --- a/frappe/desk/reportview.py +++ b/frappe/desk/reportview.py @@ -266,12 +266,12 @@ def get_sidebar_stats(stats, doctype, filters=[]): tags = [tag.name for tag in frappe.get_list("Tag")] _user_tags = [] for tag in tags: - count = frappe.db.count("Tag Link", filters={"dt": doctype, "tag": tag}) + count = frappe.db.count("Tag Link", filters={"document_type": doctype, "tag": tag}) if count > 0: _user_tags.append([tag, count]) frappe.cache().hset("tags_count", doctype, _user_tags) - return {"defined_cat": [], "stats": {"_user_tags": frappe.cache().hget("tags_count", doctype)}} + return {"stats": {"_user_tags": frappe.cache().hget("tags_count", doctype)}} @frappe.whitelist() @frappe.read_only() diff --git a/frappe/model/delete_doc.py b/frappe/model/delete_doc.py index 193dcd417e..ba1372f278 100644 --- a/frappe/model/delete_doc.py +++ b/frappe/model/delete_doc.py @@ -16,11 +16,11 @@ from frappe.core.doctype.file.file import remove_all from frappe.utils.password import delete_all_passwords_for from frappe.model.naming import revert_series_if_last from frappe.utils.global_search import delete_for_document -from frappe.utils.global_tags import delete_tags_for_document +from frappe.desk.doctype.tag.tag import delete_tags_for_document from frappe.exceptions import FileNotFoundError -doctypes_to_skip = ("Communication", "ToDo", "DocShare", "Email Unsubscribe", "Activity Log", "File", "Version", "Document Follow", "Comment" , "View Log") +doctypes_to_skip = ("Communication", "ToDo", "DocShare", "Email Unsubscribe", "Activity Log", "File", "Version", "Document Follow", "Comment" , "View Log", "Tag Link") def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reload=False, ignore_permissions=False, flags=None, ignore_on_trash=False, ignore_missing=True): diff --git a/frappe/patches.txt b/frappe/patches.txt index 953b7f2c52..5de9831c79 100644 --- a/frappe/patches.txt +++ b/frappe/patches.txt @@ -252,4 +252,4 @@ frappe.patches.v12_0.move_email_and_phone_to_child_table frappe.patches.v12_0.delete_duplicate_indexes frappe.patches.v12_0.set_default_incoming_email_port frappe.patches.v12_0.update_global_search -frappe.patches.v12_0.global_tags +frappe.patches.v12_0.setup_global_tags diff --git a/frappe/patches/v12_0/setup_global_tags.py b/frappe/patches/v12_0/setup_global_tags.py index b3bd15745c..4f501c5e92 100644 --- a/frappe/patches/v12_0/setup_global_tags.py +++ b/frappe/patches/v12_0/setup_global_tags.py @@ -5,6 +5,7 @@ def execute(): frappe.delete_doc_if_exists("DocType", "Tag Doc Category") frappe.reload_doc("desk", "doctype", "tag") + frappe.reload_doc("desk", "doctype", "tag_link") tag_list = [] tag_links = [] @@ -20,27 +21,10 @@ def execute(): if not tag: continue - tag_list.append(tag.strip()) + tag_list.append((tag.strip(), time, time, 'Administrator')) tag_link_name = frappe.generate_hash(dt_tags.name + tag.strip(), 10), tag_links.append((tag_link_name, doctype.name, dt_tags.name, tag.strip(), time, time, 'Administrator')) - - temp_list = [] - for count, value in enumerate(set(tag_list)): - temp_list.append((value, time, time, 'Administrator')) - - if count and (count%1000 == 0 or count == len(tag_list)-1): - frappe.db.sql(""" - INSERT INTO `tabTag` (`name`, `creation`, `modified`, `modified_by`) VALUES {} - """.format(", ".join(['%s'] * len(temp_list))), tuple(temp_list)) - temp_list = [] - - for count, value in enumerate(set(tag_links)): - temp_list.append(value) - - if count and (count%1000 == 0 or count == len(tag_links)-1): - frappe.db.sql(""" - INSERT INTO `tabTag Link` (`name`, `dt`, `dn`, `tag`, `creation`, `modified`, `modified_by`) VALUES {} - """.format(", ".join(['%s'] * len(temp_list))), tuple(temp_list)) - temp_list = [] \ No newline at end of file + frappe.db.bulk_insert("Tag", fields=["name", "creation", "modified", "modified_by"], values=tag_list) + frappe.db.bulk_insert("Tag Link", fields=["name", "document_type", "document_name", "tag", "creation", "modified", "modified_by"], values=tag_links) \ No newline at end of file diff --git a/frappe/public/js/frappe/desk.js b/frappe/public/js/frappe/desk.js index fee6f4280a..13fd58a0ea 100644 --- a/frappe/public/js/frappe/desk.js +++ b/frappe/public/js/frappe/desk.js @@ -603,7 +603,7 @@ frappe.Application = Class.extend({ }, set_global_tags() { - frappe.global_tags.utils.set_tags(); + frappe.tags.utils.set_tags(); } }); diff --git a/frappe/public/js/frappe/ui/filters/filter.js b/frappe/public/js/frappe/ui/filters/filter.js index 128ea4e872..c953b221c4 100644 --- a/frappe/public/js/frappe/ui/filters/filter.js +++ b/frappe/public/js/frappe/ui/filters/filter.js @@ -61,7 +61,6 @@ frappe.ui.Filter = class { doctype: this.parent_doctype, filter_fields: this.filter_fields, select: (doctype, fieldname) => { - console.log(doctype, fieldname); this.set_field(doctype, fieldname); } }); diff --git a/frappe/public/js/frappe/ui/tag_editor.js b/frappe/public/js/frappe/ui/tag_editor.js index 00102cf808..5811714bb5 100644 --- a/frappe/public/js/frappe/ui/tag_editor.js +++ b/frappe/public/js/frappe/ui/tag_editor.js @@ -42,7 +42,7 @@ frappe.ui.TagEditor = Class.extend({ user_tags.push(tag) me.user_tags = user_tags.join(","); me.on_change && me.on_change(me.user_tags); - frappe.global_tags.utils.set_tags(); + frappe.tags.utils.set_tags(); } }); } @@ -57,7 +57,7 @@ frappe.ui.TagEditor = Class.extend({ user_tags.splice(user_tags.indexOf(tag), 1); me.user_tags = user_tags.join(","); me.on_change && me.on_change(me.user_tags); - frappe.global_tags.utils.set_tags(); + frappe.tags.utils.set_tags(); } }); } diff --git a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js index bb5f7edbe5..bfcc5a2d77 100644 --- a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js +++ b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js @@ -1,7 +1,7 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // MIT License. See license.txt frappe.provide('frappe.search'); -frappe.provide('frappe.global_tags'); +frappe.provide('frappe.tags'); frappe.search.AwesomeBar = Class.extend({ setup: function(element) { @@ -181,7 +181,7 @@ frappe.search.AwesomeBar = Class.extend({ frappe.search.utils.get_executables(txt) ); if (txt.charAt(0) === "#") { - options = frappe.global_tags.utils.get_tags(txt); + options = frappe.tags.utils.get_tags(txt); } var out = this.deduplicate(options); return out.sort(function(a, b) { diff --git a/frappe/public/js/frappe/ui/toolbar/global_tags.js b/frappe/public/js/frappe/ui/toolbar/global_tags.js index 1c9f9a88f8..6bbeb10c81 100644 --- a/frappe/public/js/frappe/ui/toolbar/global_tags.js +++ b/frappe/public/js/frappe/ui/toolbar/global_tags.js @@ -1,15 +1,15 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors // MIT License. See license.txt -frappe.provide("frappe.global_tags"); +frappe.provide("frappe.tags"); -frappe.global_tags.utils = { +frappe.tags.utils = { get_tags: function(txt) { txt = txt.slice(1); let out = []; - for (let i in frappe.global_tags.tags) { - let tag = frappe.global_tags.tags[i]; + for (let i in frappe.tags.tags) { + let tag = frappe.tags.tags[i]; let level = frappe.search.utils.fuzzy_search(txt, tag); if (level) { out.push({ @@ -31,10 +31,10 @@ frappe.global_tags.utils = { set_tags() { frappe.call({ - method: "frappe.utils.global_tags.get_tags_list_for_awesomebar", + method: "frappe.desk.doctype.tag.tag.get_tags_list_for_awesomebar", callback: function(r) { if (r && r.message) { - frappe.global_tags.tags = $.extend([], r.message); + frappe.tags.tags = $.extend([], r.message); } } }); @@ -51,6 +51,9 @@ frappe.global_tags.utils = { } function make_description(content) { + if (!content) { + return; + } var field_length = 110; var field_value = null; if (content.length > field_length) { @@ -88,12 +91,13 @@ frappe.global_tags.utils = { } return new Promise(function(resolve) { frappe.call({ - method: "frappe.utils.global_tags.get_documents_for_tag", + method: "frappe.desk.doctype.tag.tag.get_documents_for_tag", args: { tag: tag }, callback: function(r) { if (r.message) { + console.log(r.message); resolve(get_results_sets(r.message)); } else { resolve([]); diff --git a/frappe/public/js/frappe/ui/toolbar/search.js b/frappe/public/js/frappe/ui/toolbar/search.js index efd3be2868..7e7cca0768 100644 --- a/frappe/public/js/frappe/ui/toolbar/search.js +++ b/frappe/public/js/frappe/ui/toolbar/search.js @@ -386,7 +386,7 @@ frappe.search.SearchDialog = Class.extend({ global_search: { input_placeholder: __("Search"), empty_state_text: __("Search for anything"), - no_results_status: (keyword) => __("

No results found for '" + keyword + "' in Global Search

"), + no_results_status: (keyword) => "

" + __("No results found for {0} in Global Search", [keyword]) + "

", get_results: function(keywords, callback) { var start = 0, limit = 1000; @@ -403,11 +403,11 @@ frappe.search.SearchDialog = Class.extend({ global_tag: { input_placeholder: __("Search"), empty_state_text: __("Search for anything"), - no_results_status: (keyword) => __("

No results found for '" + keyword + "' in Global Tags

"), + no_results_status: (keyword) => "

" + __("No results found for {0} in Global Tags", [keyword]) + "

", get_results: function(keywords, callback) { var results = frappe.search.utils.get_nav_results(keywords); - frappe.global_tags.utils.get_tag_results(keywords) + frappe.tags.utils.get_tag_results(keywords) .then(function(global_results) { results = results.concat(global_results); callback(results, keywords); diff --git a/frappe/utils/global_tags.py b/frappe/utils/global_tags.py deleted file mode 100644 index 93cf82a140..0000000000 --- a/frappe/utils/global_tags.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - -from __future__ import unicode_literals -import frappe - -def delete_tags_for_document(doc): - """ - Delete the __global_tags entry of a document that has - been deleted - :param doc: Deleted document - """ - if not frappe.db.table_exists("Tag Link"): - return - - frappe.db.sql("""DELETE FROM `tabTag Link` WHERE `dt`=%s AND `dn`=%s""", (doc.doctype, doc.name)) - -def update_global_tags(doc, tags): - """ - Adds tags for documents - :param doc: Document to be added to global tags - """ - - new_tags = list(set([tag.strip() for tag in tags.split(",") if tag])) - - for tag in new_tags: - if not frappe.db.exists("Tag Link", {"parenttype": doc.doctype, "parent": doc.name, "tag": tag}): - frappe.get_doc({ - "doctype": "Tag Link", - "dt": doc.doctype, - "dn": doc.name, - "parenttype": doc.doctype, - "parent": doc.name, - "title": doc.get_title() or '', - "tag": tag - }).insert(ignore_permissions=True) - - existing_tags = [tag.tag for tag in frappe.get_list("Tag Link", filters={"dt": doc.doctype, "dn": doc.name}, fields=["tag"])] - - deleted_tags = get_deleted_tags(new_tags, existing_tags) - - if deleted_tags: - for tag in deleted_tags: - delete_tag_for_document(doc.doctype, doc.name, tag) - -def get_deleted_tags(new_tags, existing_tags): - - return list(set(existing_tags) - set(new_tags)) - -def delete_tag_for_document(dt, dn, tag): - frappe.db.sql("""DELETE FROM `tabTag Link` WHERE dt=%s, dn=%s, tag=%s""", (dt, dn, tag)) - -@frappe.whitelist() -def get_documents_for_tag(tag): - """ - Search for given text in __global_tags - :param tag: tag to be searched - """ - # remove hastag `#` from tag - tag = tag[1:] - results = [] - - result = frappe.db.sql(""" - SELECT `dt`, `dn`, `title`, `tag` - FROM `tabTag Link` - WHERE `tag`=%s - """, (tag), as_dict=True) - - for res in result: - results.append({ - "doctype": res.dt, - "name": res.dn, - "content": res.title - }) - - return results - -@frappe.whitelist() -def get_tags_list_for_awesomebar(): - return [t.name for t in frappe.get_list("Tag")] \ No newline at end of file