diff --git a/README.md b/README.md index 81b903fd..e677950f 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,7 @@ should include the following features: ## Planning Release #2 +- [ ] External Database - [ ] Table column visibity - [ ] Capture Spool-Image - [ ] Scan QR/Barcodes of a spool diff --git a/octoprint_SpoolManager/__init__.py b/octoprint_SpoolManager/__init__.py index b6076cac..0b3a51be 100644 --- a/octoprint_SpoolManager/__init__.py +++ b/octoprint_SpoolManager/__init__.py @@ -282,7 +282,6 @@ def _on_clientOpened(self, payload): selectedSpoolAsDict = Transformer.transformSpoolModelToDict(selectedSpool) else: # spool not found - self._logger.warning("Last selected Spool from plugin-settings not found in database. Maybe deleted in the meantime.") pass self._sendDataToClient(dict(action="initalData", @@ -367,6 +366,7 @@ def get_settings_defaults(self): ## Genral settings[SettingsKeys.SETTINGS_KEY_SELECTED_SPOOL_DATABASE_ID] = None + settings[SettingsKeys.SETTINGS_KEY_REMINDER_SELECTING_SPOOL] = True settings[SettingsKeys.SETTINGS_KEY_WARN_IF_SPOOL_NOT_SELECTED] = True settings[SettingsKeys.SETTINGS_KEY_WARN_IF_FILAMENT_NOT_ENOUGH] = True settings[SettingsKeys.SETTINGS_KEY_CURRENCY_SYMBOL] = "€" diff --git a/octoprint_SpoolManager/api/SpoolManagerAPI.py b/octoprint_SpoolManager/api/SpoolManagerAPI.py index de668a6f..48d76b9d 100644 --- a/octoprint_SpoolManager/api/SpoolManagerAPI.py +++ b/octoprint_SpoolManager/api/SpoolManagerAPI.py @@ -99,9 +99,26 @@ def loadSelectedSpool(self): databaseId = self._settings.get_int([SettingsKeys.SETTINGS_KEY_SELECTED_SPOOL_DATABASE_ID]) if (databaseId != None): spoolModel = self._databaseManager.loadSpool(databaseId) + if (spoolModel == None): + self._logger.warning( + "Last selected Spool from plugin-settings not found in database. Maybe deleted in the meantime.") return spoolModel + + def _createSampleSpoolModel(self): + #DisplayName, Vendor, Material, Color[# code], Diameter [mm], Density [g/cm³], Temperature [°C], TotalWeight [g], UsedWeight [g], UsedLength [mm], FirstUse [dd.mm.yyyy hh:mm], LastUse [dd.mm.yyyy hh:mm], PurchasedFrom, PurchasedOn [dd.mm.yyyy hh:mm], Cost, CostUnit, Labels, NoteText + + s1 = SpoolModel() + s1.displayName = "Number #1" + s1.vendor = "The Spool Company" + s1.material = "PETG" + s1.color = "#FF0000" + s1.diameter = 1.75 + s1.density = 1.27 + return s1 + + ################################################### APIs ############################################################################################## ALLOWED TO PRINT @@ -110,6 +127,7 @@ def allowed_to_print(self): checkForSelectedSpool = self._settings.get_boolean([SettingsKeys.SETTINGS_KEY_WARN_IF_SPOOL_NOT_SELECTED]) checkForFilamentLength = self._settings.get_boolean([SettingsKeys.SETTINGS_KEY_WARN_IF_FILAMENT_NOT_ENOUGH]) + reminderSelectingSpool = self._settings.get_boolean([SettingsKeys.SETTINGS_KEY_REMINDER_SELECTING_SPOOL]) if (checkForFilamentLength == False and checkForSelectedSpool == False): return flask.jsonify({ @@ -123,18 +141,24 @@ def allowed_to_print(self): "result": "noSpoolSelected", }) - if (checkForFilamentLength == True): - # check if loaded - if (spoolModel == None): + if (checkForFilamentLength == True and spoolModel != None): + # # check if loaded + # if (spoolModel == None): + # return flask.jsonify({ + # "result": "noSpoolForUsageCheck", + # }) + # else: + result = self.checkRemainingFilament(); + if (result == False): return flask.jsonify({ - "result": "noSpoolForUsageCheck", + "result": "filamentNotEnough", }) - else: - result = self.checkRemainingFilament(); - if (result == False): - return flask.jsonify({ - "result": "filamentNotEnough", - }) + + if (reminderSelectingSpool == True and spoolModel != None): + return flask.jsonify({ + "result": "reminderSpoolSelection", + "spoolName": spoolModel.displayName + }) return flask.jsonify({ "result": "startPrint" @@ -143,6 +167,7 @@ def allowed_to_print(self): ##################################################################################################### SELECT SPOOL @octoprint.plugin.BlueprintPlugin.route("/selectSpool", methods=["PUT"]) def select_spool(self): + self._logger.info("API Store selected spool") jsonData = request.json databaseId = self._getValueFromJSONOrNone("databaseId", jsonData) @@ -150,13 +175,18 @@ def select_spool(self): spoolModel = self._databaseManager.loadSpool(databaseId) # check if loaded if (spoolModel != None): + self._logger.info("Store selected spool '"+spoolModel.displayName+"' in settings.") + # - store spool in Settings self._settings.set_int([SettingsKeys.SETTINGS_KEY_SELECTED_SPOOL_DATABASE_ID], databaseId) self._settings.save() self.checkRemainingFilament() + else: + self._logger.warning("Selected Spool with id '"+str(databaseId)+"' not in database anymore. Maybe deleted in the meantime.") else: # No selection + self._logger.info("Clear stored selected spool in settings.") self._settings.set_int([SettingsKeys.SETTINGS_KEY_SELECTED_SPOOL_DATABASE_ID], None) self._settings.save() pass @@ -169,7 +199,7 @@ def select_spool(self): ################################################################################################## LOAD ALL SPOOLS @octoprint.plugin.BlueprintPlugin.route("/loadSpoolsByQuery", methods=["GET"]) def load_allSpools(self): - + self._logger.debug("API Load all spool") # sp1 = SpoolModel() # sp1.displayName = "Spool No.1" # sp1.vendor = "Janbex" @@ -237,13 +267,16 @@ def load_allSpools(self): ####################################################################################################### SAVE SPOOL @octoprint.plugin.BlueprintPlugin.route("/saveSpool", methods=["PUT"]) def save_spool(self): + self._logger.info("API Save spool") jsonData = request.json databaseId = self._getValueFromJSONOrNone("databaseId", jsonData) if (databaseId != None): + self._logger.info("Update spool with database id '"+str(databaseId)+"'") spoolModel = self._databaseManager.loadSpool(databaseId) self._updateSpoolModelFromJSONData(spoolModel, jsonData) else: + self._logger.info("Create new spool") spoolModel = SpoolModel() self._updateSpoolModelFromJSONData(spoolModel, jsonData) @@ -255,6 +288,7 @@ def save_spool(self): ##################################################################################################### DELETE SPOOL @octoprint.plugin.BlueprintPlugin.route("/deleteSpool/", methods=["DELETE"]) def delete_printjob(self, databaseId): + self._logger.info("API Delete spool with database id '" + str(databaseId) + "'") printJob = self._databaseManager.deleteSpool(databaseId) # snapshotFilename = CameraManager.buildSnapshotFilename(printJob.printStartDateTime) # self._cameraManager.deleteSnapshot(snapshotFilename) diff --git a/octoprint_SpoolManager/api/Transformer.py b/octoprint_SpoolManager/api/Transformer.py index c21dad62..d7c0f88f 100644 --- a/octoprint_SpoolManager/api/Transformer.py +++ b/octoprint_SpoolManager/api/Transformer.py @@ -4,6 +4,29 @@ from octoprint_SpoolManager.models.SpoolModel import SpoolModel from octoprint_SpoolManager.common import StringUtils +def _calculateRemainingWeight(usedWeight, totalWeight): + if (usedWeight == None or totalWeight == None): + return None + + if ( (type(usedWeight) == int or type(usedWeight) == float) and + (type(totalWeight) == int or type(totalWeight) == float) ): + result = totalWeight - usedWeight + return result + + return None + +def _calculateUsedPercentage(remainingWeight, totalWeight): + if (remainingWeight == None or totalWeight == None): + return None + + if ( (type(remainingWeight) == int or type(remainingWeight) == float) and + (type(totalWeight) == int or type(totalWeight) == float) ): + result = remainingWeight / (totalWeight / 100.0); + return result + + return None + + def transformSpoolModelToDict(spoolModel): spoolAsDict = spoolModel.__data__ @@ -14,10 +37,24 @@ def transformSpoolModelToDict(spoolModel): spoolAsDict["created"] = StringUtils.formatDateTime(spoolModel.created) - # Decimal and date time needs to be converted + + totalWeight = spoolModel.totalWeight + usedWeight = spoolModel.usedWeight + remainingWeight = _calculateRemainingWeight(usedWeight, totalWeight) + usedPercentage = _calculateUsedPercentage(remainingWeight, totalWeight) + + spoolAsDict["remainingWeight"] = StringUtils.formatFloat(remainingWeight) + spoolAsDict["usedPercentage"] = StringUtils.formatFloat(usedPercentage) + + + # Decimal and date time needs to be converted. ATTENTION orgiginal fields will be modified spoolAsDict["totalWeight"] = StringUtils.formatFloat(spoolModel.totalWeight) spoolAsDict["usedWeight"] = StringUtils.formatFloat(spoolModel.usedWeight) + + + + # spoolAsDict["temperature"] = StringUtils.formatSave("{:.02f}", spoolAsDict["temperature"], "") # spoolAsDict["weight"] = StringUtils.formatSave("{:.02f}", spoolAsDict["weight"], "") # spoolAsDict["remainingWeight"] = StringUtils.formatSave("{:.02f}", spoolAsDict["remainingWeight"], "") diff --git a/octoprint_SpoolManager/common/SettingsKeys.py b/octoprint_SpoolManager/common/SettingsKeys.py index 7684cb28..337c7994 100644 --- a/octoprint_SpoolManager/common/SettingsKeys.py +++ b/octoprint_SpoolManager/common/SettingsKeys.py @@ -5,6 +5,7 @@ class SettingsKeys(): SETTINGS_KEY_SELECTED_SPOOL_DATABASE_ID = "selectedSpoolDatabaseId" + SETTINGS_KEY_REMINDER_SELECTING_SPOOL = "reminderSelectingSpool" SETTINGS_KEY_WARN_IF_SPOOL_NOT_SELECTED = "warnIfSpoolNotSelected" SETTINGS_KEY_WARN_IF_FILAMENT_NOT_ENOUGH = "warnIfFilamentNotEnough" diff --git a/octoprint_SpoolManager/static/css/SpoolManager.css b/octoprint_SpoolManager/static/css/SpoolManager.css index 2e83a02a..421d51a9 100644 --- a/octoprint_SpoolManager/static/css/SpoolManager.css +++ b/octoprint_SpoolManager/static/css/SpoolManager.css @@ -31,12 +31,12 @@ overflow: hidden; text-overflow: ellipsis; } - - .dropdown-menu { - max-height: 120px; - overflow-y: auto; - overflow-x: hidden; - } +// TODO Maybe not needed any more +// .dropdown-menu { +// max-height: 120px; +// overflow-y: auto; +// overflow-x: hidden; +// } diff --git a/octoprint_SpoolManager/static/js/ComponentFactory.js b/octoprint_SpoolManager/static/js/ComponentFactory.js index 4960044d..fdb72ff9 100644 --- a/octoprint_SpoolManager/static/js/ComponentFactory.js +++ b/octoprint_SpoolManager/static/js/ComponentFactory.js @@ -203,13 +203,13 @@ function ComponentFactory(pluginId) { showHexInput : true, allowBlank : false, basicColors : { - white : 'fff', - black : '000', - red : 'f00', + white : 'ffffff', + black : '000000', + red : 'ff0000', green : '008000', - blue : '00f', - yellow : 'ff0', - orange : 'f60', + blue : '0000ff', + yellow : 'ffff00', + orange : 'ffa500', purple : '800080', // gray : '808080', // darkgray : 'A9A9A9', diff --git a/octoprint_SpoolManager/static/js/SpoolManager-EditSpoolDialog.js b/octoprint_SpoolManager/static/js/SpoolManager-EditSpoolDialog.js index db28d2b9..40ae25b4 100644 --- a/octoprint_SpoolManager/static/js/SpoolManager-EditSpoolDialog.js +++ b/octoprint_SpoolManager/static/js/SpoolManager-EditSpoolDialog.js @@ -169,9 +169,9 @@ function SpoolManagerEditSpoolDialog(){ this.color(updateData.color == null ? DEFAULT_COLOR : updateData.color); this.temperature(updateData.temperature); this.totalWeight(updateData.totalWeight); -// this.remainingWeight(updateData.remainingWeight); + this.remainingWeight(updateData.remainingWeight); this.code(updateData.code); -// this.usedPercentage(updateData.usedPercentage); // TODO needed? + this.usedPercentage(updateData.usedPercentage); this.usedLength(updateData.usedLength); this.usedWeight(updateData.usedWeight); @@ -349,7 +349,7 @@ function SpoolManagerEditSpoolDialog(){ self.updateUsedPercentage(); }); - // update used percentage + // update RemainingWeight self.updateRemainingWeight = function(){ var total = self.spoolItemForEditing.totalWeight(); var used = self.spoolItemForEditing.usedWeight(); diff --git a/octoprint_SpoolManager/static/js/SpoolManager.js b/octoprint_SpoolManager/static/js/SpoolManager.js index d767e010..5ce4d757 100644 --- a/octoprint_SpoolManager/static/js/SpoolManager.js +++ b/octoprint_SpoolManager/static/js/SpoolManager.js @@ -142,7 +142,14 @@ $(function() { displayName = " "; } - var label = color + " - " + material + " - " + displayName; + var remainingFilament = spoolItem.remainingWeight; + if (remainingFilament && displayName.trim().length != 0){ + remainingFilament = " (" + remainingFilament + "%)" ; + } else { + remainingFilament = ""; + } + + var label = color + " - " + material + " - " + displayName + remainingFilament; return label } @@ -239,15 +246,24 @@ $(function() { } return; } - if ("noSpoolForUsageCheck" == result){ - self.showPopUp("Error", "", "No Spool selected for usage check. Select a spool first"); - return - } +// Not needed because a length check is only done, if spool was selected +// if ("noSpoolForUsageCheck" == result){ +// self.showPopUp("Error", "", "No Spool selected for usage check. Select a spool first"); +// return; +// } if ("filamentNotEnough" == result){ var check = confirm('Not enough filament. Do you want to start the print anyway?'); if (check == true) { startPrint(); } + return; + } + if ("reminderSpoolSelection" == result){ + var question = "Please verify your selected Spool '"+responseData.spoolName+"'. Do you want to start the print anyway?"; + var check = confirm(question); + if (check == true) { + startPrint(); + } } } }); diff --git a/octoprint_SpoolManager/templates/SpoolManager_settings.jinja2 b/octoprint_SpoolManager/templates/SpoolManager_settings.jinja2 index d2b07c25..398892b1 100644 --- a/octoprint_SpoolManager/templates/SpoolManager_settings.jinja2 +++ b/octoprint_SpoolManager/templates/SpoolManager_settings.jinja2 @@ -14,6 +14,13 @@
+
+
+ +
+
- +
@@ -238,8 +238,10 @@ -->
@@ -278,7 +280,7 @@
- +
diff --git a/octoprint_SpoolManager/test/test_CSVExporterImporter.py b/octoprint_SpoolManager/test/test_CSVExporterImporter.py new file mode 100644 index 00000000..9ef25039 --- /dev/null +++ b/octoprint_SpoolManager/test/test_CSVExporterImporter.py @@ -0,0 +1,97 @@ +import logging + +# from octoprint_PrintJobHistory.api import TransformPrintJob2JSON +# from octoprint_PrintJobHistory.common.CSVExportImporter import parseCSV, transform2CSV +# +# logging.basicConfig(level=logging.DEBUG) +# testLogger = logging.getLogger("testLogger") +# logging.info("Start CSV-Test") +# ################################### +# ## IMPORT +# +# def updateParsingStatus(lineNumber): +# print("Parsing line '{}'".format(int(lineNumber))) +# +# print("START IMPORT") +# errorCollection = list() +# csvFile4Import = "/Users/o0632/0_Projekte/3DDruck/OctoPrint/OctoPrint-PrintJobHistory/testdata/sample.csv" +# result = parseCSV(csvFile4Import, updateParsingStatus, errorCollection, testLogger, deleteAfterParsing=False) +# print(errorCollection) +# print("END IMPORT") + +# +# +# ################################### +# # EXPORT CSV TEST with single MOCK-Object +# singleJob = { +# "userName": "Olaf", +# # "printStartDateTimeFormatted": datetime.datetime(2019, 12, 11, 14, 53), +# "printStartDateTimeFormatted": "2019-12-11 14:53", +# "printStatusResult": "success", +# "duration": 0, +# "fileSize": 3123, +# "temperatureModels": [ +# {"sensorName": "bed", "sensorValue": 123.3}, +# {"sensorName": "tool0", "sensorValue": 321.1} +# ], +# "filamentModel": { +# "spoolName": "My Best Spool", +# "material": "PETG", +# "diameter": 1.234, +# "density": 1.25, +# "usedLengthFormatted": 9.24, +# "calculatedLengthFormatted": 100.24, +# "usedWeight": 6.06, +# "usedCost": 0.04 +# } +# } + +# NOT WORKING +# allJobsModels = list() +# allJobsModels.append(singleJob) +# allJobsDict = TransformPrintJob2JSON.transformAllPrintJobModels(allJobsModels) +# csvResult = transform2CSV(allJobsDict) +# +# print(csvResult) + +# from datetime import date +# from datetime import datetime +# +# now = datetime.now() +# # today = date.today() +# print(now.strftime('%I:%M')) +# pass + +# from octoprint.plugins.softwareupdate.version_checks import github_release +# +# result = github_release._is_current(dict( +# local=dict(value="1.0.0rc6"), +# remote=dict(value="1.0.0rc7")), +# "python", force_base=False ) +# if (result): +# print("Locale Version is newer or equal") +# else: +# print("Remote Version is newer, update available") + + +# f = open("costUnitBug.txt", "r") +# fieldValue = f.readline() +# f.close() +# fieldValue = fieldValue.strip() +# costUnit = fieldValue[-1] +# if (costUnit.isdigit()): +# # no unit present +# usedCost = float(fieldValue) +# else: +# # Split between cot and unit +# costValue = "" +# for i in range(len(fieldValue)): +# c = fieldValue[i] +# if (c.isdigit() or c == "." ): +# costValue += c +# else: +# costUnit = fieldValue[i:] +# break +# +# print(usedCost) +# print(costUnit) diff --git a/screenshots/editSpool-dialog.png b/screenshots/editSpool-dialog.png index 4004fdda..c14a0924 100644 Binary files a/screenshots/editSpool-dialog.png and b/screenshots/editSpool-dialog.png differ diff --git a/screenshots/scanSpool-dialog.png b/screenshots/scanSpool-dialog.png index 57c46034..feff5bd2 100644 Binary files a/screenshots/scanSpool-dialog.png and b/screenshots/scanSpool-dialog.png differ diff --git a/setup.py b/setup.py index f110fe8b..7b608b68 100644 --- a/setup.py +++ b/setup.py @@ -15,7 +15,7 @@ # The plugin's version. Can be overwritten within OctoPrint's internal data via __plugin_version__ in the plugin module # -plugin_version = "1.0.0rc2" +plugin_version = "1.0.0rc3" # The plugin's description. Can be overwritten within OctoPrint's internal data via __plugin_description__ in the plugin # module @@ -25,7 +25,7 @@ plugin_author = "OllisGit" # The plugin's author's mail address. -plugin_author_email = "you@example.com" +plugin_author_email = "ollisgit@gmail.com" # The plugin's homepage URL. Can be overwritten within OctoPrint's internal data via __plugin_url__ in the plugin module plugin_url = "https://github.com/you/OctoPrint-SpoolManager" diff --git a/testdata/sample_import.csv b/testdata/sample_import.csv new file mode 100644 index 00000000..a7c1cd06 --- /dev/null +++ b/testdata/sample_import.csv @@ -0,0 +1,5 @@ +DisplayName, Vendor, Material, Color [#code], Diameter [mm], Density [g/cm³], Temperature [°C], TotalWeight [g], UsedWeight [g], UsedLength [mm], FirstUse [dd.mm.yyyy hh:mm], LastUse [dd.mm.yyyy hh:mm], PurchasedFrom, PurchasedOn [dd.mm.yyyy hh:mm], Cost, CostUnit, Labels, NoteText + + +Name, Cost, Weight, Used, TempOffset -> Vendor, Material, Density, Diameter +