mirror of
https://git.mirrors.martin98.com/https://github.com/Ultimaker/Cura
synced 2025-08-04 07:00:39 +08:00
Merge branch 'CURA-10901-warn-if-formulas-are-NOK' into yttr
This commit is contained in:
commit
92bed2a185
@ -4,6 +4,7 @@ checks:
|
|||||||
diagnostic-definition-redundant-override: true
|
diagnostic-definition-redundant-override: true
|
||||||
diagnostic-resources-macos-app-directory-name: true
|
diagnostic-resources-macos-app-directory-name: true
|
||||||
diagnostic-resource-file-deleted: true
|
diagnostic-resource-file-deleted: true
|
||||||
|
diagnostic-incorrect-formula: true
|
||||||
diagnostic-material-temperature-defined: true
|
diagnostic-material-temperature-defined: true
|
||||||
fixes:
|
fixes:
|
||||||
diagnostic-definition-redundant-override: true
|
diagnostic-definition-redundant-override: true
|
||||||
|
@ -6,6 +6,7 @@ from .linters.defintion import Definition
|
|||||||
from .linters.linter import Linter
|
from .linters.linter import Linter
|
||||||
from .linters.meshes import Meshes
|
from .linters.meshes import Meshes
|
||||||
from .linters.directory import Directory
|
from .linters.directory import Directory
|
||||||
|
from .linters.formulas import Formulas
|
||||||
|
|
||||||
|
|
||||||
def getLinter(file: Path, settings: dict) -> Optional[List[Linter]]:
|
def getLinter(file: Path, settings: dict) -> Optional[List[Linter]]:
|
||||||
@ -14,12 +15,12 @@ def getLinter(file: Path, settings: dict) -> Optional[List[Linter]]:
|
|||||||
return [Directory(file, settings)]
|
return [Directory(file, settings)]
|
||||||
|
|
||||||
if ".inst" in file.suffixes and ".cfg" in file.suffixes:
|
if ".inst" in file.suffixes and ".cfg" in file.suffixes:
|
||||||
return [Directory(file, settings), Profile(file, settings)]
|
return [Directory(file, settings), Profile(file, settings), Formulas(file, settings)]
|
||||||
|
|
||||||
if ".def" in file.suffixes and ".json" in file.suffixes:
|
if ".def" in file.suffixes and ".json" in file.suffixes:
|
||||||
if file.stem in ("fdmprinter.def", "fdmextruder.def"):
|
if file.stem in ("fdmprinter.def", "fdmextruder.def"):
|
||||||
return None
|
return [Formulas(file, settings)]
|
||||||
return [Directory(file, settings), Definition(file, settings)]
|
return [Directory(file, settings), Definition(file, settings), Formulas(file, settings)]
|
||||||
|
|
||||||
if file.parent.stem == "meshes":
|
if file.parent.stem == "meshes":
|
||||||
return [Meshes(file, settings)]
|
return [Meshes(file, settings)]
|
||||||
|
155
printer-linter/src/printerlinter/linters/formulas.py
Normal file
155
printer-linter/src/printerlinter/linters/formulas.py
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
import difflib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
from configparser import ConfigParser
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Iterator
|
||||||
|
from unittest.mock import MagicMock
|
||||||
|
|
||||||
|
from UM.Settings.DefinitionContainer import DefinitionContainer
|
||||||
|
from UM.VersionUpgradeManager import VersionUpgradeManager
|
||||||
|
from cura.CuraApplication import CuraApplication
|
||||||
|
from cura.Settings.CuraFormulaFunctions import CuraFormulaFunctions
|
||||||
|
|
||||||
|
from ..diagnostic import Diagnostic
|
||||||
|
from ..replacement import Replacement
|
||||||
|
from .linter import Linter
|
||||||
|
|
||||||
|
FORMULA_NAMES = [
|
||||||
|
"extruderValue", "extruderValues", "anyExtruderWithMaterial", "anyExtruderNrWithOrDefault",
|
||||||
|
"resolveOrValue", "defaultExtruderPosition", "valueFromContainer", "extruderValueFromContainer"
|
||||||
|
]
|
||||||
|
|
||||||
|
DELIMITERS = [r'\+', '-', '=', '/', '\*', r'\(', r'\)', r'\[', r'\]', '{', '}', ' ', '^']
|
||||||
|
|
||||||
|
|
||||||
|
class Formulas(Linter):
|
||||||
|
"""Finds issues in definition files, such as overriding default parameters."""
|
||||||
|
|
||||||
|
def __init__(self, file: Path, settings: dict) -> None:
|
||||||
|
super().__init__(file, settings)
|
||||||
|
self._cura_correction_strings = FORMULA_NAMES + list(self.getCuraSettingList())
|
||||||
|
self._definition = {}
|
||||||
|
|
||||||
|
def getCuraSettingList(self) -> list:
|
||||||
|
if VersionUpgradeManager._VersionUpgradeManager__instance is None:
|
||||||
|
VersionUpgradeManager._VersionUpgradeManager__instance = VersionUpgradeManager(MagicMock())
|
||||||
|
CuraApplication._initializeSettingDefinitions()
|
||||||
|
definition_container = DefinitionContainer("whatever")
|
||||||
|
with open(os.path.join(os.path.dirname(__file__), "..", "..", "..", "..", "resources", "definitions", "fdmprinter.def.json"), encoding="utf-8") as data:
|
||||||
|
definition_container.deserialize(data.read())
|
||||||
|
return definition_container.getAllKeys()
|
||||||
|
|
||||||
|
def check(self) -> Iterator[Diagnostic]:
|
||||||
|
if self._settings["checks"].get("diagnostic-incorrect-formula", False):
|
||||||
|
for check in self.checkFormulas():
|
||||||
|
yield check
|
||||||
|
yield
|
||||||
|
|
||||||
|
def checkFormulas(self) -> Iterator[Diagnostic]:
|
||||||
|
|
||||||
|
self._loadDefinitionFiles(self._file)
|
||||||
|
self._content = self._file.read_text()
|
||||||
|
definition_name = list(self._definition.keys())[0]
|
||||||
|
definition = self._definition[definition_name]
|
||||||
|
if "overrides" in definition:
|
||||||
|
for key, value_dict in definition["overrides"].items():
|
||||||
|
for value in value_dict:
|
||||||
|
if value in ("enable", "resolve", "value", "minimum_value_warning", "maximum_value_warning",
|
||||||
|
"maximum_value", "minimum_value"):
|
||||||
|
key_incorrect = self.checkValueIncorrect(key)
|
||||||
|
if key_incorrect:
|
||||||
|
found = self._appendCorrections(key, key)
|
||||||
|
value_incorrect = self.checkValueIncorrect(value_dict[value])
|
||||||
|
if value_incorrect:
|
||||||
|
found = self._appendCorrections(key, value_dict[value])
|
||||||
|
if key_incorrect or value_incorrect:
|
||||||
|
|
||||||
|
if len(found.group().splitlines()) > 1:
|
||||||
|
replacements = []
|
||||||
|
else:
|
||||||
|
replacements = [Replacement(
|
||||||
|
file=self._file,
|
||||||
|
offset=found.span(1)[0],
|
||||||
|
length=len(found.group()),
|
||||||
|
replacement_text=self._replacement_text)]
|
||||||
|
yield Diagnostic(
|
||||||
|
file=self._file,
|
||||||
|
diagnostic_name="diagnostic-incorrect-formula",
|
||||||
|
message=f"Given formula {found.group()} seems incorrect, Do you mean {self._correct_formula}? please correct the formula and try again.",
|
||||||
|
level="Error",
|
||||||
|
offset=found.span(0)[0],
|
||||||
|
replacements=replacements
|
||||||
|
)
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
def _appendCorrections(self, key, incorrectString):
|
||||||
|
|
||||||
|
if self._file.suffix == '.cfg':
|
||||||
|
key_with_incorrectValue = re.compile(r'(\b' + key + r'\b\s*=\s*[^=\n]+.*)')
|
||||||
|
else:
|
||||||
|
key_with_incorrectValue = re.compile(r'.*(\"' + key + r'\"[\s\:\S]*?)\{[\s\S]*?\},?')
|
||||||
|
found = key_with_incorrectValue.search(self._content)
|
||||||
|
if len(found.group().splitlines()) > 1:
|
||||||
|
self._replacement_text = ''
|
||||||
|
else:
|
||||||
|
self._replacement_text = found.group().replace(incorrectString, self._correct_formula)
|
||||||
|
return found
|
||||||
|
|
||||||
|
|
||||||
|
def _loadDefinitionFiles(self, definition_file) -> None:
|
||||||
|
""" Loads definition file contents into self._definition. Also load parent definition if it exists. """
|
||||||
|
definition_name = Path(definition_file.stem).stem
|
||||||
|
|
||||||
|
if not definition_file.exists() or definition_name in self._definition:
|
||||||
|
return
|
||||||
|
|
||||||
|
if definition_file.suffix == ".json":
|
||||||
|
# Load definition file into dictionary
|
||||||
|
self._definition[definition_name] = json.loads(definition_file.read_text())
|
||||||
|
|
||||||
|
if definition_file.suffix == ".cfg":
|
||||||
|
self._definition[definition_name] = self._parseCfg(definition_file)
|
||||||
|
|
||||||
|
|
||||||
|
def _parseCfg(self, file_path:Path) -> dict:
|
||||||
|
config = ConfigParser()
|
||||||
|
config.read([file_path])
|
||||||
|
file_data ={}
|
||||||
|
overrides = {}
|
||||||
|
|
||||||
|
available_sections = ["values"]
|
||||||
|
for section in available_sections:
|
||||||
|
options = config.options(section)
|
||||||
|
for option in options:
|
||||||
|
values ={}
|
||||||
|
values["value"] = config.get(section, option)
|
||||||
|
overrides[option] = values
|
||||||
|
file_data["overrides"]= overrides# Process the value here
|
||||||
|
|
||||||
|
return file_data
|
||||||
|
|
||||||
|
def checkValueIncorrect(self, formula) -> bool:
|
||||||
|
if isinstance(formula, str):
|
||||||
|
self._correct_formula = self._correctTyposInFormula(formula)
|
||||||
|
if self._correct_formula == formula:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def _correctTyposInFormula(self, formula):
|
||||||
|
pattern = '|'.join(DELIMITERS)
|
||||||
|
tokens = re.split(pattern, formula)
|
||||||
|
|
||||||
|
output = formula
|
||||||
|
for token in tokens:
|
||||||
|
if '(' not in token and ')' not in token:
|
||||||
|
cleaned_token = re.sub(r'[^\w\s]', '', token)
|
||||||
|
possible_matches = difflib.get_close_matches(cleaned_token, self._cura_correction_strings, n=1, cutoff=0.8)
|
||||||
|
if possible_matches:
|
||||||
|
output = output.replace(cleaned_token, possible_matches[0])
|
||||||
|
return output
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user