From 513f74ffa6f0319fd3fdd35554879359d77a181c Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 18 Aug 2023 10:59:00 +0530 Subject: [PATCH 01/87] feat: create single doctype for comparator --- .../doctype/document_comparator/__init__.py | 0 .../document_comparator.json | 63 +++++++++++++++++++ .../test_document_comparator.py | 9 +++ 3 files changed, 72 insertions(+) create mode 100644 frappe/core/doctype/document_comparator/__init__.py create mode 100644 frappe/core/doctype/document_comparator/document_comparator.json create mode 100644 frappe/core/doctype/document_comparator/test_document_comparator.py diff --git a/frappe/core/doctype/document_comparator/__init__.py b/frappe/core/doctype/document_comparator/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frappe/core/doctype/document_comparator/document_comparator.json b/frappe/core/doctype/document_comparator/document_comparator.json new file mode 100644 index 0000000000..3a60d866e5 --- /dev/null +++ b/frappe/core/doctype/document_comparator/document_comparator.json @@ -0,0 +1,63 @@ +{ + "actions": [], + "creation": "2023-08-14 13:06:24.520160", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "doctype_name", + "column_break_peck", + "document", + "section_break_gppi", + "version_table" + ], + "fields": [ + { + "fieldname": "doctype_name", + "fieldtype": "Link", + "label": "Doctype", + "options": "DocType" + }, + { + "fieldname": "document", + "fieldtype": "Dynamic Link", + "label": "Document", + "options": "doctype_name" + }, + { + "fieldname": "column_break_peck", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_gppi", + "fieldtype": "Section Break" + }, + { + "fieldname": "version_table", + "fieldtype": "HTML", + "label": "version_table" + } + ], + "hide_toolbar": 1, + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2023-08-14 14:08:19.031748", + "modified_by": "Administrator", + "module": "Core", + "name": "Document Comparator", + "owner": "Administrator", + "permissions": [ + { + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/frappe/core/doctype/document_comparator/test_document_comparator.py b/frappe/core/doctype/document_comparator/test_document_comparator.py new file mode 100644 index 0000000000..937df8eadd --- /dev/null +++ b/frappe/core/doctype/document_comparator/test_document_comparator.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestDocumentComparator(FrappeTestCase): + pass From e53a78544c174a7420dfe55bd0db6b21fa7e65de Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 18 Aug 2023 11:00:52 +0530 Subject: [PATCH 02/87] feat: add logic for changed values and rows --- .../document_comparator.py | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 frappe/core/doctype/document_comparator/document_comparator.py diff --git a/frappe/core/doctype/document_comparator/document_comparator.py b/frappe/core/doctype/document_comparator/document_comparator.py new file mode 100644 index 0000000000..5e5b412afc --- /dev/null +++ b/frappe/core/doctype/document_comparator/document_comparator.py @@ -0,0 +1,91 @@ +# Copyright (c) 2023, Frappe Technologies and contributors +# For license information, please see license.txt + +import json + +import frappe +from frappe.core.doctype.version.version import get_diff +from frappe.model.document import Document + + +class DocumentComparator(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + doctype_name: DF.Link | None + document: DF.DynamicLink | None + # end: auto-generated types + pass + + @frappe.whitelist() + def compare_document(self): + amended_document_names = frappe.db.get_list( + self.doctype_name, + filters={"name": ("like", "%" + self.document + "%")}, + order_by="modified", + pluck="name", + limit=5, + ) + amended_docs = [frappe.get_doc(self.doctype_name, name) for name in amended_document_names] + + changed = {} + row_changed = {} + for i in range(1, len(amended_docs)): + diff = get_diff(amended_docs[i - 1], amended_docs[i], compare_cancelled=True) + changed = get_diff_grid(amended_docs, i, diff, "changed", changed) + row_changed = get_rows_updated_grid(amended_docs, i, diff, "row_changed", row_changed) + + return amended_document_names, { + "changed": changed, + "row_changed": row_changed, + } + + +def get_diff_grid(amended_docs, i, diff, key, changed_fields): + for change in diff[key]: + fieldname = get_field_label(change[0], doctype=amended_docs[0].doctype) + value = change[-1] + if fieldname not in changed_fields: + changed_fields[fieldname] = [""] * len(amended_docs) + changed_fields[fieldname][i] = value if value else "" + + if i == 1: + value = change[1] + changed_fields[fieldname][i - 1] = value if value else "" + + return changed_fields + + +def get_rows_updated_grid(amended_docs, i, diff, key, changed_fields): + for change in diff[key]: + table_name = get_field_label(change[0], doctype=amended_docs[0].doctype) + changed_fields[table_name] = {"index": change[1]} + for field in change[-1]: + fieldname = get_field_label(field[0], is_child=True) + value = field[-1] + if fieldname not in changed_fields[table_name]: + changed_fields[table_name][fieldname] = [""] * len(amended_docs) + changed_fields[table_name][fieldname][i] = value if value else "" + + if i == 1: + value = field[1] + changed_fields[table_name][fieldname][i - 1] = value if value else "" + + return changed_fields + + +def get_field_label(fieldname, doctype=None, is_child=False): + if is_child: + label = frappe.db.get_value("DocField", {"fieldname": fieldname}, "label") + return label + + meta = frappe.get_meta(doctype) + label = meta.get_label(fieldname) + if label not in ["No Label", "None", ""]: + return label + return fieldname From 0d80ffc988c1559713f428229170edf7a109ffa3 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 18 Aug 2023 11:01:55 +0530 Subject: [PATCH 03/87] fix: return df label only when not none --- frappe/model/meta.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frappe/model/meta.py b/frappe/model/meta.py index eee18b48a1..5acff47394 100644 --- a/frappe/model/meta.py +++ b/frappe/model/meta.py @@ -243,7 +243,8 @@ class Meta(Document): def get_label(self, fieldname): """Get label of the given fieldname""" if df := self.get_field(fieldname): - return df.label + if df.label: + return df.label if fieldname in DEFAULT_FIELD_LABELS: return DEFAULT_FIELD_LABELS[fieldname]() From ef7af5e849e7df08e2eb18b18edb28892a792e88 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 18 Aug 2023 11:03:34 +0530 Subject: [PATCH 04/87] fix: child rows for cancelled docs in getdiff --- frappe/core/doctype/version/version.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/frappe/core/doctype/version/version.py b/frappe/core/doctype/version/version.py index d68cda25b5..75dfb1a11d 100644 --- a/frappe/core/doctype/version/version.py +++ b/frappe/core/doctype/version/version.py @@ -59,7 +59,7 @@ class Version(Document): return json.loads(self.data) -def get_diff(old, new, for_child=False): +def get_diff(old, new, for_child=False, compare_cancelled=False): """Get diff between 2 document objects If there is a change, then returns a dict like: @@ -111,7 +111,19 @@ def get_diff(old, new, for_child=False): # check rows for additions, changes for i, d in enumerate(new_value): - old_row_name = getattr(d, old_row_name_field, None) + old_row_name = None + + if compare_cancelled: + amended_from = frappe.db.get_value(d.parenttype, d.parent, "amended_from") + if amended_from: + parent_doc = frappe.get_doc(d.parenttype, amended_from) + old_table = parent_doc.get(d.parentfield) + if old_table and len(old_table) > i: + old_row_name = old_table[i].name + + if not old_row_name: + old_row_name = getattr(d, old_row_name_field, None) + if old_row_name and old_row_name in old_rows_by_name: found_rows.add(old_row_name) From c3d0ab5f9a4ef4c961bf11e7574fb2175bc7d3a7 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 18 Aug 2023 11:04:31 +0530 Subject: [PATCH 05/87] feat: add html template for comparator --- .../document_comparator.html | 74 +++++++++++++++++++ .../document_comparator.js | 35 +++++++++ 2 files changed, 109 insertions(+) create mode 100644 frappe/core/doctype/document_comparator/document_comparator.html create mode 100644 frappe/core/doctype/document_comparator/document_comparator.js diff --git a/frappe/core/doctype/document_comparator/document_comparator.html b/frappe/core/doctype/document_comparator/document_comparator.html new file mode 100644 index 0000000000..3b4950adea --- /dev/null +++ b/frappe/core/doctype/document_comparator/document_comparator.html @@ -0,0 +1,74 @@ +
+ +
+ {% if documents.length > 1 %} +

Documents to Compare : {{ documents.length }}

+ {% else %} +

Documents to Compare : {{ documents.length - 1 }}

+ {% endif %} +
+ + {% var field_keys = Object.keys(changed).sort(); %} + {% if field_keys.length > 0 %} +
+
Changes
+ + + + {% for doc in documents %} + + {% endfor %} + + + {% for fieldname in field_keys %} + + + {% var values = changed[fieldname] %} + + {% for value in values %} + + {% endfor %} + + {% endfor %} + +
Fields {{ doc }}
{{ fieldname }} {{ value }}
+
+ {% endif %} + + {% var tables = Object.keys(row_changed).sort(); %} + {% if tables.length > 0 %} +
+
Rows Updated
+ + + + {% for doc in documents %} + + {% endfor %} + + + {% for table in tables %} + + + {% var row_index = row_changed[table]["index"] %} + + {% var fields = Object.keys(row_changed[table]).sort(); %} + {% for field in fields %} + {% if field != "index" %} + + + {% var values = row_changed[table][field] %} + {% for value in values %} + + {% endfor %} + + {% endfor %} + {% endfor %} + + {% endfor %} + +
Fields {{ doc }}
{{ table }}
idx : {{ row_index }}
{{ field }} {{ value }}
+
+ {% endif %} + +
\ No newline at end of file diff --git a/frappe/core/doctype/document_comparator/document_comparator.js b/frappe/core/doctype/document_comparator/document_comparator.js new file mode 100644 index 0000000000..22e73a848f --- /dev/null +++ b/frappe/core/doctype/document_comparator/document_comparator.js @@ -0,0 +1,35 @@ +// Copyright (c) 2023, Frappe Technologies and contributors +// For license information, please see license.txt + +frappe.ui.form.on("Document Comparator", { + refresh(frm) { + frm.disable_save(); + + frm.set_query("doctype_name", () => { + return { + filters: { + track_changes: 1, + }, + }; + }); + + frm.page.set_primary_action("Compare", () => { + frm.call({ + doc: frm.doc, + method: "compare_document", + callback: function (r) { + let document_names = r.message[0]; + let changed_fields = r.message[1]; + let render_dict = { + documents: document_names, + changed: changed_fields.changed, + row_changed: changed_fields.row_changed, + }; + $(frappe.render_template("document_comparator", render_dict)).appendTo( + frm.fields_dict.version_table.$wrapper.empty() + ); + }, + }); + }); + }, +}); From 0b00aabc6aac5518e9423217bc01e8d61f887518 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 18 Aug 2023 11:27:34 +0530 Subject: [PATCH 06/87] fix: remove indicator --- frappe/core/doctype/document_comparator/document_comparator.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frappe/core/doctype/document_comparator/document_comparator.js b/frappe/core/doctype/document_comparator/document_comparator.js index 22e73a848f..4119af7378 100644 --- a/frappe/core/doctype/document_comparator/document_comparator.js +++ b/frappe/core/doctype/document_comparator/document_comparator.js @@ -3,6 +3,8 @@ frappe.ui.form.on("Document Comparator", { refresh(frm) { + frm.page.clear_indicator(); + frm.disable_save(); frm.set_query("doctype_name", () => { From 53f5280af06817dadfe1a2a0bb8e87157eb44b30 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 18 Aug 2023 17:27:11 +0530 Subject: [PATCH 07/87] fix: multiple rows changed in each table --- .../document_comparator.html | 12 ++++++------ .../document_comparator/document_comparator.js | 2 ++ .../document_comparator.json | 3 ++- .../document_comparator/document_comparator.py | 17 ++++++++++++----- 4 files changed, 22 insertions(+), 12 deletions(-) diff --git a/frappe/core/doctype/document_comparator/document_comparator.html b/frappe/core/doctype/document_comparator/document_comparator.html index 3b4950adea..6a5ec2f2b8 100644 --- a/frappe/core/doctype/document_comparator/document_comparator.html +++ b/frappe/core/doctype/document_comparator/document_comparator.html @@ -50,14 +50,14 @@ {% for table in tables %} {{ table }} - {% var row_index = row_changed[table]["index"] %} - idx : {{ row_index }} - {% var fields = Object.keys(row_changed[table]).sort(); %} - {% for field in fields %} - {% if field != "index" %} + {% var rows = Object.keys(row_changed[table]).sort(); %} + {% for idx in rows %} + idx : {{ idx }} + {% var fields = Object.keys(row_changed[table][idx]).sort(); %} + {% for field in fields %} {{ field }} - {% var values = row_changed[table][field] %} + {% var values = row_changed[table][idx][field] %} {% for value in values %} {{ value }} {% endfor %} diff --git a/frappe/core/doctype/document_comparator/document_comparator.js b/frappe/core/doctype/document_comparator/document_comparator.js index 4119af7378..45f70c45e9 100644 --- a/frappe/core/doctype/document_comparator/document_comparator.js +++ b/frappe/core/doctype/document_comparator/document_comparator.js @@ -11,6 +11,7 @@ frappe.ui.form.on("Document Comparator", { return { filters: { track_changes: 1, + is_submittable: 1, }, }; }); @@ -30,6 +31,7 @@ frappe.ui.form.on("Document Comparator", { $(frappe.render_template("document_comparator", render_dict)).appendTo( frm.fields_dict.version_table.$wrapper.empty() ); + frm.set_df_property("version_table", "hidden", 0); }, }); }); diff --git a/frappe/core/doctype/document_comparator/document_comparator.json b/frappe/core/doctype/document_comparator/document_comparator.json index 3a60d866e5..fc0644731f 100644 --- a/frappe/core/doctype/document_comparator/document_comparator.json +++ b/frappe/core/doctype/document_comparator/document_comparator.json @@ -36,6 +36,7 @@ { "fieldname": "version_table", "fieldtype": "HTML", + "hidden": 1, "label": "version_table" } ], @@ -43,7 +44,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-08-14 14:08:19.031748", + "modified": "2023-08-18 14:07:50.848659", "modified_by": "Administrator", "module": "Core", "name": "Document Comparator", diff --git a/frappe/core/doctype/document_comparator/document_comparator.py b/frappe/core/doctype/document_comparator/document_comparator.py index 5e5b412afc..4857b8e5b6 100644 --- a/frappe/core/doctype/document_comparator/document_comparator.py +++ b/frappe/core/doctype/document_comparator/document_comparator.py @@ -62,19 +62,26 @@ def get_diff_grid(amended_docs, i, diff, key, changed_fields): def get_rows_updated_grid(amended_docs, i, diff, key, changed_fields): + # set an empty dictionary for each table + # so it does not get overwritten for every change in same table + for table in diff[key]: + table_name = get_field_label(table[0], doctype=amended_docs[0].doctype) + changed_fields[table_name] = {} + for change in diff[key]: table_name = get_field_label(change[0], doctype=amended_docs[0].doctype) - changed_fields[table_name] = {"index": change[1]} + index = change[1] + changed_fields[table_name][index] = {} for field in change[-1]: fieldname = get_field_label(field[0], is_child=True) value = field[-1] - if fieldname not in changed_fields[table_name]: - changed_fields[table_name][fieldname] = [""] * len(amended_docs) - changed_fields[table_name][fieldname][i] = value if value else "" + if fieldname not in changed_fields[table_name][index]: + changed_fields[table_name][index][fieldname] = [""] * len(amended_docs) + changed_fields[table_name][index][fieldname][i] = value if value else "" if i == 1: value = field[1] - changed_fields[table_name][fieldname][i - 1] = value if value else "" + changed_fields[table_name][index][fieldname][i - 1] = value if value else "" return changed_fields From 62e4df8a1ff10afc16c337cfe3e59d5f4fdceef3 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 18 Aug 2023 14:06:05 +0200 Subject: [PATCH 08/87] feat(File Uploader): open file details from search --- frappe/public/js/frappe/file_uploader/TreeNode.vue | 14 ++++++++++++++ frappe/public/scss/desk/tree.scss | 13 +++++++++++++ 2 files changed, 27 insertions(+) diff --git a/frappe/public/js/frappe/file_uploader/TreeNode.vue b/frappe/public/js/frappe/file_uploader/TreeNode.vue index 308bb2b825..f7c7aca706 100644 --- a/frappe/public/js/frappe/file_uploader/TreeNode.vue +++ b/frappe/public/js/frappe/file_uploader/TreeNode.vue @@ -8,6 +8,16 @@ >
{{ node.label }} + + + +
    { return icons.closed; }); +let open_file = (filename) => { + return frappe.utils.get_form_link("File", filename); +}; + diff --git a/package.json b/package.json index 0794ae5d1d..63907dee69 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ }, "homepage": "https://frappeframework.com", "dependencies": { + "@popperjs/core": "^2.11.2", "@editorjs/editorjs": "^2.26.3", "@frappe/esbuild-plugin-postcss2": "^0.1.3", "@redis/client": "^1.5.8", diff --git a/yarn.lock b/yarn.lock index f632157bbe..75fb245e9e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -81,6 +81,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@popperjs/core@^2.11.2": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + "@redis/client@^1.5.8": version "1.5.8" resolved "https://registry.yarnpkg.com/@redis/client/-/client-1.5.8.tgz#a375ba7861825bd0d2dc512282b8bff7b98dbcb1" From a6468cab3a253bf54da704389634b9cf1f0c95bd Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Fri, 15 Sep 2023 18:20:02 +0530 Subject: [PATCH 83/87] fix: using icon instead of svg --- frappe/public/icons/timeless/icons.svg | 8 +++++++- frappe/public/js/frappe/file_uploader/TreeNode.vue | 12 +++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/frappe/public/icons/timeless/icons.svg b/frappe/public/icons/timeless/icons.svg index 29aa428d99..a67fadfd36 100644 --- a/frappe/public/icons/timeless/icons.svg +++ b/frappe/public/icons/timeless/icons.svg @@ -120,6 +120,12 @@ + + + + + + @@ -302,7 +308,7 @@ - + diff --git a/frappe/public/js/frappe/file_uploader/TreeNode.vue b/frappe/public/js/frappe/file_uploader/TreeNode.vue index 3368a94875..759504bf65 100644 --- a/frappe/public/js/frappe/file_uploader/TreeNode.vue +++ b/frappe/public/js/frappe/file_uploader/TreeNode.vue @@ -17,15 +17,9 @@ :href="open_file(node.value)" :disabled="node.fetching" target="_blank" - class="file-doc-link ml-3" - > - - - - - + class="file-doc-link ml-2" + v-html="frappe.utils.icon('external-link', 'sm')" + />