Merge branch 'maintenance' into devel

Conflicts:
	src/octoprint/static/js/app/dataupdater.js
	src/octoprint/static/js/app/viewmodels/timelapse.js
This commit is contained in:
Gina Häußge 2016-06-01 13:29:56 +02:00
commit 7d52aa4be2
5 changed files with 160 additions and 104 deletions

View file

@ -112,7 +112,7 @@ class EventManager(object):
self._registeredListeners = collections.defaultdict(list) self._registeredListeners = collections.defaultdict(list)
self._logger = logging.getLogger(__name__) self._logger = logging.getLogger(__name__)
self._queue = Queue.PriorityQueue() self._queue = Queue.Queue()
self._worker = threading.Thread(target=self._work) self._worker = threading.Thread(target=self._work)
self._worker.daemon = True self._worker.daemon = True
self._worker.start() self._worker.start()
@ -120,7 +120,7 @@ class EventManager(object):
def _work(self): def _work(self):
try: try:
while True: while True:
(event, payload) = self._queue.get(True) event, payload = self._queue.get(True)
eventListeners = self._registeredListeners[event] eventListeners = self._registeredListeners[event]
self._logger.debug("Firing event: %s (Payload: %r)" % (event, payload)) self._logger.debug("Firing event: %s (Payload: %r)" % (event, payload))
@ -149,7 +149,7 @@ class EventManager(object):
payload being a payload object specific to the event. payload being a payload object specific to the event.
""" """
self._queue.put((event, payload), 0) self._queue.put((event, payload))
if event == Events.UPDATED_FILES and "type" in payload and payload["type"] == "printables": if event == Events.UPDATED_FILES and "type" in payload and payload["type"] == "printables":
# when sending UpdatedFiles with type "printables", also send another event with deprecated type "gcode" # when sending UpdatedFiles with type "printables", also send another event with deprecated type "gcode"
@ -157,8 +157,7 @@ class EventManager(object):
import copy import copy
legacy_payload = copy.deepcopy(payload) legacy_payload = copy.deepcopy(payload)
legacy_payload["type"] = "gcode" legacy_payload["type"] = "gcode"
self._queue.put((event, legacy_payload), 0) self._queue.put((event, legacy_payload))
def subscribe(self, event, callback): def subscribe(self, event, callback):
""" """

View file

@ -533,7 +533,10 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback):
self.refresh_sd_files(blocking=True) self.refresh_sd_files(blocking=True)
existingSdFiles = map(lambda x: x[0], self._comm.getSdFiles()) existingSdFiles = map(lambda x: x[0], self._comm.getSdFiles())
remoteName = util.get_dos_filename(filename, existing_filenames=existingSdFiles, extension="gco") remoteName = util.get_dos_filename(filename,
existing_filenames=existingSdFiles,
extension="gco",
whitelisted_extensions=["gco", "g"])
self._timeEstimationData = TimeEstimationHelper() self._timeEstimationData = TimeEstimationHelper()
self._comm.startFileTransfer(absolutePath, filename, "/" + remoteName) self._comm.startFileTransfer(absolutePath, filename, "/" + remoteName)

View file

