from __future__ import absolute_import, division, print_function, unicode_literals import glob import os, time, sys import io import flask from octoprint_klipper.util import * from flask_babel import gettext from shutil import copy, copyfile try: import configparser except ImportError: import ConfigParser as configparser if sys.version_info[0] < 3: import StringIO def list_cfg_files(self, path): """Generate list of config files. Args: path (str): Path to the config files. Returns: list: for every file a dict with keys for name, file, size, mdate, url. """ files = [] if path=="backup": cfg_path = os.path.join(self.get_plugin_data_folder(), "configs", "*") else: cfg_path = os.path.expanduser( self._settings.get(["configuration", "config_path"]) ) cfg_path = os.path.join(cfg_path, "*.cfg") cfg_files = glob.glob(cfg_path) log_debug(self, "list_cfg_files Path: " + cfg_path) for f in cfg_files: filesize = os.path.getsize(f) filemdate = time.localtime(os.path.getmtime(f)) if path != "backup": url = flask.url_for("index") + "plugin/klipper/download/configs/" + os.path.basename(f) else: url = flask.url_for("index") + "plugin/klipper/download/backup/" + os.path.basename(f) files.append(dict( name= os.path.basename(f), file= f, size= " ({:.1f} KB)".format(filesize / 1000.0), mdate= time.strftime("%d.%m.%Y %H:%M", filemdate), url= url, )) log_debug(self, "list_cfg_files " + str(len(files)) + ": " + f) return files def get_cfg(self, file): """Get the content of a configuration file. Args: file (str): The name of the file to read Returns: dict: config (str): The configuration of the file text (str): The text of the error """ response = {"config":"", "text": ""} if not file: cfg_path = os.path.expanduser( self._settings.get(["configuration", "configpath"]) ) file = os.path.join(cfg_path, self._settings.get(["configuration", "baseconfig"])) if file_exist(self, file): log_debug(self, "get_cfg_files Path: " + file) try: with io.open(file, "r", encoding='utf-8') as f: response['config'] = f.read() except IOError as Err: log_error( self, gettext("Error: Klipper config file not found at:") + " {}".format(file) + "\n" + gettext("IOError:") + " {}".format(Err) ) response['text'] = Err return response except UnicodeDecodeError as Err: log_error( self, gettext("Decode Error:") +"\n" + "{}".format(Err) + "\n\n" + gettext("Please convert your config files to utf-8!") + "\n" + gettext("Or you can also paste your config \ninto the Editor and save it.") ) response['text'] = Err return response else: return response else: response['text'] = gettext("File not found!") return response def save_cfg(self, content, filename): """Save the configuration file to given file. Args: content (str): The content of the configuration. filename (str): The filename of the configuration file. Default is "printer.cfg" Returns: bool: True if the configuration file was saved successfully. Otherwise False """ log_debug( self, "Save klipper config" ) configpath = os.path.expanduser(self._settings.get(["configuration", "config_path"])) if filename == "": filename = self._settings.get(["configuration", "baseconfig"]) if filename[-4:] != ".cfg": filename += ".cfg" filepath = os.path.join(configpath, filename) log_debug(self, "Writing Klipper config to {}".format(filepath)) try: with io.open(filepath, "w", encoding='utf-8') as f: f.write(content) except IOError: log_error(self, "Error: Couldn't open Klipper config file: {}".format(filepath)) return False else: log_debug(self, "Written Klipper config to {}".format(filepath)) return True finally: copy_cfg_to_backup(self, filepath) def check_cfg_ok(self, data): """Checks the given data on parsing errors. Args: data (str): Content to be validated. Returns: bool: True if the data is valid. False if it is not. """ try: dataToValidated = configparser.RawConfigParser(strict=False) if sys.version_info[0] < 3: import StringIO buf = StringIO.StringIO(data) dataToValidated.readfp(buf) else: dataToValidated.read_string(data) except configparser.Error as error: show_error_message(self, error) log_debug(self, 'check_cfg: NOK!') return False else: if not is_float_ok(self, dataToValidated): log_debug(self, "check_cfg: NOK!") return False log_debug(self, "check_cfg: OK") return True def show_error_message(self, error): error.message = error.message.replace('\\n', '') if sys.version_info[0] < 3: error.message = error.message.replace('file: u', 'Klipper Configuration', 1) error.message = error.message.replace("'", '', 2) error.message = error.message.replace("u'", "'", 1) else: error.message = error.message.replace('file:', 'Klipper Configuration', 1) error.message = error.message.replace("'", '', 2) log_error( self, ('Error: Invalid Klipper config file:\n' + '{}'.format(str(error))), ) def is_float_ok(self, dataToValidated): sections_search_list = [ "bltouch", "probe" ] value_search_list = [ "x_offset", "y_offset", "z_offset" ] try: # cycle through sections and then values for y in sections_search_list: for x in value_search_list: if dataToValidated.has_option(y, x): a_float = dataToValidated.getfloat(y, x) except ValueError as error: log_error( self, "Error: Invalid Value for " + x + " in Section: " + y + "\n" + "{}".format(str(error)) ) send_message( self, type = "PopUp", subtype = "warning", title = "Invalid Config data\n", payload = "\n" + "Invalid Value for " + x + " in Section: " + y + "\n" + "{}".format(str(error)) ) return False else: return True def copy_cfg(self, file, dst): """Copy the config file to the destination. Args: file (str): Filepath of the config file to copy. dst (str): Path to copy the config file to. Returns: bool: True if the copy succeeded, False otherwise. """ if os.path.isfile(file): try: copy(file, dst) except IOError: log_error( self, "Error: Klipper config file not found at: {}".format(file) ) return False else: log_debug( self, "File copied: " + file ) return True return False def copy_cfg_to_backup(self, src): """Copy the config file to backup directory of OctoKlipper. Args: src (str): Path to the config file to copy. Returns: bool: True if the config file was copied successfully. False otherwise. """ if not os.path.isfile(src): return False cfg_path = os.path.join(self.get_plugin_data_folder(), "configs", "") filename = os.path.basename(src) if not os.path.exists(cfg_path): try: os.mkdir(cfg_path) except OSError: log_error(self, "Error: Creation of the backup directory {} failed".format(cfg_path)) return False else: log_debug(self, "Directory {} created".format(cfg_path)) dst = os.path.join(cfg_path, filename) log_debug(self, "copy_cfg_to_backup:" + src + " to " + dst) if src == dst: return False try: copyfile(src, dst) except IOError: log_error( self, "Error: Couldn't copy Klipper config file to {}".format(dst) ) return False else: log_debug(self, "CfgBackup " + dst + " written") return True