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._logger = logging.getLogger(__name__)
self._queue = Queue.PriorityQueue()
self._queue = Queue.Queue()
self._worker = threading.Thread(target=self._work)
self._worker.daemon = True
self._worker.start()
@ -120,7 +120,7 @@ class EventManager(object):
def _work(self):
try:
while True:
(event, payload) = self._queue.get(True)
event, payload = self._queue.get(True)
eventListeners = self._registeredListeners[event]
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.
"""
self._queue.put((event, payload), 0)
self._queue.put((event, payload))
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"
@ -157,8 +157,7 @@ class EventManager(object):
import copy
legacy_payload = copy.deepcopy(payload)
legacy_payload["type"] = "gcode"
self._queue.put((event, legacy_payload), 0)
self._queue.put((event, legacy_payload))
def subscribe(self, event, callback):
"""

View file

@ -533,7 +533,10 @@ class Printer(PrinterInterface, comm.MachineComPrintCallback):
self.refresh_sd_files(blocking=True)
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._comm.startFileTransfer(absolutePath, filename, "/" + remoteName)

View file

@ -11,8 +11,6 @@ function DataUpdater(allViewModels) {
self._lastProcessingTimes = [];
self._lastProcessingTimesSize = 20;
self._timelapse_popup = undefined;
self.increaseThrottle = function() {
self.setThrottle(self._throttleFactor + 1);
};
@ -156,7 +154,6 @@ function DataUpdater(allViewModels) {
var type = event.data["type"];
var payload = event.data["payload"];
var html = "";
var format = {};
log.debug("Got event " + type + " with payload: " + JSON.stringify(payload));
@ -164,91 +161,6 @@ function DataUpdater(allViewModels) {
if (payload && payload.hasOwnProperty("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") {
gcodeUploadProgress.addClass("progress-striped").addClass("active");
gcodeUploadProgressBar.css("width", "100%");

View file

@ -4,6 +4,8 @@ $(function() {
self.loginState = parameters[0];
self.timelapsePopup = undefined;
self.defaultFps = 25;
self.defaultPostRoll = 0;
self.defaultInterval = 10;
@ -222,11 +224,88 @@ $(function() {
.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.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();
};

View file

@ -206,7 +206,7 @@ def get_free_bytes(path):
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
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.
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.
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`.
Returns:
@ -228,16 +230,43 @@ def get_dos_filename(origin, existing_filenames=None, extension=None, **kwargs):
Raises:
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
if existing_filenames is None:
existing_filenames = []
filename, ext = os.path.splitext(origin)
if extension is None:
if extension is not 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
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:
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):
filename = unicode(filename)
@ -302,14 +358,21 @@ def find_collision_free_name(filename, extension, existing_filenames, max_power=
extension = make_valid(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
return filename + "." + extension
return result
counter = 1
power = 1
prefix_format = u"{segment}~{counter}"
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:
return result
counter += 1