@ -11,8 +11,6 @@ function DataUpdater(allViewModels) {
self._lastProcessingTimes = []; self._lastProcessingTimes = [];
self._lastProcessingTimesSize = 20; self._lastProcessingTimesSize = 20;
self._timelapse_popup = undefined;
self.increaseThrottle = function() { self.increaseThrottle = function() {
self.setThrottle(self._throttleFactor + 1); self.setThrottle(self._throttleFactor + 1);
}; };
@ -156,7 +154,6 @@ function DataUpdater(allViewModels) {
var type = event.data["type"]; var type = event.data["type"];
var payload = event.data["payload"]; var payload = event.data["payload"];
var html = ""; var html = "";
var format = {};
log.debug("Got event " + type + " with payload: " + JSON.stringify(payload)); log.debug("Got event " + type + " with payload: " + JSON.stringify(payload));
@ -164,91 +161,6 @@ function DataUpdater(allViewModels) {
if (payload && payload.hasOwnProperty("config_hash")) { if (payload && payload.hasOwnProperty("config_hash")) {
self._configHash = payload.config_hash; self._configHash = payload.config_hash;
} }
} else if (type == "MovieRendering") {
if (self._timelapse_popup !== undefined) {
self._timelapse_popup.remove();
}
self._timelapse_popup = new PNotify({
title: gettext("Rendering timelapse"),
text: _.sprintf(gettext("Now rendering timelapse %(movie_basename)s. Due to performance reasons it is not recommended to start a print job while a movie is still rendering."), payload),
hide: false,
callbacks: {
before_close: function() {
self._timelapse_popup = undefined;
}
}
});
} else if (type == "MovieDone") {
if (self._timelapse_popup !== undefined) {
self._timelapse_popup.remove();
}
self._timelapse_popup = new PNotify({
title: gettext("Timelapse ready"),
text: _.sprintf(gettext("New timelapse %(movie_basename)s is done rendering."), payload),
type: "success",
callbacks: {
before_close: function(notice) {
if (self._timelapse_popup == notice) {
self._timelapse_popup = undefined;
}
}
}
});
} else if (type == "MovieFailed") {
html = "<p>" + _.sprintf(gettext("Rendering of timelapse %(movie_basename)s failed with return code %(returncode)s"), payload) + "</p>";
html += pnotifyAdditionalInfo('<pre style="overflow: auto">' + payload.error + '</pre>');
if (self._timelapse_popup !== undefined) {
self._timelapse_popup.remove();
}
self._timelapse_popup = new PNotify({
title: gettext("Rendering failed"),
text: html,
type: "error",
hide: false,
callbacks: {
before_close: function(notice) {
if (self._timelapse_popup == notice) {
self._timelapse_popup = undefined;
}
}
}
});
} else if (type == "PostRollStart") {
var title = gettext("Capturing timelapse postroll");
var text;
if (!payload.postroll_duration) {
text = _.sprintf(gettext("Now capturing timelapse post roll, this will take only a moment..."), format);
} else {
format = {
time: moment().add(payload.postroll_duration, "s").format("LT")
};
if (payload.postroll_duration > 60) {
format.duration = _.sprintf(gettext("%(minutes)d min"), {minutes: payload.postroll_duration / 60});
text = _.sprintf(gettext("Now capturing timelapse post roll, this will take approximately %(duration)s (so until %(time)s)..."), format);
} else {
format.duration = _.sprintf(gettext("%(seconds)d sec"), {seconds: payload.postroll_duration});
text = _.sprintf(gettext("Now capturing timelapse post roll, this will take approximately %(duration)s..."), format);
}
}
if (self._timelapse_popup !== undefined) {
self._timelapse_popup.remove();
}
self._timelapse_popup = new PNotify({
title: title,
text: text,
hide: false,
callbacks: {
before_close: function(notice) {
if (self._timelapse_popup == notice) {
self._timelapse_popup = undefined;
}
}
}
});
} else if (type == "SlicingStarted") { } else if (type == "SlicingStarted") {
gcodeUploadProgress.addClass("progress-striped").addClass("active"); gcodeUploadProgress.addClass("progress-striped").addClass("active");
gcodeUploadProgressBar.css("width", "100%"); gcodeUploadProgressBar.css("width", "100%");

View file

@ -4,6 +4,8 @@ $(function() {
self.loginState = parameters[0]; self.loginState = parameters[0];
self.timelapsePopup = undefined;
self.defaultFps = 25; self.defaultFps = 25;
self.defaultPostRoll = 0; self.defaultPostRoll = 0;
self.defaultInterval = 10; self.defaultInterval = 10;
@ -222,11 +224,88 @@ $(function() {
.done(self.fromResponse); .done(self.fromResponse);
}; };
self.displayTimelapsePopup = function(options) {
if (self.timelapsePopup !== undefined) {
self.timelapsePopup.remove();
}
_.extend(options, {
callbacks: {
before_close: function(notice) {
if (self.timelapsePopup == notice) {
self.timelapsePopup = undefined;
}
}
}
});
self.timelapsePopup = new PNotify(options);
};
self.onDataUpdaterReconnect = function() { self.onDataUpdaterReconnect = function() {
self.requestData(); self.requestData();
}; };
self.onEventMovieDone = function() { self.onEventPostRollStart = function(payload) {
var title = gettext("Capturing timelapse postroll");
var text;
if (!payload.postroll_duration) {
text = _.sprintf(gettext("Now capturing timelapse post roll, this will take only a moment..."), format);
} else {
var format = {
time: moment().add(payload.postroll_duration, "s").format("LT")
};
if (payload.postroll_duration > 60) {
format.duration = _.sprintf(gettext("%(minutes)d min"), {minutes: payload.postroll_duration / 60});
text = _.sprintf(gettext("Now capturing timelapse post roll, this will take approximately %(duration)s (so until %(time)s)..."), format);
} else {
format.duration = _.sprintf(gettext("%(seconds)d sec"), {seconds: payload.postroll_duration});
text = _.sprintf(gettext("Now capturing timelapse post roll, this will take approximately %(duration)s..."), format);
}
}
self.displayTimelapsePopup({
title: title,
text: text,
hide: false
});
};
self.onEventMovieRendering = function(payload) {
self.displayTimelapsePopup({
title: gettext("Rendering timelapse"),
text: _.sprintf(gettext("Now rendering timelapse %(movie_basename)s. Due to performance reasons it is not recommended to start a print job while a movie is still rendering."), payload),
hide: false
});
};
self.onEventMovieFailed = function(payload) {
var html = "<p>" + _.sprintf(gettext("Rendering of timelapse %(movie_basename)s failed with return code %(returncode)s"), payload) + "</p>";
html += pnotifyAdditionalInfo('<pre style="overflow: auto">' + payload.error + '</pre>');
self.displayTimelapsePopup({
title: gettext("Rendering failed"),
text: html,
type: "error",
hide: false
});
};
self.onEventMovieDone = function(payload) {
self.displayTimelapsePopup({
title: gettext("Timelapse ready"),
text: _.sprintf(gettext("New timelapse %(movie_basename)s is done rendering."), payload),
type: "success",
callbacks: {
before_close: function(notice) {
if (self.timelapsePopup == notice) {
self.timelapsePopup = undefined;
}
}
}
});
self.requestData(); self.requestData();
}; };

View file

@ -206,7 +206,7 @@ def get_free_bytes(path):
return psutil.disk_usage(path).free return psutil.disk_usage(path).free
def get_dos_filename(origin, existing_filenames=None, extension=None, **kwargs): def get_dos_filename(input, existing_filenames=None, extension=None, whitelisted_extensions=None, **kwargs):
""" """
Converts the provided input filename to a 8.3 DOS compatible filename. If ``existing_filenames`` is provided, the Converts the provided input filename to a 8.3 DOS compatible filename. If ``existing_filenames`` is provided, the
conversion result will be guaranteed not to collide with any of the filenames provided thus. conversion result will be guaranteed not to collide with any of the filenames provided thus.
@ -219,6 +219,8 @@ def get_dos_filename(origin, existing_filenames=None, extension=None, **kwargs):
Optional. Optional.
extension (string): The .3 file extension to use for the generated filename. If not provided, the extension of extension (string): The .3 file extension to use for the generated filename. If not provided, the extension of
the provided ``filename`` will simply be truncated to 3 characters. the provided ``filename`` will simply be truncated to 3 characters.
whitelisted_extensions (list): A list of extensions on ``input`` that will be left as-is instead of
exchanging for ``extension``.
kwargs (dict): Additional keyword arguments to provide to :func:`find_collision_free_name`. kwargs (dict): Additional keyword arguments to provide to :func:`find_collision_free_name`.
Returns: Returns:
@ -228,16 +230,43 @@ def get_dos_filename(origin, existing_filenames=None, extension=None, **kwargs):
Raises: Raises:
ValueError: No 8.3 compatible name could be found that doesn't collide with the provided ``existing_filenames``. ValueError: No 8.3 compatible name could be found that doesn't collide with the provided ``existing_filenames``.
Examples:
>>> get_dos_filename("test1234.gco")
u'test1234.gco'
>>> get_dos_filename("test1234.gcode")
u'test1234.gco'
>>> get_dos_filename("test12345.gco")
u'test12~1.gco'
>>> get_dos_filename("test1234.fnord", extension="gco")
u'test1234.gco'
>>> get_dos_filename("auto0.g", extension="gco")
u'auto0.gco'
>>> get_dos_filename("auto0.g", extension="gco", whitelisted_extensions=["g"])
u'auto0.g'
>>> get_dos_filename(None)
>>> get_dos_filename("foo")
u'foo'
""" """
if origin is None: if input is None:
return None return None
if existing_filenames is None: if existing_filenames is None:
existing_filenames = [] existing_filenames = []
filename, ext = os.path.splitext(origin) if extension is not None:
if extension is None: extension = extension.lower()
if whitelisted_extensions is None:
whitelisted_extensions = []
filename, ext = os.path.splitext(input)
ext = ext.lower()
ext = ext[1:] if ext.startswith(".") else ext
if ext in whitelisted_extensions or extension is None:
extension = ext extension = ext
return find_collision_free_name(filename, extension, existing_filenames, **kwargs) return find_collision_free_name(filename, extension, existing_filenames, **kwargs)
@ -286,9 +315,36 @@ def find_collision_free_name(filename, extension, existing_filenames, max_power=
Raises: Raises:
ValueError: No collision free name could be found. ValueError: No collision free name could be found.
"""
# TODO unit test! Examples:
>>> find_collision_free_name("test1234", "gco", [])
u'test1234.gco'
>>> find_collision_free_name("test1234", "gcode", [])
u'test1234.gco'
>>> find_collision_free_name("test12345", "gco", [])
u'test12~1.gco'
>>> find_collision_free_name("test 123", "gco", [])
u'test_123.gco'
>>> find_collision_free_name("test1234", "g o", [])
u'test1234.g_o'
>>> find_collision_free_name("test12345", "gco", ["test12~1.gco"])
u'test12~2.gco'
>>> many_files = ["test12~{}.gco".format(x) for x in range(10)[1:]]
>>> find_collision_free_name("test12345", "gco", many_files)
u'test1~10.gco'
>>> many_more_files = many_files + ["test1~{}.gco".format(x) for x in range(10, 99)]
>>> find_collision_free_name("test12345", "gco", many_more_files)
u'test1~99.gco'
>>> many_more_files_plus_one = many_more_files + ["test1~99.gco"]
>>> find_collision_free_name("test12345", "gco", many_more_files_plus_one)
Traceback (most recent call last):
...
ValueError: Can't create a collision free filename
>>> find_collision_free_name("test12345", "gco", many_more_files_plus_one, max_power=3)
u'test~100.gco'
"""
if not isinstance(filename, unicode): if not isinstance(filename, unicode):
filename = unicode(filename) filename = unicode(filename)
@ -302,14 +358,21 @@ def find_collision_free_name(filename, extension, existing_filenames, max_power=
extension = make_valid(extension) extension = make_valid(extension)
extension = extension[:3] if len(extension) > 3 else extension extension = extension[:3] if len(extension) > 3 else extension
if len(filename) <= 8 and not filename + "." + extension in existing_filenames: full_name_format = u"{filename}.{extension}" if extension else u"{filename}"
result = full_name_format.format(filename=filename,
extension=extension)
if len(filename) <= 8 and not result in existing_filenames:
# early exit # early exit
return filename + "." + extension return result
counter = 1 counter = 1
power = 1 power = 1
prefix_format = u"{segment}~{counter}"
while counter < (10 ** max_power): while counter < (10 ** max_power):
result = filename[:(6 - power + 1)] + "~" + str(counter) + "." + extension prefix = prefix_format.format(segment=filename[:(6 - power + 1)], counter=str(counter))
result = full_name_format.format(filename=prefix,
extension=extension)
if result not in existing_filenames: if result not in existing_filenames:
return result return result
counter += 1 counter += 1