Merge branch 'develop' into fix-custom-columns

This commit is contained in:
Shivam Mishra 2020-08-20 08:59:15 +00:00 committed by GitHub
commit ecf1944ed9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 453 additions and 171 deletions

View file

@ -75,17 +75,23 @@ def get_value(doctype, fieldname, filters=None, as_dict=True, debug=False, paren
filters = get_safe_filters(filters)
try:
fieldname = json.loads(fieldname)
fields = json.loads(fieldname)
except (TypeError, ValueError):
# name passed, not json
pass
fields = [fieldname]
# check whether the used filters were really parseable and usable
# and did not just result in an empty string or dict
if not filters:
filters = None
return frappe.db.get_value(doctype, filters, fieldname, as_dict=as_dict, debug=debug)
value = frappe.get_list(doctype, filters=filters, fields=fields, debug=debug, limit=1)
if as_dict:
value = value[0] if value else {}
else:
value = value[0].fieldname
return value
@frappe.whitelist()
def get_single_value(doctype, field):

View file

@ -609,7 +609,7 @@
"link_fieldname": "reference_doctype"
}
],
"modified": "2020-08-06 12:59:32.369093",
"modified": "2020-08-06 12:59:32.369095",
"modified_by": "Administrator",
"module": "Core",
"name": "DocType",

View file

@ -42,26 +42,6 @@ frappe.ui.form.on('Report', {
}
},
report_type: function(frm) {
frm.set_intro("");
switch(frm.doc.report_type) {
case "Report Builder":
frm.set_intro(__("Report Builder reports are managed directly by the report builder. Nothing to do."));
break;
case "Query Report":
frm.set_intro(__("Write a SELECT query. Note result is not paged (all data is sent in one go).")
+ __("To format columns, give column labels in the query.") + "<br>"
+ __("[Label]:[Field Type]/[Options]:[Width]") + "<br><br>"
+ __("Example:") + "<br>"
+ "Employee:Link/Employee:200" + "<br>"
+ "Rate:Currency:120" + "<br>")
break;
case "Script Report":
frm.set_intro(__("Write a Python file in the same folder where this is saved and return column and result."));
break;
}
},
set_doctype_roles: function(frm) {
return frm.call('set_doctype_roles').then(() => {
frm.refresh_field('roles');

View file

@ -1,4 +1,5 @@
{
"actions": [],
"autoname": "field:report_name",
"creation": "2013-03-09 15:45:57",
"doctype": "DocType",
@ -17,10 +18,15 @@
"disabled",
"disable_prepared_report",
"prepared_report",
"filters_section",
"filters",
"columns_section",
"columns",
"section_break_6",
"query",
"javascript",
"report_script",
"client_code_section",
"javascript",
"json",
"permission_rules",
"roles"
@ -94,7 +100,8 @@
},
{
"fieldname": "section_break_6",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"label": "Query / Script"
},
{
"depends_on": "eval:doc.report_type==\"Query Report\"",
@ -142,15 +149,50 @@
"read_only": 1
},
{
"depends_on": "eval:doc.report_type==\"Script Report\" && doc.is_standard===\"No\"",
"description": "output in the form of `data = [columns, result]`",
"depends_on": "eval:(doc.report_type===\"Script Report\" \n|| doc.report_type==\"Query Report\") \n&& doc.is_standard===\"No\"",
"description": "Filters will be accessible via <code>filters</code>. <br><br>Send output as <code>result = [result]</code>, or for old style <code>data = [columns], [result]</code>",
"fieldname": "report_script",
"fieldtype": "Code",
"label": "Script"
},
{
"collapsible": 1,
"collapsible_depends_on": "filters",
"fieldname": "filters_section",
"fieldtype": "Section Break",
"label": "Filters"
},
{
"fieldname": "filters",
"fieldtype": "Table",
"label": "Filters",
"options": "Report Filter"
},
{
"collapsible": 1,
"collapsible_depends_on": "columns",
"fieldname": "columns_section",
"fieldtype": "Section Break",
"label": "Columns"
},
{
"fieldname": "columns",
"fieldtype": "Table",
"label": "Columns",
"options": "Report Column"
},
{
"collapsible": 1,
"collapsible_depends_on": "javascript",
"fieldname": "client_code_section",
"fieldtype": "Section Break",
"label": "Client Code"
}
],
"idx": 1,
"modified": "2019-10-09 15:43:08.577610",
"index_web_pages_for_search": 1,
"links": [],
"modified": "2020-08-17 16:49:28.474274",
"modified_by": "Administrator",
"module": "Core",
"name": "Report",

View file

@ -51,6 +51,9 @@ class Report(Document):
def on_trash(self):
delete_custom_role('report', self.name)
def get_columns(self):
return [d.as_dict(no_default_fields = True) for d in self.columns]
def set_doctype_roles(self):
if not self.get('roles') and self.is_standard == 'No':
meta = frappe.get_meta(self.ref_doctype)
@ -99,8 +102,8 @@ class Report(Document):
if not self.query.lower().startswith("select"):
frappe.throw(_("Query must be a SELECT"), title=_('Report Document Error'))
result = [list(t) for t in frappe.db.sql(self.query, filters)]
columns = [cstr(c[0]) for c in frappe.db.get_description()]
result = [list(t) for t in frappe.db.sql(self.query, filters, debug=True)]
columns = self.get_columns() or [cstr(c[0]) for c in frappe.db.get_description()]
return [columns, result]
@ -134,135 +137,167 @@ class Report(Document):
def execute_script(self, filters):
# server script
loc = {"filters": frappe._dict(filters), 'data':[]}
loc = {"filters": frappe._dict(filters), 'data':None, 'result':None}
safe_exec(self.report_script, None, loc)
return loc['data']
if loc['data']:
return loc['data']
else:
return self.get_columns(), loc['result']
def get_data(self, filters=None, limit=None, user=None, as_dict=False, ignore_prepared_report=False):
columns = []
out = []
if self.report_type in ('Query Report', 'Script Report', 'Custom Report'):
# query and script reports
data = frappe.desk.query_report.run(self.name,
filters=filters, user=user, ignore_prepared_report=ignore_prepared_report)
for d in data.get('columns'):
if isinstance(d, dict):
col = frappe._dict(d)
if not col.fieldname:
col.fieldname = col.label
columns.append(col)
else:
fieldtype, options = "Data", None
parts = d.split(':')
if len(parts) > 1:
if parts[1]:
fieldtype, options = parts[1], None
if fieldtype and '/' in fieldtype:
fieldtype, options = fieldtype.split('/')
columns.append(frappe._dict(label=parts[0], fieldtype=fieldtype, fieldname=parts[0], options=options))
out += data.get('result')
columns, result = self.run_query_report(filters, user, ignore_prepared_report)
else:
# standard report
params = json.loads(self.json)
if params.get('fields'):
columns = params.get('fields')
elif params.get('columns'):
columns = params.get('columns')
else:
columns = [['name', self.ref_doctype]]
for df in frappe.get_meta(self.ref_doctype).fields:
if df.in_list_view:
columns.append([df.fieldname, self.ref_doctype])
_filters = params.get('filters') or []
if filters:
for key, value in iteritems(filters):
condition, _value = '=', value
if isinstance(value, (list, tuple)):
condition, _value = value
_filters.append([key, condition, _value])
def _format(parts):
# sort by is saved as DocType.fieldname, covert it to sql
return '`tab{0}`.`{1}`'.format(*parts)
if params.get('sort_by'):
order_by = _format(params.get('sort_by').split('.')) + ' ' + params.get('sort_order')
elif params.get('order_by'):
order_by = params.get('order_by')
else:
order_by = _format([self.ref_doctype, 'modified']) + ' desc'
if params.get('sort_by_next'):
order_by += ', ' + _format(params.get('sort_by_next').split('.')) + ' ' + params.get('sort_order_next')
group_by = None
if params.get('group_by'):
group_by_args = frappe._dict(params['group_by'])
group_by = group_by_args['group_by']
order_by = '_aggregate_column desc'
result = frappe.get_list(self.ref_doctype,
fields = [
get_group_by_field(group_by_args, c[1]) if c[0] == '_aggregate_column' and group_by_args
else _format([c[1], c[0]])
for c in columns
],
filters=_filters,
order_by = order_by,
as_list=True,
limit=limit,
group_by=group_by,
user=user)
_columns = []
for (fieldname, doctype) in columns:
meta = frappe.get_meta(doctype)
if meta.get_field(fieldname):
field = meta.get_field(fieldname)
else:
if fieldname == '_aggregate_column':
label = get_group_by_column_label(group_by_args, meta)
else:
label = meta.get_label(fieldname)
field = frappe._dict(fieldname=fieldname, label=label)
# since name is the primary key for a document, it will always be a Link datatype
if fieldname == "name":
field.fieldtype = "Link"
field.options = doctype
_columns.append(field)
columns = _columns
out = out + [list(d) for d in result]
if params.get('add_totals_row'):
out = append_totals_row(out)
columns, result = self.run_standard_report(filters, limit, user)
if as_dict:
data = []
for row in out:
if isinstance(row, (list, tuple)):
_row = frappe._dict()
for i, val in enumerate(row):
_row[columns[i].get('fieldname')] = val
elif isinstance(row, dict):
# no need to convert from dict to dict
_row = frappe._dict(row)
data.append(_row)
else:
data = out
return columns, data
result = self.build_data_dict(result, columns)
return columns, result
def run_query_report(self, filters, user, ignore_prepared_report=False):
columns, result = [], []
data = frappe.desk.query_report.run(self.name,
filters=filters, user=user, ignore_prepared_report=ignore_prepared_report)
for d in data.get('columns'):
if isinstance(d, dict):
col = frappe._dict(d)
if not col.fieldname:
col.fieldname = col.label
columns.append(col)
else:
fieldtype, options = "Data", None
parts = d.split(':')
if len(parts) > 1:
if parts[1]:
fieldtype, options = parts[1], None
if fieldtype and '/' in fieldtype:
fieldtype, options = fieldtype.split('/')
columns.append(frappe._dict(label=parts[0], fieldtype=fieldtype, fieldname=parts[0], options=options))
result += data.get('result')
return columns, result
def run_standard_report(self, filters, limit, user):
params = json.loads(self.json)
columns = self.get_standard_report_columns(params)
result = []
order_by, group_by, group_by_args = self.get_standard_report_order_by(params)
_result = frappe.get_list(self.ref_doctype,
fields = [
get_group_by_field(group_by_args, c[1]) if c[0] == '_aggregate_column' and group_by_args
else Report._format([c[1], c[0]]) for c in columns
],
filters = self.get_standard_report_filters(params, filters),
order_by = order_by,
group_by = group_by,
as_list = True,
limit = limit,
user = user)
columns = self.build_standard_report_columns(columns, group_by_args)
result = result + [list(d) for d in _result]
if params.get('add_totals_row'):
result = append_totals_row(result)
return columns, result
@staticmethod
def _format(parts):
# sort by is saved as DocType.fieldname, covert it to sql
return '`tab{0}`.`{1}`'.format(*parts)
def get_standard_report_columns(self, params):
if params.get('fields'):
columns = params.get('fields')
elif params.get('columns'):
columns = params.get('columns')
elif params.get('fields'):
columns = params.get('fields')
else:
columns = [['name', self.ref_doctype]]
for df in frappe.get_meta(self.ref_doctype).fields:
if df.in_list_view:
columns.append([df.fieldname, self.ref_doctype])
return columns
def get_standard_report_filters(self, params, filters):
_filters = params.get('filters') or []
if filters:
for key, value in iteritems(filters):
condition, _value = '=', value
if isinstance(value, (list, tuple)):
condition, _value = value
_filters.append([key, condition, _value])
return _filters
def get_standard_report_order_by(self, params):
group_by_args = None
if params.get('sort_by'):
order_by = Report._format(params.get('sort_by').split('.')) + ' ' + params.get('sort_order')
elif params.get('order_by'):
order_by = params.get('order_by')
else:
order_by = Report._format([self.ref_doctype, 'modified']) + ' desc'
if params.get('sort_by_next'):
order_by += ', ' + Report._format(params.get('sort_by_next').split('.')) + ' ' + params.get('sort_order_next')
group_by = None
if params.get('group_by'):
group_by_args = frappe._dict(params['group_by'])
group_by = group_by_args['group_by']
order_by = '_aggregate_column desc'
return order_by, group_by, group_by_args
def build_standard_report_columns(self, columns, group_by_args):
_columns = []
for (fieldname, doctype) in columns:
meta = frappe.get_meta(doctype)
if meta.get_field(fieldname):
field = meta.get_field(fieldname)
else:
if fieldname == '_aggregate_column':
label = get_group_by_column_label(group_by_args, meta)
else:
label = meta.get_label(fieldname)
field = frappe._dict(fieldname=fieldname, label=label)
# since name is the primary key for a document, it will always be a Link datatype
if fieldname == "name":
field.fieldtype = "Link"
field.options = doctype
_columns.append(field)
return _columns
def build_data_dict(self, result, columns):
data = []
for row in result:
if isinstance(row, (list, tuple)):
_row = frappe._dict()
for i, val in enumerate(row):
_row[columns[i].get('fieldname')] = val
elif isinstance(row, dict):
# no need to convert from dict to dict
_row = frappe._dict(row)
data.append(_row)
return data
@Document.whitelist
def toggle_disable(self, disable):

View file

@ -111,3 +111,41 @@ data = [
# check values
self.assertTrue('System User' in [d.get('type') for d in data[1]])
def test_script_report_with_columns(self):
report_name = 'Test Script Report With Columns'
if frappe.db.exists("Report", report_name):
frappe.delete_doc('Report', report_name)
report = frappe.get_doc({
'doctype': 'Report',
'ref_doctype': 'User',
'report_name': report_name,
'report_type': 'Script Report',
'is_standard': 'No',
'columns': [
dict(fieldname='type', label='Type', fieldtype='Data'),
dict(fieldname='value', label='Value', fieldtype='Int'),
]
}).insert(ignore_permissions=True)
report.report_script = '''
totals = {}
for user in frappe.get_all('User', fields = ['name', 'user_type', 'creation']):
if not user.user_type in totals:
totals[user.user_type] = 0
totals[user.user_type] = totals[user.user_type] + 1
result = [
{"type":key, "value": value} for key, value in totals.items()
]
'''
report.save()
data = report.get_data()
# check columns
self.assertEqual(data[0][0]['label'], 'Type')
# check values
self.assertTrue('System User' in [d.get('type') for d in data[1]])

View file

@ -0,0 +1,61 @@
{
"actions": [],
"creation": "2020-01-14 11:28:37.583656",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"fieldname",
"label",
"fieldtype",
"options",
"width"
],
"fields": [
{
"fieldname": "fieldname",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Fieldname",
"reqd": 1
},
{
"fieldname": "label",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Label",
"reqd": 1
},
{
"fieldname": "fieldtype",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Fieldtype",
"options": "Check\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nInt\nLink\nSelect\nTime",
"reqd": 1
},
{
"fieldname": "options",
"fieldtype": "Data",
"label": "Options"
},
{
"fieldname": "width",
"fieldtype": "Int",
"label": "Width"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2020-08-17 14:32:17.174796",
"modified_by": "Administrator",
"module": "Core",
"name": "Report Column",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View file

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class ReportColumn(Document):
pass

View file

@ -0,0 +1,71 @@
{
"actions": [],
"creation": "2020-01-14 11:38:58.016498",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"fieldname",
"label",
"fieldtype",
"mandatory",
"options",
"wildcard_filter"
],
"fields": [
{
"fieldname": "fieldname",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Fieldname",
"reqd": 1
},
{
"fieldname": "label",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Label",
"reqd": 1
},
{
"fieldname": "fieldtype",
"fieldtype": "Select",
"in_list_view": 1,
"label": "Fieldtype",
"options": "Check\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nInt\nLink\nSelect\nTime",
"reqd": 1
},
{
"default": "0",
"fieldname": "mandatory",
"fieldtype": "Check",
"in_list_view": 1,
"label": "Mandatory"
},
{
"fieldname": "options",
"fieldtype": "Data",
"label": "Options"
},
{
"default": "0",
"description": "Will add \"%\" before and after the query",
"fieldname": "wildcard_filter",
"fieldtype": "Check",
"label": "Wildcard Filter"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2020-08-17 16:15:46.937267",
"modified_by": "Administrator",
"module": "Core",
"name": "Report Filter",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1
}

View file

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2020, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
# import frappe
from frappe.model.document import Document
class ReportFilter(Document):
pass

View file

@ -47,11 +47,11 @@ class Workspace:
self.allowed_pages = get_allowed_pages(cache=True)
self.allowed_reports = get_allowed_reports(cache=True)
if not minimal:
self.onboarding_doc = self.get_onboarding_doc()
self.onboarding = None
self.table_counts = get_table_with_counts()
self.restricted_doctypes = frappe.cache().get_value("domain_restricted_doctypes") or build_domain_restriced_doctype_cache()
self.restricted_pages = frappe.cache().get_value("domain_restricted_pages") or build_domain_restriced_page_cache()
@ -59,7 +59,7 @@ class Workspace:
def is_page_allowed(self):
cards = self.doc.cards + get_custom_reports_and_doctypes(self.doc.module) + self.extended_cards
shortcuts = self.doc.shortcuts + self.extended_shortcuts
for section in cards:
links = loads(section.links) if isinstance(section.links, string_types) else section.links
for item in links:
@ -195,7 +195,7 @@ class Workspace:
'docs_url': self.onboarding_doc.documentation_url,
'items': self.get_onboarding_steps()
}
@handle_not_exist
def get_cards(self):
cards = self.doc.cards
@ -303,7 +303,7 @@ class Workspace:
if self.is_item_allowed(item.link_to, item.type) and _in_active_domains(item):
if item.type == "Report":
report = self.allowed_reports.get(item.link_to, {})
if report.get("report_type") in ["Query Report", "Script Report"]:
if report.get("report_type") in ["Query Report", "Script Report", "Custom Report"]:
new_item['is_query_report'] = 1
else:
new_item['ref_doctype'] = report.get('ref_doctype')
@ -358,7 +358,7 @@ def get_desk_sidebar_items(flatten=False, cache=True):
_cache = frappe.cache()
if cache:
pages = _cache.get_value("desk_sidebar_items", user=frappe.session.user)
if not pages or not cache:
# don't get domain restricted pages
blocked_modules = frappe.get_doc('User', frappe.session.user).get_blocked_modules()
@ -377,7 +377,7 @@ def get_desk_sidebar_items(flatten=False, cache=True):
order_by = "pin_to_top desc, pin_to_bottom asc, name asc"
all_pages = frappe.get_all("Desk Page", fields=["name", "category"], filters=filters, order_by=order_by, ignore_permissions=True)
pages = []
# Filter Page based on Permission
for page in all_pages:
try:

View file

@ -8,7 +8,7 @@
"engine": "InnoDB",
"field_order": [
"send_from",
"column_break_2",
"schedule_sending",
"schedule_send",
"recipients",
"email_group",
@ -114,19 +114,22 @@
"label": "Recipients"
},
{
"depends_on": "eval: doc.schedule_sending",
"fieldname": "schedule_send",
"fieldtype": "Datetime",
"label": "Schedule Send"
},
{
"fieldname": "column_break_2",
"fieldtype": "Column Break"
},
{
"default": "0",
"fieldname": "send_attachments",
"fieldtype": "Check",
"label": "Send Attachments"
},
{
"default": "0",
"fieldname": "schedule_sending",
"fieldtype": "Check",
"label": "Schedule Sending"
}
],
"has_web_view": 1,
@ -136,7 +139,7 @@
"is_published_field": "published",
"links": [],
"max_attachments": 3,
"modified": "2020-07-21 16:25:17.687476",
"modified": "2020-08-17 18:11:59.541686",
"modified_by": "Administrator",
"module": "Email",
"name": "Newsletter",

View file

@ -271,7 +271,8 @@ def send_scheduled_email():
"""Send scheduled newsletter to the recipients."""
scheduled_newsletter = frappe.get_all('Newsletter', filters = {
'schedule_send': ('<=', now_datetime()),
'email_sent': 0
'email_sent': 0,
'schedule_sending': 1
}, fields = ['name'], ignore_ifnull=True)
for newsletter in scheduled_newsletter:
send_newsletter(newsletter.name)

View file

@ -69,6 +69,7 @@ class TestNewsletter(unittest.TestCase):
"send_from": "Test Sender <test_sender@example.com>",
"message": "Testing my news.",
"published": published,
"schedule_sending": bool(schedule_send),
"schedule_send": schedule_send
}).insert(ignore_permissions=True)

View file

@ -366,6 +366,12 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
this.report_settings.html_format = settings.html_format;
this.report_settings.execution_time = settings.execution_time || 0;
frappe.query_reports[this.report_name] = this.report_settings;
if (this.report_doc.filters && !this.report_settings.filters) {
// add configured filters
this.report_settings.filters = this.report_doc.filters;
}
resolve();
});
}).catch(reject);
@ -1109,8 +1115,11 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
.map(f => {
var v = f.get_value();
// hidden fields dont have $input
if(f.df.hidden) v = f.value;
if(v === '%') v = null;
if (f.df.hidden) v = f.value;
if (v === '%') v = null;
if (f.df.wildcard_filter) {
v = `%${v}%`;
}
return {
[f.df.fieldname]: v
};

View file

@ -4,7 +4,7 @@ from __future__ import unicode_literals
import unittest, frappe, os
from frappe.core.doctype.user.user import generate_keys
from frappe.frappeclient import FrappeClient
from frappe.frappeclient import FrappeClient, FrappeException
from frappe.utils.data import get_url
import requests
@ -56,6 +56,21 @@ class TestAPI(unittest.TestCase):
doc = server.get_doc("Note", "get_this")
self.assertTrue(doc)
def test_get_value(self):
server = FrappeClient(get_url(), "Administrator", "admin", verify=False)
frappe.db.sql("delete from `tabNote` where title = 'get_value'")
frappe.db.commit()
test_content = "test get value"
server.insert_many([
{"doctype": "Note", "public": True, "title": "get_value", "content": test_content},
])
self.assertEqual(server.get_value("Note", "content", {"title": "get_value"}).get('content'), test_content)
self.assertRaises(FrappeException, server.get_value, "Note", "(select (password) from(__Auth) order by name desc limit 1)", {"title": "get_value"})
def test_update_doc(self):
server = FrappeClient(get_url(), "Administrator", "admin", verify=False)
frappe.db.sql("delete from `tabNote` where title in ('Sing','sing')")

View file

@ -154,7 +154,7 @@ class WebsiteGenerator(Document):
# Check if the route is changed
if old_doc and old_doc.route != self.route:
# Remove the route from index if the route has changed
remove_document_from_index("web_routes", old_doc.route)
remove_document_from_index(old_doc.route)
def update_website_search_index(self):
"""
@ -169,4 +169,4 @@ class WebsiteGenerator(Document):
frappe.enqueue(update_index_for_path, path=self.route)
elif self.route:
# If the website is not published
remove_document_from_index("web_routes", self.route)
remove_document_from_index(self.route)