Merge pull request #29159 from ankush/perf/faster_render
perf: ~3x faster `/app` loading
This commit is contained in:
commit
9fac1a06f6
12 changed files with 113 additions and 69 deletions
|
|
@ -385,25 +385,6 @@ def load_print_css(bootinfo, print_settings):
|
|||
)
|
||||
|
||||
|
||||
def get_unseen_notes():
|
||||
note = DocType("Note")
|
||||
nsb = DocType("Note Seen By").as_("nsb")
|
||||
|
||||
return (
|
||||
frappe.qb.from_(note)
|
||||
.select(note.name, note.title, note.content, note.notify_on_every_login)
|
||||
.where(
|
||||
(note.notify_on_login == 1)
|
||||
& (note.expire_notification_on > frappe.utils.now())
|
||||
& (
|
||||
ParameterizedValueWrapper(frappe.session.user).notin(
|
||||
SubQuery(frappe.qb.from_(nsb).select(nsb.user).where(nsb.parent == note.name))
|
||||
)
|
||||
)
|
||||
)
|
||||
).run(as_dict=1)
|
||||
|
||||
|
||||
def get_success_action():
|
||||
return frappe.get_all("Success Action", fields=["*"])
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
UNSEEN_NOTES_KEY = "unseen_notes::"
|
||||
|
||||
|
||||
class Note(Document):
|
||||
# begin: auto-generated types
|
||||
|
|
@ -39,6 +41,10 @@ class Note(Document):
|
|||
self.print_heading = self.name
|
||||
self.sub_heading = ""
|
||||
|
||||
def clear_cache(self):
|
||||
frappe.cache.delete_keys(UNSEEN_NOTES_KEY)
|
||||
return super().clear_cache()
|
||||
|
||||
def mark_seen_by(self, user: str) -> None:
|
||||
if user in [d.user for d in self.seen_by]:
|
||||
return
|
||||
|
|
@ -62,3 +68,33 @@ def get_permission_query_conditions(user):
|
|||
|
||||
def has_permission(doc, user):
|
||||
return bool(doc.public or doc.owner == user)
|
||||
|
||||
|
||||
def get_unseen_notes():
|
||||
from frappe.query_builder.terms import ParameterizedValueWrapper, SubQuery
|
||||
|
||||
def _get_unseen_notes():
|
||||
note = frappe.qb.DocType("Note")
|
||||
nsb = frappe.qb.DocType("Note Seen By").as_("nsb")
|
||||
|
||||
return (
|
||||
frappe.qb.from_(note)
|
||||
.select(note.name, note.title, note.content, note.notify_on_every_login)
|
||||
.where(
|
||||
(note.notify_on_login == 1)
|
||||
& (note.expire_notification_on > frappe.utils.now())
|
||||
& (
|
||||
ParameterizedValueWrapper(frappe.session.user).notin(
|
||||
SubQuery(frappe.qb.from_(nsb).select(nsb.user).where(nsb.parent == note.name))
|
||||
)
|
||||
)
|
||||
)
|
||||
).run(as_dict=1)
|
||||
|
||||
return (
|
||||
frappe.cache.get_value(
|
||||
f"{UNSEEN_NOTES_KEY}{frappe.session.user}",
|
||||
generator=_get_unseen_notes,
|
||||
)
|
||||
or []
|
||||
)
|
||||
|
|
|
|||
|
|
@ -126,7 +126,8 @@ def clear_expired_sessions():
|
|||
|
||||
def get():
|
||||
"""get session boot info"""
|
||||
from frappe.boot import get_bootinfo, get_unseen_notes
|
||||
from frappe.boot import get_bootinfo
|
||||
from frappe.desk.doctype.note.note import get_unseen_notes
|
||||
from frappe.utils.change_log import get_change_log
|
||||
|
||||
bootinfo = None
|
||||
|
|
@ -175,9 +176,9 @@ def get():
|
|||
"default_path": get_default_path() or "",
|
||||
}
|
||||
|
||||
bootinfo["desk_theme"] = frappe.db.get_value("User", frappe.session.user, "desk_theme") or "Light"
|
||||
bootinfo["desk_theme"] = frappe.get_cached_value("User", frappe.session.user, "desk_theme") or "Light"
|
||||
bootinfo["user"]["impersonated_by"] = frappe.session.data.get("impersonated_by")
|
||||
bootinfo["navbar_settings"] = frappe.get_cached_doc("Navbar Settings")
|
||||
bootinfo["navbar_settings"] = frappe.client_cache.get_doc("Navbar Settings")
|
||||
bootinfo.has_app_updates = has_app_update_notifications()
|
||||
|
||||
return bootinfo
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import frappe
|
||||
from frappe.boot import get_unseen_notes, get_user_pages_or_reports
|
||||
from frappe.desk.doctype.note.note import mark_as_seen
|
||||
from frappe.boot import get_user_pages_or_reports
|
||||
from frappe.desk.doctype.note.note import get_unseen_notes, mark_as_seen
|
||||
from frappe.tests import IntegrationTestCase
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,9 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: MIT. See LICENSE
|
||||
import frappe
|
||||
from frappe.utils.caching import site_cache
|
||||
|
||||
|
||||
def get_jenv():
|
||||
import frappe
|
||||
|
||||
|
|
@ -88,22 +92,23 @@ def render_template(template, context=None, is_path=None, safe_render=True):
|
|||
if context is None:
|
||||
context = {}
|
||||
|
||||
jenv: SandboxedEnvironment = get_jenv()
|
||||
if is_path or guess_is_path(template):
|
||||
is_path = True
|
||||
compiled_template = jenv.get_template(template)
|
||||
else:
|
||||
if safe_render and ".__" in template:
|
||||
throw(_("Illegal template"))
|
||||
try:
|
||||
compiled_template = jenv.from_string(template)
|
||||
except TemplateError:
|
||||
import html
|
||||
try:
|
||||
if is_path or guess_is_path(template):
|
||||
is_path = True
|
||||
compiled_template = compile_template(template)
|
||||
else:
|
||||
jenv: SandboxedEnvironment = get_jenv()
|
||||
if safe_render and ".__" in template:
|
||||
throw(_("Illegal template"))
|
||||
|
||||
throw(
|
||||
title="Jinja Template Error",
|
||||
msg=f"<pre>{template}</pre><pre>{html.escape(get_traceback())}</pre>",
|
||||
)
|
||||
compiled_template = jenv.from_string(template)
|
||||
except TemplateError:
|
||||
import html
|
||||
|
||||
throw(
|
||||
title="Jinja Template Error",
|
||||
msg=f"<pre>{template}</pre><pre>{html.escape(get_traceback())}</pre>",
|
||||
)
|
||||
|
||||
import time
|
||||
|
||||
|
|
@ -136,30 +141,38 @@ def guess_is_path(template):
|
|||
|
||||
|
||||
def get_jloader():
|
||||
jloader = _get_jloader()
|
||||
frappe.local.jloader = jloader # backward compat
|
||||
return jloader
|
||||
|
||||
|
||||
@site_cache(ttl=10 * 60, maxsize=16)
|
||||
def compile_template(path):
|
||||
jenv = get_jenv()
|
||||
return jenv.get_template(path)
|
||||
|
||||
|
||||
@site_cache(ttl=10 * 60, maxsize=8)
|
||||
def _get_jloader():
|
||||
from jinja2 import ChoiceLoader, PackageLoader, PrefixLoader
|
||||
|
||||
import frappe
|
||||
|
||||
if not getattr(frappe.local, "jloader", None):
|
||||
from jinja2 import ChoiceLoader, PackageLoader, PrefixLoader
|
||||
apps = frappe.get_hooks("template_apps")
|
||||
if not apps:
|
||||
apps = list(reversed(frappe.get_installed_apps(_ensure_on_bench=True)))
|
||||
|
||||
apps = frappe.get_hooks("template_apps")
|
||||
if not apps:
|
||||
apps = list(
|
||||
reversed(
|
||||
frappe.local.flags.web_pages_apps or frappe.get_installed_apps(_ensure_on_bench=True)
|
||||
)
|
||||
)
|
||||
if "frappe" not in apps:
|
||||
apps.append("frappe")
|
||||
|
||||
if "frappe" not in apps:
|
||||
apps.append("frappe")
|
||||
jloader = ChoiceLoader(
|
||||
# search for something like app/templates/...
|
||||
[PrefixLoader({app: PackageLoader(app, ".") for app in apps})]
|
||||
# search for something like templates/...
|
||||
+ [PackageLoader(app, ".") for app in apps]
|
||||
)
|
||||
|
||||
frappe.local.jloader = ChoiceLoader(
|
||||
# search for something like app/templates/...
|
||||
[PrefixLoader({app: PackageLoader(app, ".") for app in apps})]
|
||||
# search for something like templates/...
|
||||
+ [PackageLoader(app, ".") for app in apps]
|
||||
)
|
||||
|
||||
return frappe.local.jloader
|
||||
return jloader
|
||||
|
||||
|
||||
def set_filters(jenv):
|
||||
|
|
|
|||
|
|
@ -287,8 +287,8 @@ 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"], order_by=None
|
||||
first_name, last_name, avatar, name = frappe.get_cached_value(
|
||||
"User", user, ["first_name", "last_name", "user_image", "name"]
|
||||
)
|
||||
return _dict(
|
||||
{
|
||||
|
|
|
|||
|
|
@ -20,3 +20,9 @@ class WebsiteRouteMeta(Document):
|
|||
def autoname(self):
|
||||
if self.name and self.name.startswith("/"):
|
||||
self.name = self.name[1:]
|
||||
|
||||
def clear_cache(self):
|
||||
from frappe.website.website_components.metatags import has_meta_tags
|
||||
|
||||
has_meta_tags.clear_cache()
|
||||
return super().clear_cache()
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ class TemplatePage(BaseTemplatePage):
|
|||
|
||||
def setup_template_source(self):
|
||||
"""Setup template source, frontmatter and markdown conversion"""
|
||||
self.source = self.get_raw_template()
|
||||
self.original_source = self.source = self.get_raw_template()
|
||||
self.extract_frontmatter()
|
||||
self.convert_from_markdown()
|
||||
|
||||
|
|
@ -233,7 +233,10 @@ class TemplatePage(BaseTemplatePage):
|
|||
else:
|
||||
safe_render = True
|
||||
|
||||
html = frappe.render_template(self.source, self.context, safe_render=safe_render)
|
||||
src_modified = self.source is not self.original_source
|
||||
html = frappe.render_template(
|
||||
self.source if src_modified else self.context.template, self.context, safe_render=safe_render
|
||||
)
|
||||
|
||||
return html
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ def get_pages(app=None):
|
|||
if app:
|
||||
apps = [app]
|
||||
else:
|
||||
apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps()
|
||||
apps = frappe.get_installed_apps()
|
||||
|
||||
for app in apps:
|
||||
app_path = frappe.get_app_path(app)
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ def get_frontmatter(string):
|
|||
body = result.group(2)
|
||||
|
||||
return {
|
||||
"attributes": yaml.safe_load(frontmatter),
|
||||
"attributes": yaml.safe_load(frontmatter) if frontmatter else "",
|
||||
"body": body,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import frappe
|
||||
from frappe.utils.caching import site_cache
|
||||
|
||||
METATAGS = ("title", "description", "image", "author", "published_on")
|
||||
|
||||
|
|
@ -58,14 +59,17 @@ class MetaTags:
|
|||
route = self.path
|
||||
if route == "":
|
||||
# homepage
|
||||
route = frappe.db.get_single_value("Website Settings", "home_page")
|
||||
route = frappe.get_website_settings("home_page")
|
||||
|
||||
route_exists = (
|
||||
route and not route.endswith((".js", ".css")) and frappe.db.exists("Website Route Meta", route)
|
||||
)
|
||||
route_exists = route and not route.endswith((".js", ".css")) and has_meta_tags(route)
|
||||
|
||||
if route_exists:
|
||||
website_route_meta = frappe.get_doc("Website Route Meta", route)
|
||||
for meta_tag in website_route_meta.meta_tags:
|
||||
d = meta_tag.get_meta_dict()
|
||||
self.tags.update(d)
|
||||
|
||||
|
||||
@site_cache(ttl=10 * 60, maxsize=16)
|
||||
def has_meta_tags(route):
|
||||
return bool(frappe.db.exists("Website Route Meta", route))
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ def get_context(context):
|
|||
frappe.msgprint(_("Log in to access this page."))
|
||||
frappe.redirect(f"/login?{urlencode({'redirect-to': frappe.request.path})}")
|
||||
|
||||
elif frappe.db.get_value("User", frappe.session.user, "user_type", order_by=None) == "Website User":
|
||||
elif frappe.session.data.user_type == "Website User":
|
||||
frappe.throw(_("You are not permitted to access this page."), frappe.PermissionError)
|
||||
|
||||
try:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue