<commit_before># -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals

__author__ = "<NAME> <<EMAIL>>"
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
__copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License"

import os
import datetime
import io

from past.builtins import basestring

from collections import defaultdict
from flask import request, g, url_for, make_response, render_template, send_from_directory, redirect, abort

import octoprint.plugin

from octoprint.server import app, userManager, groupManager, pluginManager, gettext, \
	debug, LOCALES, VERSION, DISPLAY_VERSION, BRANCH, preemptiveCache, \
	NOT_MODIFIED
from octoprint.access.permissions import Permissions
from octoprint.settings import settings
from octoprint.filemanager import full_extension_tree, get_all_extensions
from octoprint.util import to_unicode, to_bytes, sv

import re
import base64

from . import util

import logging
_logger = logging.getLogger(__name__)

_templates = dict()
_plugin_names = None
_plugin_vars = None

_valid_id_re = re.compile("[a-z_]+")
_valid_div_re = re.compile("[a-zA-Z_-]+")

def _preemptive_unless(base_url=None, additional_unless=None):
	if base_url is None:
		base_url = request.url_root

	disabled_for_root = not settings().getBoolean(["devel", "cache", "preemptive"]) \
	                    or base_url in settings().get(["server", "preemptiveCache", "exceptions"]) \
	                    or not (base_url.startswith("http://") or base_url.startswith("https://"))

	recording_disabled = request.headers.get("X-Preemptive-Record", "yes") == "no"

	if callable(additional_unless):
		return recording_disabled or disabled_for_root or additional_unless()
	else:
		return recording_disabled or disabled_for_root

def _preemptive_data(key, path=None, base_url=None, data=None, additional_request_data=None):
	if path is None:
		path = request.path
	if base_url is None:
		base_url = request.url_root

	d = dict(path=path,
	         base_url=base_url,
	         query_string="l10n={}".format(g.locale.language if g.locale else "en"))

	if key != "_default":
		d["plugin"] = key

	# add data if we have any
	if data is not None:
		try:
			if callable(data):
				data = data()
			if data:
				if "query_string" in data:
					data["query_string"] = "l10n={}&{}".format(g.locale.language, data["query_string"])
				d.update(data)
		except Exception:
			_logger.exception("Error collecting data for preemptive cache from plugin {}".format(key))

	# add additional request data if we have any
	if callable(additional_request_data):
		try:
			ard = additional_request_data()
			if ard:
				d.update(dict(
					_additional_request_data=ard
				))
		except Exception:
			_logger.exception("Error retrieving additional data for preemptive cache from plugin {}".format(key))

	return d

def _cache_key(ui, url=None, locale=None, additional_key_data=None):
	if url is None:
		url = request.base_url
	if locale is None:
		locale = g.locale.language if g.locale else "en"

	k = "ui:{}:{}:{}".format(ui, url, locale)
	if callable(additional_key_data):
		try:
			ak = additional_key_data()
			if ak:
				# we have some additional key components, let's attach them
				if not isinstance(ak, (list, tuple)):
					ak = [ak]
				k = "{}:{}".format(k, ":".join(ak))
		except Exception:
			_logger.exception("Error while trying to retrieve additional cache key parts for ui {}".format(ui))
	return k

def _valid_status_for_cache(status_code):
	return 200 <= status_code < 400

