Merge remote-tracking branch 'origin/devel' into devel
This commit is contained in:
commit
ad21bbb2ff
8 changed files with 248 additions and 25 deletions
|
|
@ -102,7 +102,7 @@
|
|||
|
||||
Instructs the server to decrease the message rate by 500ms.
|
||||
|
||||
.. sec-jsclient-socket-throttling:
|
||||
.. _sec-jsclient-socket-throttling:
|
||||
|
||||
Communication Throttling
|
||||
========================
|
||||
|
|
|
|||
|
|
@ -3,26 +3,87 @@
|
|||
:mod:`OctoPrint.timelapse`
|
||||
--------------------------
|
||||
|
||||
.. todo::
|
||||
.. js:function:: OctoPrint.timelapse.get(unrendered, opts)
|
||||
|
||||
Needs to be documented
|
||||
Get a list of all timelapses and the current timelapse config.
|
||||
|
||||
.. js:function:: OctoPrint.timelapse.get(opts)
|
||||
If ``unrendered`` is true, also retrieve the list of unrendered
|
||||
timelapses.
|
||||
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.timelapse.list(opts)
|
||||
|
||||
Get the lists of rendered and unrendered timelapses. The returned promis
|
||||
will be resolved with an object containing the properties ``rendered``
|
||||
which will have the list of rendered timelapses, and ``unrendered`` which
|
||||
will have the list of unrendered timelapses.
|
||||
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.timelapse.listRendered(opts)
|
||||
|
||||
Get the list of rendered timelapses.
|
||||
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.timelapse.listUnrendered(opts)
|
||||
|
||||
Get the list of unrendered timelapses.
|
||||
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.timelapse.download(filename, opts)
|
||||
|
||||
Download the rendered timelapse ``filename``.
|
||||
|
||||
:param string filename: The name of the rendered timelapse to download
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.timelapse.delete(filename, opts)
|
||||
|
||||
Delete the rendered timelapse ``filename``.
|
||||
|
||||
:param string filename: The name of the rendered timelapse to delete
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.timelapse.deleteUnrendered(name, opts)
|
||||
|
||||
Delete the unrendered timelapse ``name``.
|
||||
|
||||
:param string name: The name of the unrendered timelapse to delete
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.timelapse.renderUnrendered(name, opts)
|
||||
|
||||
Render the unrendered timelapse ``name``.
|
||||
|
||||
:param string name: The name of the unrendered timelapse to render
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.timelapse.getConfig(opts)
|
||||
|
||||
Get the current timelapse configuration.
|
||||
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.timelapse.saveConfig(config, opts)
|
||||
|
||||
Save the timelapse configuration.
|
||||
|
||||
:param object config: The config to save
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`Timelapse API <sec-api-timelapse>`
|
||||
|
|
|
|||
|
|
@ -3,31 +3,104 @@
|
|||
:mod:`OctoPrint.users`
|
||||
----------------------
|
||||
|
||||
.. todo::
|
||||
.. note::
|
||||
|
||||
Needs to be documented
|
||||
Most methods here require that the used API token or a the existing browser session
|
||||
has admin rights *or* corresponds to the user to be modified. Some methods
|
||||
definitely require admin rights.
|
||||
|
||||
.. js:function:: OctoPrint.users.list(opts)
|
||||
|
||||
Get a list of all registered users.
|
||||
|
||||
Requires admin rights.
|
||||
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.users.get(name, opts)
|
||||
|
||||
Get information about a specific user.
|
||||
|
||||
:param string name: The user's name
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.users.add(user, opts)
|
||||
|
||||
Add a new user.
|
||||
|
||||
Requires admin rights.
|
||||
|
||||
:param object user: The new user
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.users.update(name, active, admin, opts)
|
||||
|
||||
Update an existing user.
|
||||
|
||||
Requires admin rights.
|
||||
|
||||
:param string name: The user's name
|
||||
:param bool active: The new ``active`` state of the user
|
||||
:param bool admin: The new ``admin`` state of the user
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.users.delete(name, opts)
|
||||
|
||||
Delete an existing user.
|
||||
|
||||
Requires admin rights.
|
||||
|
||||
:param string name: The user's name
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.users.changePassword(name, password, opts)
|
||||
|
||||
Change the password for a user.
|
||||
|
||||
:param string name: The user's name
|
||||
:param string password: The new password
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.users.generateApiKey(name, opts)
|
||||
|
||||
Generate a new API key for a user.
|
||||
|
||||
:param string name: The user's name
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.users.resetApiKey(name, opts)
|
||||
|
||||
Reset the API key for a user to being unset.
|
||||
|
||||
:param string name: The user's name
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.users.getSettings(name, opts)
|
||||
|
||||
Get the settings for a user.
|
||||
|
||||
:param string name: The user's name
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.users.saveSettings(name, settings, opts)
|
||||
|
||||
Save the settings for a user.
|
||||
|
||||
:param string name: The user's name
|
||||
:param object settings: The new settings, may be a partial set of settings which will be merged unto the current ones
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`User API <sec-api-users>`
|
||||
:ref:`User API <sec-api-user>`
|
||||
The documentation of the underlying user API.
|
||||
|
|
|
|||
|
|
@ -3,10 +3,27 @@
|
|||
:mod:`OctoPrint.wizard`
|
||||
-----------------------
|
||||
|
||||
.. todo::
|
||||
.. note::
|
||||
|
||||
Needs to be documented
|
||||
All methods here require that the used API token or a the existing browser session
|
||||
has admin rights.
|
||||
|
||||
.. js:function:: OctoPrint.wizard.get(opts)
|
||||
|
||||
Retrieve additional data about registered wizards.
|
||||
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. js:function:: OctoPrint.wizard.finish(handled, opts)
|
||||
|
||||
Inform wizards that the wizard dialog has been finished.
|
||||
|
||||
:param list handled: List of identifiers of handled wizards
|
||||
:param object opts: Additional options for the request
|
||||
:returns Promise: A `jQuery Promise <http://api.jquery.com/Types/#Promise>`_ for the request's response
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`Wizard API <sec-api-wizard>`
|
||||
The documentation of the underlying wizard API.
|
||||
|
|
|
|||
|
|
@ -473,7 +473,9 @@ class LessSimpleCache(BaseCache):
|
|||
|
||||
def __init__(self, threshold=500, default_timeout=300):
|
||||
BaseCache.__init__(self, default_timeout=default_timeout)
|
||||
self._mutex = threading.RLock()
|
||||
self._cache = {}
|
||||
self._bypassed = set()
|
||||
self.clear = self._cache.clear
|
||||
self._threshold = threshold
|
||||
|
||||
|
|
@ -482,27 +484,35 @@ class LessSimpleCache(BaseCache):
|
|||
now = time.time()
|
||||
for idx, (key, (expires, _)) in enumerate(self._cache.items()):
|
||||
if expires is not None and expires <= now or idx % 3 == 0:
|
||||
self._cache.pop(key, None)
|
||||
with self._mutex:
|
||||
self._cache.pop(key, None)
|
||||
|
||||
def get(self, key):
|
||||
import pickle
|
||||
now = time.time()
|
||||
expires, value = self._cache.get(key, (0, None))
|
||||
with self._mutex:
|
||||
expires, value = self._cache.get(key, (0, None))
|
||||
if expires is None or expires > now:
|
||||
return pickle.loads(value)
|
||||
|
||||
def set(self, key, value, timeout=None):
|
||||
import pickle
|
||||
self._prune()
|
||||
self._cache[key] = (self.calculate_timeout(timeout=timeout),
|
||||
pickle.dumps(value, pickle.HIGHEST_PROTOCOL))
|
||||
|
||||
with self._mutex:
|
||||
self._prune()
|
||||
self._cache[key] = (self.calculate_timeout(timeout=timeout),
|
||||
pickle.dumps(value, pickle.HIGHEST_PROTOCOL))
|
||||
if key in self._bypassed:
|
||||
self._bypassed.remove(key)
|
||||
|
||||
def add(self, key, value, timeout=None):
|
||||
self.set(key, value, timeout=None)
|
||||
self._cache.setdefault(key, self._cache[key])
|
||||
with self._mutex:
|
||||
self.set(key, value, timeout=None)
|
||||
self._cache.setdefault(key, self._cache[key])
|
||||
|
||||
def delete(self, key):
|
||||
self._cache.pop(key, None)
|
||||
with self._mutex:
|
||||
self._cache.pop(key, None)
|
||||
|
||||
def calculate_timeout(self, timeout=None):
|
||||
if timeout is None:
|
||||
|
|
@ -514,7 +524,8 @@ class LessSimpleCache(BaseCache):
|
|||
def over_threshold(self):
|
||||
if self._threshold is None:
|
||||
return False
|
||||
return len(self._cache) > self._threshold
|
||||
with self._mutex:
|
||||
return len(self._cache) > self._threshold
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.get(key)
|
||||
|
|
@ -526,7 +537,16 @@ class LessSimpleCache(BaseCache):
|
|||
return self.delete(key)
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self._cache
|
||||
with self._mutex:
|
||||
return key in self._cache
|
||||
|
||||
def set_bypassed(self, key):
|
||||
with self._mutex:
|
||||
self._bypassed.add(key)
|
||||
|
||||
def is_bypassed(self, key):
|
||||
with self._mutex:
|
||||
return key in self._bypassed
|
||||
|
||||
_cache = LessSimpleCache()
|
||||
|
||||
|
|
@ -552,11 +572,13 @@ def cached(timeout=5 * 60, key=lambda: "view:%s" % flask.request.path, unless=No
|
|||
# bypass the cache if "unless" condition is true
|
||||
if callable(unless) and unless():
|
||||
logger.debug("Cache for {path} bypassed, calling wrapped function".format(path=flask.request.path))
|
||||
_cache.set_bypassed(cache_key)
|
||||
return f_with_duration(*args, **kwargs)
|
||||
|
||||
# also bypass the cache if it's disabled completely
|
||||
if not settings().getBoolean(["devel", "cache", "enabled"]):
|
||||
logger.debug("Cache for {path} disabled, calling wrapped function".format(path=flask.request.path))
|
||||
_cache.set_bypassed(cache_key)
|
||||
return f_with_duration(*args, **kwargs)
|
||||
|
||||
rv = _cache.get(cache_key)
|
||||
|
|
@ -575,6 +597,7 @@ def cached(timeout=5 * 60, key=lambda: "view:%s" % flask.request.path, unless=No
|
|||
# do not store if the "unless_response" condition is true
|
||||
if callable(unless_response) and unless_response(rv):
|
||||
logger.debug("Not caching result for {path} (key: {key}), bypassed".format(path=flask.request.path, key=cache_key))
|
||||
_cache.set_bypassed(cache_key)
|
||||
return rv
|
||||
|
||||
# store it in the cache
|
||||
|
|
@ -591,6 +614,11 @@ def is_in_cache(key=lambda: "view:%s" % flask.request.path):
|
|||
key = key()
|
||||
return key in _cache
|
||||
|
||||
def is_cache_bypassed(key=lambda: "view:%s" % flask.request.path):
|
||||
if callable(key):
|
||||
key = key()
|
||||
return _cache.is_bypassed(key)
|
||||
|
||||
def cache_check_headers():
|
||||
return "no-cache" in flask.request.cache_control or "no-cache" in flask.request.pragma
|
||||
|
||||
|
|
|
|||
|
|
@ -146,6 +146,9 @@ def in_cache():
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -7,25 +7,52 @@
|
|||
})(window || this, function(OctoPrint, $) {
|
||||
var url = "api/timelapse";
|
||||
|
||||
var downloadUrl = "downloads/timelapse";
|
||||
|
||||
var timelapseUrl = function(filename) {
|
||||
return url + "/" + filename;
|
||||
};
|
||||
|
||||
var timelapseDownloadUrl = function(filename) {
|
||||
return downloadUrl + "/" + filename;
|
||||
};
|
||||
|
||||
var unrenderedTimelapseUrl = function(name) {
|
||||
return url + "/unrendered/" + name;
|
||||
};
|
||||
|
||||
var getTimelapseData = function (opts) {
|
||||
var getTimelapseData = function (unrendered, opts) {
|
||||
if (unrendered) {
|
||||
opts = opts || {};
|
||||
opts.data = {unrendered: unrendered};
|
||||
}
|
||||
return OctoPrint.get(url, opts);
|
||||
};
|
||||
|
||||
OctoPrint.timelapse = {
|
||||
get: getTimelapseData,
|
||||
|
||||
list: function (opts) {
|
||||
list: function(opts) {
|
||||
var deferred = $.Deferred();
|
||||
|
||||
getTimelapseData(opts)
|
||||
getTimelapseData(true, opts)
|
||||
.done(function (response, status, request) {
|
||||
deferred.resolve({
|
||||
rendered: response.files,
|
||||
unrendered: response.unrendered
|
||||
}, status, request);
|
||||
})
|
||||
.fail(function () {
|
||||
deferred.reject.apply(null, arguments);
|
||||
});
|
||||
|
||||
return deferred.promise();
|
||||
},
|
||||
|
||||
listRendered: function (opts) {
|
||||
var deferred = $.Deferred();
|
||||
|
||||
getTimelapseData(false, opts)
|
||||
.done(function (response, status, request) {
|
||||
deferred.resolve(response.files, status, request);
|
||||
})
|
||||
|
|
@ -36,8 +63,22 @@
|
|||
return deferred.promise();
|
||||
},
|
||||
|
||||
listUnrendered: function (opts) {
|
||||
var deferred = $.Deferred();
|
||||
|
||||
getTimelapseData(true, opts)
|
||||
.done(function (response, status, request) {
|
||||
deferred.resolve(response.unrendered, status, request);
|
||||
})
|
||||
.fail(function () {
|
||||
deferred.reject.apply(null, arguments);
|
||||
});
|
||||
|
||||
return deferred.promise();
|
||||
},
|
||||
|
||||
download: function (filename, opts) {
|
||||
return OctoPrint.download(timelapseUrl(filename), opts);
|
||||
return OctoPrint.download(timelapseDownloadUrl(filename), opts);
|
||||
},
|
||||
|
||||
delete: function (filename, opts) {
|
||||
|
|
@ -54,7 +95,7 @@
|
|||
|
||||
getConfig: function (opts) {
|
||||
var deferred = $.Deferred();
|
||||
getTimelapseData(opts)
|
||||
getTimelapseData(false, opts)
|
||||
.done(function (response, status, request) {
|
||||
deferred.resolve(response.config, status, request);
|
||||
})
|
||||
|
|
|
|||
|
|
@ -125,7 +125,7 @@ $(function() {
|
|||
);
|
||||
|
||||
self.requestData = function() {
|
||||
OctoPrint.timelapse.get({ data: { unrendered: true} })
|
||||
OctoPrint.timelapse.get(true)
|
||||
.done(self.fromResponse);
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue