From 4b32182ae18afe7f631a425e665665d17cb6ca3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yann=20B=C3=BCchau?= Date: Fri, 13 Jan 2017 22:49:48 +0100 Subject: [PATCH] release v0.1.5 - "ensemble tolerance" --- debian/changelog | 6 + usr/lib/simbuto/python/simbuto/__init__.py | 2 +- usr/lib/simbuto/python/simbuto/gui.py | 96 +++++---- usr/lib/simbuto/python/simbuto/manager.py | 12 +- usr/lib/simbuto/r/simbuto-functions.R | 119 +++++++---- usr/share/simbuto/gui/simbuto.glade | 193 +++++++++++------- .../simbuto/lang/de/LC_MESSAGES/simbuto.po | 188 +++++++++-------- 7 files changed, 382 insertions(+), 234 deletions(-) diff --git a/debian/changelog b/debian/changelog index d8639df..4d672a2 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +simbuto (0.1.5) unstable; urgency=medium + + * implement ensemble tolerance forecast + + -- Yann Büchau Fri, 13 Jan 2017 22:48:00 +0100 + simbuto (0.1.4) unstable; urgency=medium * implement amount and day tolerance diff --git a/usr/lib/simbuto/python/simbuto/__init__.py b/usr/lib/simbuto/python/simbuto/__init__.py index fb8da63..003e9b1 100644 --- a/usr/lib/simbuto/python/simbuto/__init__.py +++ b/usr/lib/simbuto/python/simbuto/__init__.py @@ -6,7 +6,7 @@ # Internal modules # the version -VERSION = "0.1.4" +VERSION = "0.1.5" __version__ = VERSION diff --git a/usr/lib/simbuto/python/simbuto/gui.py b/usr/lib/simbuto/python/simbuto/gui.py index 0676640..c3ca5c3 100644 --- a/usr/lib/simbuto/python/simbuto/gui.py +++ b/usr/lib/simbuto/python/simbuto/gui.py @@ -37,6 +37,9 @@ def __init__(self): signals = [signal.SIGINT, signal.SIGTERM, signal.SIGHUP], handler = self.quit ) + # can't use Gtk.main() because of a bug that prevents proper SIGINT + # handling. use Glib.MainLoop() directly instead. + self.mainloop = GLib.MainLoop() # main loop ################## @@ -74,6 +77,10 @@ def currently_edited_file(self): except AttributeError: return None + @property + def is_running(self): + return self.mainloop.is_running() + @currently_edited_file.setter def currently_edited_file(self, value): self._currently_edited_file = value @@ -405,6 +412,15 @@ def setup_gui(self): # statusbar self.reset_statusbar() # initially reset statusbar + # settings + self("ensemble_expander_label").set_text(_("Statistics")) + self("ensemble_settings_useensemble_checkbutton").set_label(_( + "display ensemble")) + self("ensemble_settings_useensemble_checkbutton").set_tooltip_text(_( + "[slower] Based on the given day and amount tolerances, run an " + "ensemble and display the 10% and 90% quantiles as darker shadow.")) + + # calendar # pretend the start date was selected and let automatic range selection # do the rest @@ -465,40 +481,51 @@ def update_statusbar(self, text = None): statuslabel.set_text(newtext) def update_graph_from_editor(self, *args, size=None): - # rect = self("plot_image").get_allocation() - if size is None: # use current size - rect = self("plot_scrolledwindow").get_allocation() - width = rect.width - height = rect.height - else: # use given size - width, height = size - - try: - currentfile = os.path.basename(self.currently_edited_file) - except AttributeError: - currentfile = _("unnamed-budget") - name = "{}.png".format(currentfile) - filename = os.path.join(config.personal_simbuto_dotfolder(), - "plots",name) - self.update_statusbar(_("updating graph...")) - success = self.signalmanager.emit_signal("create-graph-from-text", - filename=filename, # to this file - text = self.current_editor_content, # this text - width = width, height = height, # these dimensions - start = self.selected_start_date, # this start date - end = self.selected_end_date, # this end date - ) - if success[0]: - self.logger.debug(_("The graph file was obviously " - "sucessfully updated.")) - self.update_graph_from_file(filename) - self.update_statusbar(_("Graph updated")) + if self.is_running: # only if gui is running + # rect = self("plot_image").get_allocation() + if size is None: # use current size + rect = self("plot_scrolledwindow").get_allocation() + width = rect.width + height = rect.height + else: # use given size + width, height = size + + try: + currentfile = os.path.basename(self.currently_edited_file) + except AttributeError: + currentfile = _("unnamed-budget") + + cb = self("ensemble_settings_useensemble_checkbutton") + use_ensemble = cb.get_active() + + name = "{}.png".format(currentfile) + filename = os.path.join(config.personal_simbuto_dotfolder(), + "plots",name) + self.update_statusbar(_("updating graph...")) + success = self.signalmanager.emit_signal("create-graph-from-text", + filename=filename, # to this file + text = self.current_editor_content, # this text + width = width, height = height, # these dimensions + start = self.selected_start_date, # this start date + end = self.selected_end_date, # this end date + use_ensemble = use_ensemble, # use the ensemble or not + ) + if success[0]: + self.logger.debug(_("The graph file was obviously " + "sucessfully updated.")) + self.update_graph_from_file(filename) + self.update_statusbar(_("Graph updated")) + else: + self.logger.debug(_("There was a problem updating the graph.")) + self.update_statusbar(_("[WARNING] There was a problem " + "updating the graph. Please check the input!")) + return True else: - self.logger.debug(_("There was a problem updating the graph.")) - self.update_statusbar(_("[WARNING] There was a problem " - "updating the graph. Please check the input!")) - - return True + self.updating_graph_from_editor_is_now_okay = True + self.logger.debug(_("The gui is not running. Refusing to update " + "the graph now! Remembering this for gui start.")) + return False + def update_graph_from_file(self, filename): self("plot_image").set_from_file(filename) @@ -747,9 +774,6 @@ def fill_editor_from_file(self, filename): # run the gui def run(self): - # can't use Gtk.main() because of a bug that prevents proper SIGINT - # handling. use Glib.MainLoop() directly instead. - self.mainloop = GLib.MainLoop() # main loop # signal.signal(signal.SIGINT, signal.SIG_DFL) self.logger.debug(_("Starting GLib main loop...")) self.mainloop.run() diff --git a/usr/lib/simbuto/python/simbuto/manager.py b/usr/lib/simbuto/python/simbuto/manager.py index e84f95c..51de441 100644 --- a/usr/lib/simbuto/python/simbuto/manager.py +++ b/usr/lib/simbuto/python/simbuto/manager.py @@ -105,7 +105,9 @@ def md5sum_of_file(self, filename): def create_png_graph_from_text(self, text, filename, width = 600, height = 400, start = datetime.datetime.now(), - end = datetime.datetime.now() + datetime.timedelta(365) ): + end = datetime.datetime.now() + datetime.timedelta(365), + ensemble_size = 100, + use_ensemble = False): """ Create a png graph from simbuto csv-like text Args: text (str): the csv-like simbuto budget @@ -117,6 +119,10 @@ def create_png_graph_from_text(self, text, filename, end [Optional(datetime.datetime)]: the end time of the budget calculation and plotting. Defaults to the current day plus one year. + use_ensemble [Optional(bool)]: calculat an ensemble? Defaults to + False. + ensemble_size [Optional(int)]: The ensemble size to use. Defaults to + 100. Returns: success (bool): True if graph png file was created, False otherwise """ @@ -124,6 +130,8 @@ def create_png_graph_from_text(self, text, filename, start.year,start.month,start.day)) end_date = R("as.Date('{}-{}-{}')".format( end.year,end.month,end.day)) + if not use_ensemble: + ensemble_size = R("NULL") try: # append newline if not text.endswith("\n"): text += "\n" @@ -131,7 +139,7 @@ def create_png_graph_from_text(self, text, filename, budget_frame = R.read_budget_from_text(text = text) # create the timeseries from the budget timeseries_frame = R.timeseries_from_budget(budget = budget_frame, - start = start_date, end = end_date) + start = start_date, end = end_date, ensemble_size=ensemble_size) # plot to png R.plot_budget_timeseries_to_png(filename=filename, timeseries = timeseries_frame, width = width, height = height) diff --git a/usr/lib/simbuto/r/simbuto-functions.R b/usr/lib/simbuto/r/simbuto-functions.R index 2e32f4c..a01c594 100755 --- a/usr/lib/simbuto/r/simbuto-functions.R +++ b/usr/lib/simbuto/r/simbuto-functions.R @@ -24,9 +24,10 @@ timeseries_from_budget <- function( # create empty frame with day series all.days <- seq.Date(from = start, to = end, by = "days") MONEY <- data.frame(day = all.days, amount = 0) + N <- nrow(MONEY) # start with empty series - worstcase <- bestcase <- undisturbed <- rep(0, nrow(MONEY)) + worstcase <- bestcase <- undisturbed <- rep(0, N) # loop over all facts for (factnr in 1:nrow(budget)) { fact <- budget[factnr,] # current fact @@ -39,7 +40,7 @@ timeseries_from_budget <- function( fact.end <- fact.start interval = "day" # pick any interval, doesn't matter } - # cat("from=",fact.start," to=",fact.end," by=",interval," length.out=",number.occurences,"\n") + # print(fact) occurences <- c() if(fact.start <= fact.end) { occurences <- seq.Date(from = fact.start, to = fact.end, by = interval) @@ -49,20 +50,54 @@ timeseries_from_budget <- function( # get the indices indices <- na.omit(match(x = occurences, table = MONEY$day)) + + # create the series + # undisturbed - original undisturbed <- undisturbed + fact_amounts_series( occurences = occurences_bool, fact = fact,with_tolerance = FALSE) + # worst case worstcase <- worstcase + fact_amounts_series( occurences = occurences_bool, fact = fact,with_tolerance = TRUE, worst_case = TRUE ) + # best case bestcase <- bestcase + fact_amounts_series( occurences = occurences_bool, fact = fact,with_tolerance = TRUE, worst_case = FALSE ) + # ensemble + if(any(is.finite(ensemble_size))) { + if(!exists("ensemble")) # create variable if not existant yet + ensemble <- matrix(0,nrow=ensemble_size, ncol = N) + # do the runs + # cat("create members...") + for(i in 1:ensemble_size) { + tendencies <- fact_amounts_series( + occurences = occurences_bool, fact = fact,with_tolerance = TRUE, + random_tolerance = TRUE) + # add to ensemble run + ensemble[i,] <- ensemble[i,] + tendencies + } + } } # cumulate MONEY$amount = cumsum(undisturbed) MONEY$worstcase = cumsum(worstcase) MONEY$bestcase = cumsum(bestcase) + # ensemble + if(exists("ensemble")) { + # cumulate + ensemble <- t(apply(X = ensemble, MARGIN = 1, FUN = cumsum)) + # MONEY$ensmean <- apply(X = ensemble,MARGIN = 2, FUN = mean) + # MONEY$ensmedian <- apply(X = ensemble,MARGIN = 2, FUN = median) + # MONEY$ensquant25 <- apply(X = ensemble,MARGIN = 2, FUN = function(x)quantile(x,probs = c(0.25))) + # MONEY$ensquant75 <- apply(X = ensemble,MARGIN = 2, FUN = function(x)quantile(x,probs = c(0.75))) + # MONEY$ensquant10 <- apply(X = ensemble,MARGIN = 2, FUN = function(x)quantile(x,probs = c(0.10))) + # MONEY$ensquant90 <- apply(X = ensemble,MARGIN = 2, FUN = function(x)quantile(x,probs = c(0.90))) + MONEY$ensquant05 <- apply(X = ensemble,MARGIN = 2, FUN = function(x)quantile(x,probs = c(0.05))) + MONEY$ensquant95 <- apply(X = ensemble,MARGIN = 2, FUN = function(x)quantile(x,probs = c(0.95))) + # MONEY$ensmin <- apply(X = ensemble,MARGIN = 2, FUN = min) + # MONEY$ensmax <- apply(X = ensemble,MARGIN = 2, FUN = max) + } # empty data frame return(MONEY) } @@ -98,15 +133,15 @@ fact_amounts_series <- function( if(with_tolerance) { if(random_tolerance) { # modify amount randomly - amounts <- runif( n = length(indices), + amounts <- round(runif( n = length(indices), min = fact_amount - fact_tolerance_amount, max = fact_amount + fact_tolerance_amount - ) + )) # modify indices randomly - indices <- indices + runif( n = length(indices), - min = - fact_tolerance_amount, - max = + fact_tolerance_amount - ) + indices <- indices + round(runif( n = length(indices), + min = - fact_tolerance_day, + max = + fact_tolerance_day + )) } else { if(worst_case) { # worst case: all costs are highest @@ -123,6 +158,7 @@ fact_amounts_series <- function( # best case: all incomes are earliest indices <- indices - sign(fact_amount) * fact_tolerance_day } + } # fix indices that lie outside the output vector # cat("indices before fixing: ",indices,"\n") # cat("amounts before fixing: ",amounts,"\n") @@ -140,13 +176,16 @@ fact_amounts_series <- function( } # cat("indices after fixing: ",indices,"\n") # cat("amounts after fixing: ",amounts,"\n") - } } else { # keep amount amounts <- rep(fact_amount, length(indices)) } # cat("amounts before putting into out: ",amounts,"\n") + # cat("indices: ",indices,"\n") + # cat("amounts: ",amounts,"\n") + stopifnot(all(1<=indices|indices<=N)) + stopifnot(length(indices) == length(amounts)) # set the amounts to the indices out[indices] <- amounts # cat("out: ",out,"\n") @@ -155,39 +194,6 @@ fact_amounts_series <- function( return(out) } -budget_ensemble<- function( budget, - start = Sys.Date(), end = Sys.Date() + 365, - ensemble_size = 100 - ) { - # run without tolerance - timeseries_without_tolerance <- timeseries_from_budget( - budget = budget, start = start, end = end, with_tolerance = TRUE, - random_tolerance = FALSE) - # the ensemble out starts with the bare run - ENSEMBLE_OUT <- timeseries_without_tolerance - if(any(is.finite(budget$tolerance_amount))) { - # create ensemble matrix - ENSEMBLE <- matrix(NA,nrow=ensemble_size, ncol = nrow(ENSEMBLE_OUT)) - # do the runs - # cat("create members...") - for(i in 1:ensemble_size) { - ENSEMBLE[i,] <- timeseries_from_budget( budget = budget, - start = start, end = end, - with_tolerance = TRUE, random_tolerance=TRUE)$amount - } - cat("done!\n") - # calculate statistics - # cat("calculate statistics...") - ENSEMBLE_OUT$ensmean <- apply(X = ENSEMBLE,MARGIN = 2, FUN = mean) - ENSEMBLE_OUT$ensmedian <- apply(X = ENSEMBLE,MARGIN = 2, FUN = median) - ENSEMBLE_OUT$ensquant25 <- apply(X = ENSEMBLE,MARGIN = 2, FUN = function(x)quantile(x,probs = c(0.25))) - ENSEMBLE_OUT$ensquant75 <- apply(X = ENSEMBLE,MARGIN = 2, FUN = function(x)quantile(x,probs = c(0.75))) - ENSEMBLE_OUT$ensmin <- apply(X = ENSEMBLE,MARGIN = 2, FUN = min) - ENSEMBLE_OUT$ensmax <- apply(X = ENSEMBLE,MARGIN = 2, FUN = max) - # cat("done!\n") - } - return(ENSEMBLE_OUT) -} plot_budget_timeseries <- function(timeseries) { plotrange <- range(c(timeseries$amount,timeseries$worstcase, @@ -223,7 +229,32 @@ plot_budget_timeseries <- function(timeseries) { if(!is.null(timeseries$worstcase) & !is.null(timeseries$bestcase)) { polygon(x = c(timeseries$day,rev(timeseries$day)), y = c(timeseries$worstcase,rev(timeseries$bestcase)), - col = "#00000022",border=NA) + col = "#00000033",border=NA) + } + if(!is.null(timeseries$ensmin) & !is.null(timeseries$ensmax)) { + polygon(x = c(timeseries$day,rev(timeseries$day)), + y = c(timeseries$ensmin,rev(timeseries$ensmax)), + col = "#00000033",border=NA) + } + if(!is.null(timeseries$ensquant05) & !is.null(timeseries$ensquant95)) { + polygon(x = c(timeseries$day,rev(timeseries$day)), + y = c(timeseries$ensquant05,rev(timeseries$ensquant95)), + col = "#00000033",border=NA) + } + if(!is.null(timeseries$ensquant10) & !is.null(timeseries$ensquant90)) { + polygon(x = c(timeseries$day,rev(timeseries$day)), + y = c(timeseries$ensquant10,rev(timeseries$ensquant90)), + col = "#00000033",border=NA) + } + if(!is.null(timeseries$ensquant25) & !is.null(timeseries$ensquant75)) { + polygon(x = c(timeseries$day,rev(timeseries$day)), + y = c(timeseries$ensquant25,rev(timeseries$ensquant75)), + col = "#00000033",border=NA) + } + if(!is.null(timeseries$ensmean)) { + lines(x = timeseries$day, y = timeseries$ensmean + ,lwd = 2, lty = 2 + ) } # raw run lines(x = timeseries$day, y = timeseries$amount @@ -240,7 +271,7 @@ plot_budget_timeseries_to_png <- function(timeseries,filename,width=600,height=4 #### read data #### # BUDGET <- read_budget_from_text(readLines("~/Downloads/budget.simbuto")) -# MONEY <- timeseries_from_budget(budget = BUDGET) +# MONEY <- timeseries_from_budget(budget = BUDGET, ensemble_size = 500) # cat("plotting...") # plot_budget_timeseries(MONEY) # cat("done!\n") diff --git a/usr/share/simbuto/gui/simbuto.glade b/usr/share/simbuto/gui/simbuto.glade index 580961c..6813c7b 100644 --- a/usr/share/simbuto/gui/simbuto.glade +++ b/usr/share/simbuto/gui/simbuto.glade @@ -404,123 +404,178 @@ - + True False - + True - True - True - 5 - True + False - + True - False + True + True + 5 + True - + True False - vertical - + True False - Start + vertical + + + True + False + Start + + + False + True + 0 + + + + + True + True + 2017 + 8 + + + + False + True + 1 + + - False - True + True + False 0 - + True - True - 2017 - 8 - + False + vertical False - True + False + 5 1 + + + True + False + vertical + + + True + False + End + + + False + True + 0 + + + + + True + True + 2017 + 8 + + + + False + True + 1 + + + + + True + False + 2 + + - - True - False - 0 - - - + + True False - vertical + From - To - - False - False - 5 - 1 - + + + True + False + 0 + + + + + False + True + 0 + + + + + True + True + True + True + + + True + False + vertical - + + use statistics + app.refresh True - False - vertical - - - True - False - End - - - False - True - 0 - - - - - True - True - 2017 - 8 - - - - False - True - 1 - - + True + False + 0 + True True - False - 2 + True + 0 - + True False - From - To + ensemble + start True - False - 0 + True + 1 diff --git a/usr/share/simbuto/lang/de/LC_MESSAGES/simbuto.po b/usr/share/simbuto/lang/de/LC_MESSAGES/simbuto.po index ac1d1b4..2d28210 100644 --- a/usr/share/simbuto/lang/de/LC_MESSAGES/simbuto.po +++ b/usr/share/simbuto/lang/de/LC_MESSAGES/simbuto.po @@ -6,8 +6,8 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-01-13 11:31+0100\n" -"PO-Revision-Date: 2017-01-13 12:34+0100\n" +"POT-Creation-Date: 2017-01-13 22:42+0100\n" +"PO-Revision-Date: 2017-01-13 22:46+0100\n" "Last-Translator: \n" "Language-Team: \n" "Language: de\n" @@ -18,237 +18,261 @@ msgstr "" "X-Generator: Poedit 1.8.7.1\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: usr/lib/simbuto/python/simbuto/gui.py:90 +#: usr/lib/simbuto/python/simbuto/gui.py:97 msgid "" "The current buffer is empty and no file is specified. No saving necessary." msgstr "" "Der Editor ist leer und keine Datei wurde angegeben. Speichern ist nicht " "notwendig." -#: usr/lib/simbuto/python/simbuto/gui.py:98 +#: usr/lib/simbuto/python/simbuto/gui.py:105 msgid "Nonexistant or no file specified. Saving necessary!" msgstr "" "Nichtexistente oder gar keine Datei angegeben. Speichern ist notwendig!" -#: usr/lib/simbuto/python/simbuto/gui.py:105 +#: usr/lib/simbuto/python/simbuto/gui.py:112 #: usr/lib/simbuto/python/simbuto/manager.py:93 msgid "md5sum of file '{}' is '{}'" msgstr "md5-Summe der Datei '{}' is '{}'" -#: usr/lib/simbuto/python/simbuto/gui.py:110 +#: usr/lib/simbuto/python/simbuto/gui.py:117 msgid "md5sum of editor content is '{}'" msgstr "md5-Summe des Editorinhalts ist '{}'" -#: usr/lib/simbuto/python/simbuto/gui.py:114 +#: usr/lib/simbuto/python/simbuto/gui.py:121 msgid "The current budget would need saving." msgstr "Das aktuelle Budget müsste gespeichert werden." -#: usr/lib/simbuto/python/simbuto/gui.py:116 +#: usr/lib/simbuto/python/simbuto/gui.py:123 msgid "The current budget doesn't need saving." msgstr "Das aktuelle Budget muss nicht gespeichert werden." -#: usr/lib/simbuto/python/simbuto/gui.py:320 +#: usr/lib/simbuto/python/simbuto/gui.py:327 msgid "New Budget" msgstr "Neues Budget" -#: usr/lib/simbuto/python/simbuto/gui.py:320 +#: usr/lib/simbuto/python/simbuto/gui.py:327 msgid "New" msgstr "Neu" -#: usr/lib/simbuto/python/simbuto/gui.py:321 +#: usr/lib/simbuto/python/simbuto/gui.py:328 msgid "Create a new budget" msgstr "Ein neues Budget erstellen" -#: usr/lib/simbuto/python/simbuto/gui.py:322 +#: usr/lib/simbuto/python/simbuto/gui.py:329 msgid "Open Budget" msgstr "Öffne Budget" -#: usr/lib/simbuto/python/simbuto/gui.py:322 +#: usr/lib/simbuto/python/simbuto/gui.py:329 msgid "Open" msgstr "Öffnen" -#: usr/lib/simbuto/python/simbuto/gui.py:323 +#: usr/lib/simbuto/python/simbuto/gui.py:330 msgid "Open an existing budget file" msgstr "Eine existierende Budget-Datei öffnen" -#: usr/lib/simbuto/python/simbuto/gui.py:324 +#: usr/lib/simbuto/python/simbuto/gui.py:331 msgid "Save Budget" msgstr "Budget speichern" -#: usr/lib/simbuto/python/simbuto/gui.py:324 +#: usr/lib/simbuto/python/simbuto/gui.py:331 msgid "Save" msgstr "Speichern" -#: usr/lib/simbuto/python/simbuto/gui.py:325 +#: usr/lib/simbuto/python/simbuto/gui.py:332 msgid "Save this budget" msgstr "Dieses Budget speichern" -#: usr/lib/simbuto/python/simbuto/gui.py:326 +#: usr/lib/simbuto/python/simbuto/gui.py:333 msgid "Save As" msgstr "Speichern unter" -#: usr/lib/simbuto/python/simbuto/gui.py:327 +#: usr/lib/simbuto/python/simbuto/gui.py:334 msgid "Save this budget to another file" msgstr "Dieses Budget anderswo speichern" -#: usr/lib/simbuto/python/simbuto/gui.py:328 +#: usr/lib/simbuto/python/simbuto/gui.py:335 msgid "Refresh Graph" msgstr "Graph aktualisieren" -#: usr/lib/simbuto/python/simbuto/gui.py:328 +#: usr/lib/simbuto/python/simbuto/gui.py:335 msgid "Refresh" msgstr "Aktualisieren" -#: usr/lib/simbuto/python/simbuto/gui.py:329 +#: usr/lib/simbuto/python/simbuto/gui.py:336 msgid "Refresh the budget graph" msgstr "den Budget-Graph aktualisieren" -#: usr/lib/simbuto/python/simbuto/gui.py:330 +#: usr/lib/simbuto/python/simbuto/gui.py:337 msgid "Quit" msgstr "Beenden" -#: usr/lib/simbuto/python/simbuto/gui.py:331 +#: usr/lib/simbuto/python/simbuto/gui.py:338 msgid "Quit Simbuto" msgstr "Simbuto beenden" -#: usr/lib/simbuto/python/simbuto/gui.py:332 +#: usr/lib/simbuto/python/simbuto/gui.py:339 msgid "About" msgstr "Über" -#: usr/lib/simbuto/python/simbuto/gui.py:333 +#: usr/lib/simbuto/python/simbuto/gui.py:340 msgid "Display information on Simbuto" msgstr "Informationen über Simbuto anzeigen" -#: usr/lib/simbuto/python/simbuto/gui.py:334 +#: usr/lib/simbuto/python/simbuto/gui.py:341 msgid "Date Reset" msgstr "Datum zurücksetzen" -#: usr/lib/simbuto/python/simbuto/gui.py:335 +#: usr/lib/simbuto/python/simbuto/gui.py:342 msgid "Reset the selected date region" msgstr "Setze den ausgewählten Datumsbereich zurück" -#: usr/lib/simbuto/python/simbuto/gui.py:348 +#: usr/lib/simbuto/python/simbuto/gui.py:355 msgid "Simbuto budget files" msgstr "Simbuto Budget-Dateien" -#: usr/lib/simbuto/python/simbuto/gui.py:379 +#: usr/lib/simbuto/python/simbuto/gui.py:386 msgid "_File" msgstr "_Datei" -#: usr/lib/simbuto/python/simbuto/gui.py:380 +#: usr/lib/simbuto/python/simbuto/gui.py:387 msgid "_Help" msgstr "_Hilfe" -#: usr/lib/simbuto/python/simbuto/gui.py:381 +#: usr/lib/simbuto/python/simbuto/gui.py:388 msgid "_Budget" msgstr "_Budget" -#: usr/lib/simbuto/python/simbuto/gui.py:389 +#: usr/lib/simbuto/python/simbuto/gui.py:396 msgid "Budget editor" msgstr "Budget Editor" -#: usr/lib/simbuto/python/simbuto/gui.py:395 -#: usr/lib/simbuto/python/simbuto/gui.py:417 +#: usr/lib/simbuto/python/simbuto/gui.py:402 +#: usr/lib/simbuto/python/simbuto/gui.py:433 msgid "Comfort editor coming soon!" msgstr "Der Komfort-Editor kommt bald!" -#: usr/lib/simbuto/python/simbuto/gui.py:397 +#: usr/lib/simbuto/python/simbuto/gui.py:404 msgid "Comfort" msgstr "Komfort" -#: usr/lib/simbuto/python/simbuto/gui.py:398 +#: usr/lib/simbuto/python/simbuto/gui.py:405 msgid "Text" msgstr "Text" -#: usr/lib/simbuto/python/simbuto/gui.py:403 +#: usr/lib/simbuto/python/simbuto/gui.py:410 msgid "Budget graph" msgstr "Budget Graph" -#: usr/lib/simbuto/python/simbuto/gui.py:411 +#: usr/lib/simbuto/python/simbuto/gui.py:416 +msgid "Statistics" +msgstr "Statistik" + +#: usr/lib/simbuto/python/simbuto/gui.py:418 +msgid "display ensemble" +msgstr "Ensemble anzeigen" + +#: usr/lib/simbuto/python/simbuto/gui.py:420 +msgid "" +"[slower] Based on the given day and amount tolerances, run an ensemble and " +"display the 10% and 90% quantiles as darker shadow." +msgstr "" +"[langsamer] Auf Grundlage der Tages- und Betragstoleranzen wird ein Ensemble " +"gerechnet und das 10%- und 90%-Quantil als dunkerer Schatten angezeigt." + +#: usr/lib/simbuto/python/simbuto/gui.py:427 msgid "Date range" msgstr "Datumsbereich" -#: usr/lib/simbuto/python/simbuto/gui.py:412 +#: usr/lib/simbuto/python/simbuto/gui.py:428 msgid "start date" msgstr "Startdatum" -#: usr/lib/simbuto/python/simbuto/gui.py:413 +#: usr/lib/simbuto/python/simbuto/gui.py:429 msgid "end date" msgstr "Enddatum" -#: usr/lib/simbuto/python/simbuto/gui.py:436 +#: usr/lib/simbuto/python/simbuto/gui.py:452 msgid "unsaved budget" msgstr "ungespeichertes Budget" -#: usr/lib/simbuto/python/simbuto/gui.py:437 usr/bin/simbuto:36 +#: usr/lib/simbuto/python/simbuto/gui.py:453 usr/bin/simbuto:36 msgid "Simbuto" msgstr "Simbuto" -#: usr/lib/simbuto/python/simbuto/gui.py:440 +#: usr/lib/simbuto/python/simbuto/gui.py:456 msgid "emptying editor" msgstr "leere den Budget-Editor" -#: usr/lib/simbuto/python/simbuto/gui.py:449 -#: usr/lib/simbuto/python/simbuto/gui.py:463 +#: usr/lib/simbuto/python/simbuto/gui.py:465 +#: usr/lib/simbuto/python/simbuto/gui.py:479 msgid "Simbuto - a simple budgeting tool" msgstr "Simbuto - ein einfaches Werkzeug zur Kostenplanung" -#: usr/lib/simbuto/python/simbuto/gui.py:479 +#: usr/lib/simbuto/python/simbuto/gui.py:496 msgid "unnamed-budget" msgstr "unbenanntes-Budget" -#: usr/lib/simbuto/python/simbuto/gui.py:483 +#: usr/lib/simbuto/python/simbuto/gui.py:504 msgid "updating graph..." msgstr "aktualisiere Graph..." -#: usr/lib/simbuto/python/simbuto/gui.py:492 +#: usr/lib/simbuto/python/simbuto/gui.py:514 msgid "The graph file was obviously sucessfully updated." msgstr "Die Graph-Bildatei wurde erfolgreich aktualisiert." -#: usr/lib/simbuto/python/simbuto/gui.py:495 +#: usr/lib/simbuto/python/simbuto/gui.py:517 msgid "Graph updated" msgstr "Graph aktualisiert" -#: usr/lib/simbuto/python/simbuto/gui.py:497 +#: usr/lib/simbuto/python/simbuto/gui.py:519 msgid "There was a problem updating the graph." msgstr "Es gab ein Problem beim Aktualisieren des Graphen." -#: usr/lib/simbuto/python/simbuto/gui.py:498 +#: usr/lib/simbuto/python/simbuto/gui.py:520 msgid "" "[WARNING] There was a problem updating the graph. Please check the input!" msgstr "" "[WARNUNG] Es gab ein Problem beim Aktualisieren des Graphen. Bitte Eingabe " "überprüfen!" -#: usr/lib/simbuto/python/simbuto/gui.py:514 +#: usr/lib/simbuto/python/simbuto/gui.py:525 +msgid "" +"The gui is not running. Refusing to update the graph now! Remembering this " +"for gui start." +msgstr "" +"Die Gui läuft nicht. Verzichte auf Graphaktualisierung! Wird aber für den " +"Gui-Start gemerkt!" + +#: usr/lib/simbuto/python/simbuto/gui.py:541 msgid "Date region was changed." msgstr "Datumsbereich wurde geändert." -#: usr/lib/simbuto/python/simbuto/gui.py:516 +#: usr/lib/simbuto/python/simbuto/gui.py:543 msgid "To prevent recursion I won't react to this." msgstr "Um Rekursion zu verhindern, wird nicht darauf reagiert." -#: usr/lib/simbuto/python/simbuto/gui.py:521 +#: usr/lib/simbuto/python/simbuto/gui.py:548 msgid "start date is now {}" msgstr "Startdatum ist jetzt {}" -#: usr/lib/simbuto/python/simbuto/gui.py:523 +#: usr/lib/simbuto/python/simbuto/gui.py:550 msgid "end date is now {}" msgstr "Enddatum ist jetzt {}" -#: usr/lib/simbuto/python/simbuto/gui.py:525 +#: usr/lib/simbuto/python/simbuto/gui.py:552 msgid "End date before start date selected." msgstr "Enddatum vor Startdatum ausgewählt" -#: usr/lib/simbuto/python/simbuto/gui.py:529 +#: usr/lib/simbuto/python/simbuto/gui.py:556 msgid "Setting end date to one year after start date" msgstr "Setze Enddatum auf ein Jahr vor dem Startdatum" -#: usr/lib/simbuto/python/simbuto/gui.py:534 +#: usr/lib/simbuto/python/simbuto/gui.py:561 msgid "Setting start date to one month before end date" msgstr "Setze Startdatum auf einen Monat vor dem Enddatum" -#: usr/lib/simbuto/python/simbuto/gui.py:539 +#: usr/lib/simbuto/python/simbuto/gui.py:566 msgid "" "Somehow a date was selected from an unknown calendar. This should not have " "happened." @@ -256,88 +280,88 @@ msgstr "" "Irgendwie wurde ein Datum von einem unbekannten Kalender ausgewählt. Das " "hätte nicht passieren sollen." -#: usr/lib/simbuto/python/simbuto/gui.py:608 +#: usr/lib/simbuto/python/simbuto/gui.py:635 msgid "Please choose a file" msgstr "Bitte eine Datei auswählen" -#: usr/lib/simbuto/python/simbuto/gui.py:622 #: usr/lib/simbuto/python/simbuto/gui.py:649 +#: usr/lib/simbuto/python/simbuto/gui.py:676 msgid "File '{}' selected" msgstr "Datei '{}' ausgewählt" -#: usr/lib/simbuto/python/simbuto/gui.py:626 #: usr/lib/simbuto/python/simbuto/gui.py:653 +#: usr/lib/simbuto/python/simbuto/gui.py:680 msgid "File selection cancelled" msgstr "Dateiauswahl abgebrochen" -#: usr/lib/simbuto/python/simbuto/gui.py:628 #: usr/lib/simbuto/python/simbuto/gui.py:655 +#: usr/lib/simbuto/python/simbuto/gui.py:682 msgid "File selection dialog was closed" msgstr "Dateiauswahldialog wurde geschlossen" -#: usr/lib/simbuto/python/simbuto/gui.py:635 +#: usr/lib/simbuto/python/simbuto/gui.py:662 msgid "Please select a saving destination" msgstr "Bitte einen Speicherort auswählen" -#: usr/lib/simbuto/python/simbuto/gui.py:662 +#: usr/lib/simbuto/python/simbuto/gui.py:689 msgid "This feature is currently not implemented." msgstr "Diese Funktionalität ist momentan noch nicht implementiert." -#: usr/lib/simbuto/python/simbuto/gui.py:669 +#: usr/lib/simbuto/python/simbuto/gui.py:696 msgid "Do you want to save your current budget?" msgstr "Das aktuelle Budget speichern?" -#: usr/lib/simbuto/python/simbuto/gui.py:672 +#: usr/lib/simbuto/python/simbuto/gui.py:699 msgid "The user wants to save the budget to file." msgstr "Der Benutzer möchte das aktuelle Budget speichern." -#: usr/lib/simbuto/python/simbuto/gui.py:675 +#: usr/lib/simbuto/python/simbuto/gui.py:702 msgid "The user does NOT want to save the budget." msgstr "Das Benutzer möchte das aktuelle Budget NICHT speichern." -#: usr/lib/simbuto/python/simbuto/gui.py:682 +#: usr/lib/simbuto/python/simbuto/gui.py:709 msgid "a simple budgeting tool" msgstr "ein einfaches Werkzeug zur Kostenplanung" -#: usr/lib/simbuto/python/simbuto/gui.py:705 +#: usr/lib/simbuto/python/simbuto/gui.py:732 msgid "Saving the current budget to the file '{}'..." msgstr "Speichere das aktuelle Budget in die Datei '{}'..." -#: usr/lib/simbuto/python/simbuto/gui.py:711 -#: usr/lib/simbuto/python/simbuto/gui.py:714 +#: usr/lib/simbuto/python/simbuto/gui.py:738 +#: usr/lib/simbuto/python/simbuto/gui.py:741 msgid "Budget saved to '{}'" msgstr "Budget wurde nach '{}' gespeichert" -#: usr/lib/simbuto/python/simbuto/gui.py:716 +#: usr/lib/simbuto/python/simbuto/gui.py:743 msgid "Budget could NOT be saved to '{}'!" msgstr "Budget Konnte NICHT nach '{}' gespeichert werden!" -#: usr/lib/simbuto/python/simbuto/gui.py:718 +#: usr/lib/simbuto/python/simbuto/gui.py:745 msgid "[WARNING] Budget could not be saved to '{}'!" msgstr "[WARNUNG] Budget konnte NICHT nach '{}' gespeichert werden!" -#: usr/lib/simbuto/python/simbuto/gui.py:729 +#: usr/lib/simbuto/python/simbuto/gui.py:756 msgid "read file '{}' into editor...." msgstr "lese Datei '{}' in den Editor ein..." -#: usr/lib/simbuto/python/simbuto/gui.py:738 +#: usr/lib/simbuto/python/simbuto/gui.py:765 msgid "editor was filled with contents of file '{}'" msgstr "Der Editor wurde mit den Inhalten der Datei '{}' gefüllt" -#: usr/lib/simbuto/python/simbuto/gui.py:743 +#: usr/lib/simbuto/python/simbuto/gui.py:770 #: usr/lib/simbuto/python/simbuto/manager.py:56 msgid "Reading from file '{}' didn't work!" msgstr "Lesen von der Datei '{}' hat nicht funktioniert!" -#: usr/lib/simbuto/python/simbuto/gui.py:754 +#: usr/lib/simbuto/python/simbuto/gui.py:778 msgid "Starting GLib main loop..." msgstr "starte GLib Hautpschleife..." -#: usr/lib/simbuto/python/simbuto/gui.py:756 +#: usr/lib/simbuto/python/simbuto/gui.py:780 msgid "GLib main loop ended." msgstr "GLib Hauptschleife beendet." -#: usr/lib/simbuto/python/simbuto/gui.py:765 +#: usr/lib/simbuto/python/simbuto/gui.py:789 msgid "Received quitting signal." msgstr "Anhaltesignal erhalten." @@ -401,7 +425,7 @@ msgstr "Speichern von {} Zeichen in die Datei '{}' hat nicht funktioniert!" msgid "Calculating md5sum of file '{}' didn't work!" msgstr "Berechnen der md5-Summe für die Datei '{}' hat nicht funktioniert!" -#: usr/lib/simbuto/python/simbuto/manager.py:140 +#: usr/lib/simbuto/python/simbuto/manager.py:148 msgid "R could not read from text" msgstr "R konnte den Text nicht einlesen."