@app.route("/cached.gif")
def in_cache():
	url = request.base_url.replace("/cached.gif", "/")
	path = request.path.replace("/cached.gif", "/")
	base_url = request.url_root

	# select view from plugins and fall back on default view if no plugin will handle it
	ui_plugins = pluginManager.get_implementations(octoprint.plugin.UiPlugin,
	                                               sorting_context="UiPlugin.on_ui_render")
	for plugin in ui_plugins:
		try:
			if plugin.will_handle_ui(request):
				ui = plugin._identifier
				key = _cache_key(plugin._identifier,
				                 url=url,
				                 additional_key_data=plugin.get_ui_additional_key_data_for_cache)
				unless = _preemptive_unless(url, additional_unless=plugin.get_ui_preemptive_caching_additional_unless)
				data = _preemptive_data(plugin._identifier,
				                        path=path,
				                        base_url=base_url,
				                        data=plugin.get_ui_data_for_preemptive_caching,
				                        additional_request_data=plugin.get_ui_additional_request_data_for_preemptive_caching)
				break
		except Exception:
			_logger.exception("Error while calling plugin {}, skipping it".format(plugin._identifier),
			                  extra=dict(plugin=plugin._identifier))
	else:
		ui = "_default"
		key = _cache_key("_default", url=url)
		unless = _preemptive_unless(url)
		data = _preemptive_data("_default", path=path, base_url=base_url)

	response = make_response(bytes(base64.b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")))
	response.headers["Content-Type"] = "image/gif"

	if unless or not preemptiveCache.has_record(data, root=path):
		_logger.info("Preemptive cache not active for path {}, ui {} and data {!r}, signaling as cached".format(path, ui, data))
		return response
	elif util.flask.is_in_cache(key):
		_logger.info("Found path {} in cache (key: {}), signaling as cached".format(path, key))
		return response
	elif util.flask.is_cache_bypassed(key):
		_logger.info("Path {} was bypassed from cache (key: {}), signaling as cached".format(path, key))
		return response
	else:
		_logger.debug("Path {} not yet cached (key: {}), signaling as missing".format(path, key))
		return abort(404)

@app.route("/")
def index():
	global _templates, _plugin_names, _plugin_vars

	preemptive_cache_enabled = settings().getBoolean(["devel", "cache", "preemptive"])

	locale = g.locale.language if g.locale else "en"

	# helper to check if wizards are active
	def wizard_active(templates):
		return templates is not None and bool(templates["wizard"]["order"])

	# we force a refresh if the client forces one or if we have wizards cached
	force_refresh = util.flask.cache_check_headers() or "_refresh" in request.values or wizard_active(_templates.get(locale))

	# if we need to refresh our template cache or it's not yet set, process it
	fetch_template_data(refresh=force_refresh)

	now = datetime.datetime.utcnow()

	enable_accesscontrol = userManager.enabled
	enable_gcodeviewer = settings().getBoolean(["gcodeViewer", "enabled"])
	enable_timelapse = settings().getBoolean(["webcam", "timelapseEnabled"])
	enable_loading_animation = settings().getBoolean(["devel", "showLoadingAnimation"])

	def default_template_filter(template_type, template_key):
		if template_type == "navbar":
			return template_key != "login" or enable_accesscontrol
		elif template_type == "tab":
			return (template_key != "gcodeviewer" or enable_gcodeviewer) and \
			       (template_key != "timelapse" or enable_timelapse)
		elif template_type == "settings":<commit_msg>♻ Do not invalidate / cache on hard reload if printing<commit_after># -*- coding: utf-8 -*-
from __future__ import absolute_import, division, print_function, unicode_literals

__author__ = "<NAME> <<EMAIL>>"
__license__ = 'GNU Affero General Public License http://www.gnu.org/licenses/agpl.html'
__copyright__ = "Copyright (C) 2015 The OctoPrint Project - Released under terms of the AGPLv3 License"

import os
import datetime
import io

from past.builtins import basestring

from collections import defaultdict
from flask import request, g, url_for, make_response, render_template, send_from_directory, redirect, abort

import octoprint.plugin

from octoprint.server import app, userManager, groupManager, pluginManager, gettext, \
	debug, LOCALES, VERSION, DISPLAY_VERSION, BRANCH, preemptiveCache, \
	NOT_MODIFIED
from octoprint.access.permissions import Permissions
from octoprint.settings import settings
from octoprint.filemanager import full_extension_tree, get_all_extensions
from octoprint.util import to_unicode, to_bytes, sv

import re
import base64

from . import util

import logging
_logger = logging.getLogger(__name__)

_templates = dict()
_plugin_names = None
_plugin_vars = None

_valid_id_re = re.compile("[a-z_]+")
_valid_div_re = re.compile("[a-zA-Z_-]+")

def _preemptive_unless(base_url=None, additional_unless=None):
	if base_url is None:
		base_url = request.url_root

	disabled_for_root = not settings().getBoolean(["devel", "cache", "preemptive"]) \
	                    or base_url in settings().get(["server", "preemptiveCache", "exceptions"]) \
	                    or not (base_url.startswith("http://") or base_url.startswith("https://"))

	recording_disabled = request.headers.get("X-Preemptive-Record", "yes") == "no"

	if callable(additional_unless):
		return recording_disabled or disabled_for_root or additional_unless()
	else:
		return recording_disabled or disabled_for_root

def _preemptive_data(key, path=None, base_url=None, data=None, additional_request_data=None):
	if path is None:
		path = request.path
	if base_url is None:
		base_url = request.url_root

	d = dict(path=path,
	         base_url=base_url,
	         query_string="l10n={}".format(g.locale.language if g.locale else "en"))

	if key != "_default":
		d["plugin"] = key

	# add data if we have any
	if data is not None:
		try:
			if callable(data):
				data = data()
			if data:
				if "query_string" in data:
					data["query_string"] = "l10n={}&{}".format(g.locale.language, data["query_string"])
				d.update(data)
		except Exception:
			_logger.exception("Error collecting data for preemptive cache from plugin {}".format(key))

	# add additional request data if we have any
	if callable(additional_request_data):
		try:
			ard = additional_request_data()
			if ard:
				d.update(dict(
					_additional_request_data=ard
				))
		except Exception:
			_logger.exception("Error retrieving additional data for preemptive cache from plugin {}".format(key))

	return d

def _cache_key(ui, url=None, locale=None, additional_key_data=None):
	if url is None:
		url = request.base_url
	if locale is None:
		locale = g.locale.language if g.locale else "en"

	k = "ui:{}:{}:{}".format(ui, url, locale)
	if callable(additional_key_data):
		try:
			ak = additional_key_data()
			if ak:
				# we have some additional key components, let's attach them
				if not isinstance(ak, (list, tuple)):
					ak = [ak]
				k = "{}:{}".format(k, ":".join(ak))
		except Exception:
			_logger.exception("Error while trying to retrieve additional cache key parts for ui {}".format(ui))
	return k

def _valid_status_for_cache(status_code):
	return 200 <= status_code < 400

@app.route("/cached.gif")
def in_cache():
	url = request.base_url.replace("/cached.gif", "/")
	path = request.path.replace("/cached.gif", "/")
	base_url = request.url_root

	# select view from plugins and fall back on default view if no plugin will handle it
	ui_plugins = pluginManager.get_implementations(octoprint.plugin.UiPlugin,
	                                               sorting_context="UiPlugin.on_ui_render")
	for plugin in ui_plugins:
		try:
			if plugin.will_handle_ui(request):
				ui = plugin._identifier
				key = _cache_key(plugin._identifier,
				                 url=url,
				                 additional_key_data=plugin.get_ui_additional_key_data_for_cache)
				unless = _preemptive_unless(url, additional_unless=plugin.get_ui_preemptive_caching_additional_unless)
				data = _preemptive_data(plugin._identifier,
				                        path=path,
				                        base_url=base_url,
				                        data=plugin.get_ui_data_for_preemptive_caching,
				                        additional_request_data=plugin.get_ui_additional_request_data_for_preemptive_caching)
				break
		except Exception:
			_logger.exception("Error while calling plugin {}, skipping it".format(plugin._identifier),
			                  extra=dict(plugin=plugin._identifier))
	else:
		ui = "_default"
		key = _cache_key("_default", url=url)
		unless = _preemptive_unless(url)
		data = _preemptive_data("_default", path=path, base_url=base_url)

	response = make_response(bytes(base64.b64decode("R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")))
	response.headers["Content-Type"] = "image/gif"

	if unless or not preemptiveCache.has_record(data, root=path):
		_logger.info("Preemptive cache not active for path {}, ui {} and data {!r}, signaling as cached".format(path, ui, data))
		return response
	elif util.flask.is_in_cache(key):
		_logger.info("Found path {} in cache (key: {}), signaling as cached".format(path, key))
		return response
	elif util.flask.is_cache_bypassed(key):
		_logger.info("Path {} was bypassed from cache (key: {}), signaling as cached".format(path, key))
		return response
	else:
		_logger.debug("Path {} not yet cached (key: {}), signaling as missing".format(path, key))
		return abort(404)

@app.route("/")
def index():
	from octoprint.server import printer

	global _templates, _plugin_names, _plugin_vars

	preemptive_cache_enabled = settings().getBoolean(["devel", "cache", "preemptive"])

	locale = g.locale.language if g.locale else "en"

	# helper to check if wizards are active
	def wizard_active(templates):
		return templates is not None and bool(templates["wizard"]["order"])

	# we force a refresh if the client forces one and we are not printing or if we have wizards cached
	client_refresh = util.flask.cache_check_headers()
	request_refresh = "_refresh" in request.values
	printing = printer.is_printing()
	if client_refresh and printing:
		logging.getLogger(__name__).warn("Client requested cache refresh via cache-control headers but we are printing. "
		                                 "Not invalidating caches due to resource limitation. Append ?_refresh=true to "
		                                 "the URL if you absolutely require a refresh now")
	client_refresh = client_refresh and not printing
	force_refresh = client_refresh \
	                or request_refresh \
	                or wizard_active(_templates.get(locale))

	# if we need to refresh our template cache or it's not yet set, process it
	fetch_template_data(refresh=force_refresh)

	now = datetime.datetime.utcnow()

	enable_accesscontrol = userManager.enabled
	enable_gcodeviewer = settings().getBoolean(["gcodeViewer", "enabled"])
	enable_timelapse = settings().getBoolean(["webcam", "timelapseEnabled"])
	enable_loading_animation = settings().getBoolean(["devel", "showLoadingAnimation"])

	def default_template_filter(template_type, template_key):
		if template_type == "navbar":
			return template_key != "login" or enable_accesscontrol
		elif template_type == "tab":
			return (template_key != "gcodeviewer" or enable_gcodeviewer) and \
			       (template_key != "timelapse" or enable_timelapse)
		elif template_type == "settings":
{'fraction_non_alphanumeric': 0.1075940949594361, 'fraction_numerical': 0.0037238994547147226, 'mean_word_length': 4.182288077188146, 'pattern_counts': {'":': 10, '<': 13, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4014240', 'n_tokens_mistral': 5063, 'n_tokens_neox': 4593, 'n_words': 1222}