Merge pull request #17891 from ankush/misc_fixes
perf: ~45% faster Desk first response
This commit is contained in:
commit
7b3cc322f1
15 changed files with 78 additions and 49 deletions
|
|
@ -240,6 +240,7 @@ def init(site: str, sites_path: str = ".", new_site: bool = False) -> None:
|
|||
local.document_cache = {}
|
||||
local.meta_cache = {}
|
||||
local.form_dict = _dict()
|
||||
local.preload_assets = {"style": [], "script": []}
|
||||
local.session = _dict()
|
||||
local.dev_server = _dev_server
|
||||
local.qb = get_query_builder(local.conf.db_type or "mariadb")
|
||||
|
|
|
|||
|
|
@ -192,4 +192,4 @@ def get_documents_for_tag(tag):
|
|||
|
||||
@frappe.whitelist()
|
||||
def get_tags_list_for_awesomebar():
|
||||
return [t.name for t in frappe.get_list("Tag")]
|
||||
return frappe.get_list("Tag", pluck="name", order_by=None)
|
||||
|
|
|
|||
|
|
@ -452,7 +452,9 @@ def get_title_values_for_link_and_dynamic_link_fields(doc, link_fields=None):
|
|||
if not meta or not (meta.title_field and meta.show_title_field_in_link):
|
||||
continue
|
||||
|
||||
link_title = frappe.db.get_value(doctype, doc.get(field.fieldname), meta.title_field, cache=True)
|
||||
link_title = frappe.db.get_value(
|
||||
doctype, doc.get(field.fieldname), meta.title_field, cache=True, order_by=None
|
||||
)
|
||||
link_titles.update({doctype + "::" + doc.get(field.fieldname): link_title})
|
||||
|
||||
return link_titles
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ from frappe.model import optional_fields, table_fields
|
|||
from frappe.model.base_document import BaseDocument, get_controller
|
||||
from frappe.model.docstatus import DocStatus
|
||||
from frappe.model.naming import set_new_name, validate_name
|
||||
from frappe.model.utils import is_virtual_doctype
|
||||
from frappe.model.workflow import set_workflow_state_on_action, validate_workflow
|
||||
from frappe.utils import cstr, date_diff, file_lock, flt, get_datetime_str, now
|
||||
from frappe.utils.data import get_absolute_url
|
||||
|
|
@ -154,11 +155,7 @@ class Document(BaseDocument):
|
|||
# Make sure not to query the DB for a child table, if it is a virtual one.
|
||||
# During frappe is installed, the property "is_virtual" is not available in tabDocType, so
|
||||
# we need to filter those cases for the access to frappe.db.get_value() as it would crash otherwise.
|
||||
if (
|
||||
hasattr(self, "doctype")
|
||||
and not hasattr(self, "module")
|
||||
and frappe.db.get_value("DocType", df.options, "is_virtual", cache=True)
|
||||
):
|
||||
if hasattr(self, "doctype") and not hasattr(self, "module") and is_virtual_doctype(df.options):
|
||||
self.set(df.fieldname, [])
|
||||
continue
|
||||
|
||||
|
|
|
|||
|
|
@ -128,6 +128,6 @@ def get_fetch_values(doctype, fieldname, value):
|
|||
return result
|
||||
|
||||
|
||||
@site_cache(maxsize=128)
|
||||
@site_cache()
|
||||
def is_virtual_doctype(doctype):
|
||||
return frappe.db.get_value("DocType", doctype, "is_virtual")
|
||||
|
|
|
|||
|
|
@ -278,7 +278,10 @@ frappe.ui.form.Layout = class Layout {
|
|||
|
||||
make_section(df = {}) {
|
||||
this.section_count++;
|
||||
if (!df.fieldname) df.fieldname = `__section_${this.section_count}`;
|
||||
if (!df.fieldname) {
|
||||
df.fieldname = `__section_${this.section_count}`;
|
||||
df.fieldtype = "Section Break";
|
||||
}
|
||||
|
||||
this.section = new Section(
|
||||
this.current_tab ? this.current_tab.wrapper : this.page,
|
||||
|
|
@ -300,7 +303,10 @@ frappe.ui.form.Layout = class Layout {
|
|||
|
||||
make_column(df = {}) {
|
||||
this.column_count++;
|
||||
if (!df.fieldname) df.fieldname = `__column_${this.section_count}`;
|
||||
if (!df.fieldname) {
|
||||
df.fieldname = `__column_${this.section_count}`;
|
||||
df.fieldtype = "Column Break";
|
||||
}
|
||||
|
||||
this.column = new Column(this.section, df);
|
||||
if (df && df.fieldname) {
|
||||
|
|
|
|||
|
|
@ -25,8 +25,8 @@
|
|||
<li v-for="(column, index) in table_columns.filter(c => c.sortable)" :key="index" @click="query.sort = column.slug"><a class="option">{{ column.label }}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<button class="btn btn-default btn-xs btn-order">
|
||||
<span class="octicon text-muted" :class="query.order == 'asc' ? 'octicon-arrow-down' : 'octicon-arrow-up'" @click="query.order = (query.order == 'asc') ? 'desc' : 'asc'"></span>
|
||||
<button class="btn btn-default btn-xs btn-order" @click="query.order = (query.order == 'asc') ? 'desc' : 'asc'">
|
||||
<span class="octicon text-muted" :class="query.order == 'asc' ? 'octicon-arrow-down' : 'octicon-arrow-up'"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -95,13 +95,33 @@ def get_dom_id(seed=None):
|
|||
return "id-" + generate_hash(seed, 12)
|
||||
|
||||
|
||||
def include_script(path):
|
||||
def include_script(path, preload=True):
|
||||
"""Get path of bundled script files.
|
||||
|
||||
If preload is specified the path will be added to preload headers so browsers can prefetch
|
||||
assets."""
|
||||
path = bundled_asset(path)
|
||||
|
||||
if preload:
|
||||
import frappe
|
||||
|
||||
frappe.local.preload_assets["script"].append(path)
|
||||
|
||||
return f'<script type="text/javascript" src="{path}"></script>'
|
||||
|
||||
|
||||
def include_style(path, rtl=None):
|
||||
def include_style(path, rtl=None, preload=True):
|
||||
"""Get path of bundled style files.
|
||||
|
||||
If preload is specified the path will be added to preload headers so browsers can prefetch
|
||||
assets."""
|
||||
path = bundled_asset(path)
|
||||
|
||||
if preload:
|
||||
import frappe
|
||||
|
||||
frappe.local.preload_assets["style"].append(path)
|
||||
|
||||
return f'<link type="text/css" rel="stylesheet" href="{path}">'
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -273,7 +273,7 @@ def get_user_fullname(user: str) -> str:
|
|||
|
||||
def get_fullname_and_avatar(user: str) -> _dict:
|
||||
first_name, last_name, avatar, name = frappe.db.get_value(
|
||||
"User", user, ["first_name", "last_name", "user_image", "name"]
|
||||
"User", user, ["first_name", "last_name", "user_image", "name"], order_by=None
|
||||
)
|
||||
return _dict(
|
||||
{
|
||||
|
|
|
|||
|
|
@ -103,12 +103,12 @@ class WebsiteSettings(Document):
|
|||
def get_website_settings(context=None):
|
||||
hooks = frappe.get_hooks()
|
||||
context = frappe._dict(context or {})
|
||||
settings: "WebsiteSettings" = frappe.get_single("Website Settings")
|
||||
settings: "WebsiteSettings" = frappe.get_cached_doc("Website Settings")
|
||||
|
||||
context = context.update(
|
||||
{
|
||||
"top_bar_items": get_items("top_bar_items"),
|
||||
"footer_items": get_items("footer_items"),
|
||||
"top_bar_items": modify_header_footer_items(settings.top_bar_items),
|
||||
"footer_items": modify_header_footer_items(settings.footer_items),
|
||||
"post_login": [
|
||||
{"label": _("My Account"), "url": "/me"},
|
||||
{"label": _("Log out"), "url": "/?cmd=web_logout"},
|
||||
|
|
@ -203,22 +203,24 @@ def get_items(parentfield: str) -> list[dict]:
|
|||
order_by="idx asc",
|
||||
fields="*",
|
||||
)
|
||||
top_items = _items.copy()
|
||||
return modify_header_footer_items(_items)
|
||||
|
||||
|
||||
def modify_header_footer_items(items: list):
|
||||
top_items = items.copy()
|
||||
# attach child items to top bar
|
||||
for item in _items:
|
||||
if not item["parent_label"]:
|
||||
for item in items:
|
||||
if not item.parent_label:
|
||||
continue
|
||||
|
||||
for top_bar_item in top_items:
|
||||
if top_bar_item["label"] != item["parent_label"]:
|
||||
if top_bar_item.label != item.parent_label:
|
||||
continue
|
||||
|
||||
if "child_items" not in top_bar_item:
|
||||
if not top_bar_item.get("child_items"):
|
||||
top_bar_item["child_items"] = []
|
||||
|
||||
top_bar_item["child_items"].append(item)
|
||||
|
||||
top_bar_item.child_items.append(item)
|
||||
break
|
||||
|
||||
return top_items
|
||||
|
|
|
|||
|
|
@ -133,9 +133,9 @@ class WebsiteTheme(Document):
|
|||
|
||||
|
||||
def get_active_theme() -> Optional["WebsiteTheme"]:
|
||||
if website_theme := frappe.db.get_single_value("Website Settings", "website_theme"):
|
||||
if website_theme := frappe.get_website_settings("website_theme"):
|
||||
try:
|
||||
return frappe.get_doc("Website Theme", website_theme)
|
||||
return frappe.get_cached_doc("Website Theme", website_theme)
|
||||
except frappe.DoesNotExistError:
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,8 @@ UNSUPPORTED_STATIC_PAGE_TYPES = ("html", "md", "js", "xml", "css", "txt", "py",
|
|||
|
||||
|
||||
class StaticPage(BaseRenderer):
|
||||
__slots__ = ("path", "file_path")
|
||||
|
||||
def __init__(self, path, http_status_code=None):
|
||||
super().__init__(path=path, http_status_code=http_status_code)
|
||||
self.set_file_path()
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ from frappe.website.utils import can_cache, get_home_page
|
|||
|
||||
|
||||
class PathResolver:
|
||||
__slots__ = ("path",)
|
||||
|
||||
def __init__(self, path):
|
||||
self.path = path.strip("/ ")
|
||||
|
||||
|
|
@ -36,6 +38,11 @@ class PathResolver:
|
|||
return frappe.flags.redirect_location, RedirectPage(self.path)
|
||||
|
||||
endpoint = resolve_path(self.path)
|
||||
|
||||
# WARN: Hardcoded for better performance
|
||||
if endpoint == "app":
|
||||
return endpoint, TemplatePage(endpoint, 200)
|
||||
|
||||
custom_renderers = self.get_custom_page_renderers()
|
||||
renderers = custom_renderers + [
|
||||
StaticPage,
|
||||
|
|
@ -98,7 +105,7 @@ def resolve_redirect(path, query_string=None):
|
|||
]
|
||||
"""
|
||||
redirects = frappe.get_hooks("website_redirects")
|
||||
redirects += frappe.db.get_all("Website Route Redirect", ["source", "target"])
|
||||
redirects += frappe.get_all("Website Route Redirect", ["source", "target"], order_by=None)
|
||||
|
||||
if not redirects:
|
||||
return
|
||||
|
|
|
|||
|
|
@ -527,7 +527,8 @@ def build_response(path, data, http_status_code, headers: dict | None = None):
|
|||
response.headers["X-Page-Name"] = path.encode("ascii", errors="xmlcharrefreplace")
|
||||
response.headers["X-From-Cache"] = frappe.local.response.from_cache or False
|
||||
|
||||
add_preload_headers(response)
|
||||
add_preload_for_bundled_assets(response)
|
||||
|
||||
if headers:
|
||||
for key, val in headers.items():
|
||||
response.headers[key] = val.encode("ascii", errors="xmlcharrefreplace")
|
||||
|
|
@ -557,29 +558,18 @@ def set_content_type(response, data, path):
|
|||
return data
|
||||
|
||||
|
||||
def add_preload_headers(response):
|
||||
from bs4 import BeautifulSoup, SoupStrainer
|
||||
def add_preload_for_bundled_assets(response):
|
||||
|
||||
try:
|
||||
preload = []
|
||||
strainer = SoupStrainer(re.compile("script|link"))
|
||||
soup = BeautifulSoup(response.data, "html.parser", parse_only=strainer)
|
||||
for elem in soup.find_all("script", src=re.compile(".*")):
|
||||
preload.append(("script", elem.get("src")))
|
||||
links = []
|
||||
|
||||
for elem in soup.find_all("link", rel="stylesheet"):
|
||||
preload.append(("style", elem.get("href")))
|
||||
for css in frappe.local.preload_assets["style"]:
|
||||
links.append(f"<{css}>; rel=preload; as=style")
|
||||
|
||||
links = []
|
||||
for _type, link in preload:
|
||||
links.append(f"<{link}>; rel=preload; as={_type}")
|
||||
for js in frappe.local.preload_assets["script"]:
|
||||
links.append(f"<{js}>; rel=preload; as=script")
|
||||
|
||||
if links:
|
||||
response.headers["Link"] = ",".join(links)
|
||||
except Exception:
|
||||
import traceback
|
||||
|
||||
traceback.print_exc()
|
||||
if links:
|
||||
response.headers["Link"] = ",".join(links)
|
||||
|
||||
|
||||
@lru_cache
|
||||
|
|
|
|||
|
|
@ -18,7 +18,9 @@ CLOSING_SCRIPT_TAG_PATTERN = re.compile(r"</script\>")
|
|||
def get_context(context):
|
||||
if frappe.session.user == "Guest":
|
||||
frappe.throw(_("Log in to access this page."), frappe.PermissionError)
|
||||
elif frappe.db.get_value("User", frappe.session.user, "user_type") == "Website User":
|
||||
elif (
|
||||
frappe.db.get_value("User", frappe.session.user, "user_type", order_by=None) == "Website User"
|
||||
):
|
||||
frappe.throw(_("You are not permitted to access this page."), frappe.PermissionError)
|
||||
|
||||
hooks = frappe.get_hooks()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue