mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
* Added initial draft for machines * refactor: isPluginRegistryLoaded check into own ready function * Added suggestions from codereview * Refactor: base_drivers -> machine_types * Use new BaseInvenTreeSetting unique interface * Fix Django not ready error * Added get_machines function to driver - get_machines function on driver - get_machine function on driver - initialized attribute on machine * Added error handeling for driver and machine type * Extended get_machines functionality * Export everything from plugin module * Fix spelling mistakes * Better states handeling, BaseMachineType is now used instead of Machine Model * Use uuid as pk * WIP: machine termination hook * Remove termination hook as this does not work with gunicorn * Remove machine from registry after delete * Added ClassProviderMixin * Check for slug dupplication * Added config_type to MachineSettings to define machine/driver settings * Refactor helper mixins into own file in InvenTree app * Fixed typing and added required_attributes for BaseDriver * fix: generic status import * Added first draft for machine states * Added convention for status codes * Added update_machine hook * Removed unnecessary _key suffix from machine config model * Initil draft for machine API * Refactored BaseInvenTreeSetting all_items and allValues method * Added required to InvenTreeBaseSetting and check_settings method * check if all required machine settings are defined and refactor: use getattr * Fix: comment * Fix initialize error and python 3.9 compability * Make machine states available through the global states api * Added basic PUI machine admin implementation that is still in dev * Added basic machine setting UI to PUI * Added machine detail view to PUI admin center * Fix merge issues * Fix style issues * Added machine type,machine driver,error stack tables * Fix style in machine/serializers.py * Added pui link from machine to machine type/driver drawer * Removed only partially working django admin in favor of the PUI admin center implementation * Added required field to settings item * Added machine restart function * Added restart requird badge to machine table/drawer * Added driver init function * handle error functions for machines and registry * Added driver errors * Added machine table to driver drawer * Added back button to detail drawer component * Fix auto formatable pre-commit * fix: style * Fix deepsource * Removed slug field from table, added more links between drawers, remove detail drawer blur * Added initial docs * Removed description from driver/machine type select and fixed disabled driver select if no machine type is selected * Added basic label printing implementation * Remove translated column names because they are now retrieved from the api * Added printer location setting * Save last 10 used printer machine per user and sort them in the printing dialog * Added BasePrintingOptionsSerializer for common options * Fix not printing_options are not properly casted to its internal value * Fix type * Improved machine docs * Fix docs * Added UNKNOWN status code to label printer status * Skip machine loading when running migrations * Fix testing? * Fix: tests? * Fix: tests? * Disable docs check precommit * Disable docs check precommit * First draft for tests * fix test * Add type ignore * Added API tests * Test ci? * Add more tests * Added more tests * Bump api version * Changed driver/base driver naming schema * Added more tests * Fix tests * Added setting choice with kwargs and get_machines with initialized=None * Refetch table after deleting machine * Fix test --------- Co-authored-by: Matthias Mair <code@mjmair.com>
198 lines
6.7 KiB
Python
Executable File
198 lines
6.7 KiB
Python
Executable File
"""Models for the machine app."""
|
|
|
|
import uuid
|
|
from typing import Literal
|
|
|
|
from django.contrib import admin
|
|
from django.db import models
|
|
from django.utils.html import escape, format_html_join
|
|
from django.utils.safestring import mark_safe
|
|
from django.utils.translation import gettext_lazy as _
|
|
|
|
import common.models
|
|
from machine import registry
|
|
|
|
|
|
class MachineConfig(models.Model):
|
|
"""A Machine objects represents a physical machine."""
|
|
|
|
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
|
|
|
|
name = models.CharField(
|
|
unique=True,
|
|
max_length=255,
|
|
verbose_name=_('Name'),
|
|
help_text=_('Name of machine'),
|
|
)
|
|
|
|
machine_type = models.CharField(
|
|
max_length=255, verbose_name=_('Machine Type'), help_text=_('Type of machine')
|
|
)
|
|
|
|
driver = models.CharField(
|
|
max_length=255,
|
|
verbose_name=_('Driver'),
|
|
help_text=_('Driver used for the machine'),
|
|
)
|
|
|
|
active = models.BooleanField(
|
|
default=True, verbose_name=_('Active'), help_text=_('Machines can be disabled')
|
|
)
|
|
|
|
def __str__(self) -> str:
|
|
"""String representation of a machine."""
|
|
return f'{self.name}'
|
|
|
|
def save(self, *args, **kwargs) -> None:
|
|
"""Custom save function to capture creates/updates to notify the registry."""
|
|
created = self._state.adding
|
|
|
|
old_machine = None
|
|
if (
|
|
not created
|
|
and self.pk
|
|
and (old_machine := MachineConfig.objects.get(pk=self.pk))
|
|
):
|
|
old_machine = old_machine.to_dict()
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
if created:
|
|
# machine was created, add it to the machine registry
|
|
registry.add_machine(self, initialize=True)
|
|
elif old_machine:
|
|
# machine was updated, invoke update hook
|
|
# elif acts just as a type gate, old_machine should be defined always
|
|
# if machine is not created now which is already handled above
|
|
registry.update_machine(old_machine, self)
|
|
|
|
def delete(self, *args, **kwargs):
|
|
"""Remove machine from registry first."""
|
|
if self.machine:
|
|
registry.remove_machine(self.machine)
|
|
|
|
return super().delete(*args, **kwargs)
|
|
|
|
def to_dict(self):
|
|
"""Serialize a machine config to a dict including setting."""
|
|
machine = {f.name: f.value_to_string(self) for f in self._meta.fields}
|
|
machine['settings'] = {
|
|
(setting.config_type, setting.key): setting.value
|
|
for setting in MachineSetting.objects.filter(machine_config=self)
|
|
}
|
|
return machine
|
|
|
|
@property
|
|
def machine(self):
|
|
"""Machine instance getter."""
|
|
return registry.get_machine(self.pk)
|
|
|
|
@property
|
|
def errors(self):
|
|
"""Machine errors getter."""
|
|
return getattr(self.machine, 'errors', [])
|
|
|
|
@admin.display(boolean=True, description=_('Driver available'))
|
|
def is_driver_available(self) -> bool:
|
|
"""Status if driver for machine is available."""
|
|
return self.machine is not None and self.machine.driver is not None
|
|
|
|
@admin.display(boolean=True, description=_('No errors'))
|
|
def no_errors(self) -> bool:
|
|
"""Status if machine has errors."""
|
|
return len(self.errors) == 0
|
|
|
|
@admin.display(boolean=True, description=_('Initialized'))
|
|
def initialized(self) -> bool:
|
|
"""Status if machine is initialized."""
|
|
return getattr(self.machine, 'initialized', False)
|
|
|
|
@admin.display(description=_('Errors'))
|
|
def get_admin_errors(self):
|
|
"""Get machine errors for django admin interface."""
|
|
return format_html_join(
|
|
mark_safe('<br>'), '{}', ((str(error),) for error in self.errors)
|
|
) or mark_safe(f"<i>{_('No errors')}</i>")
|
|
|
|
@admin.display(description=_('Machine status'))
|
|
def get_machine_status(self):
|
|
"""Get machine status for django admin interface."""
|
|
if self.machine is None:
|
|
return None
|
|
|
|
out = mark_safe(self.machine.status.render(self.machine.status))
|
|
|
|
if self.machine.status_text:
|
|
out += escape(f' ({self.machine.status_text})')
|
|
|
|
return out
|
|
|
|
|
|
class MachineSetting(common.models.BaseInvenTreeSetting):
|
|
"""This models represents settings for individual machines."""
|
|
|
|
typ = 'machine_config'
|
|
extra_unique_fields = ['machine_config', 'config_type']
|
|
|
|
class Meta:
|
|
"""Meta for MachineSetting."""
|
|
|
|
unique_together = [('machine_config', 'config_type', 'key')]
|
|
|
|
class ConfigType(models.TextChoices):
|
|
"""Machine setting config type enum."""
|
|
|
|
MACHINE = 'M', _('Machine')
|
|
DRIVER = 'D', _('Driver')
|
|
|
|
machine_config = models.ForeignKey(
|
|
MachineConfig,
|
|
related_name='settings',
|
|
verbose_name=_('Machine Config'),
|
|
on_delete=models.CASCADE,
|
|
)
|
|
|
|
config_type = models.CharField(
|
|
verbose_name=_('Config type'), max_length=1, choices=ConfigType.choices
|
|
)
|
|
|
|
def save(self, *args, **kwargs) -> None:
|
|
"""Custom save method to notify the registry on changes."""
|
|
old_machine = self.machine_config.to_dict()
|
|
|
|
super().save(*args, **kwargs)
|
|
|
|
registry.update_machine(old_machine, self.machine_config)
|
|
|
|
@classmethod
|
|
def get_config_type(cls, config_type_str: Literal['M', 'D']):
|
|
"""Helper method to get the correct enum value for easier usage with literal strings."""
|
|
if config_type_str == 'M':
|
|
return cls.ConfigType.MACHINE
|
|
elif config_type_str == 'D':
|
|
return cls.ConfigType.DRIVER
|
|
|
|
@classmethod
|
|
def get_setting_definition(cls, key, **kwargs):
|
|
"""In the BaseInvenTreeSetting class, we have a class attribute named 'SETTINGS'.
|
|
|
|
which is a dict object that fully defines all the setting parameters.
|
|
|
|
Here, unlike the BaseInvenTreeSetting, we do not know the definitions of all settings
|
|
'ahead of time' (as they are defined externally in the machine driver).
|
|
|
|
Settings can be provided by the caller, as kwargs['settings'].
|
|
|
|
If not provided, we'll look at the machine registry to see what settings this machine driver requires
|
|
"""
|
|
if 'settings' not in kwargs:
|
|
machine_config: MachineConfig = kwargs.pop('machine_config', None)
|
|
if machine_config and machine_config.machine:
|
|
config_type = kwargs.get('config_type', None)
|
|
if config_type == cls.ConfigType.DRIVER:
|
|
kwargs['settings'] = machine_config.machine.driver_settings
|
|
elif config_type == cls.ConfigType.MACHINE:
|
|
kwargs['settings'] = machine_config.machine.machine_settings
|
|
|
|
return super().get_setting_definition(key, **kwargs)
|