Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor states/status #4857

Merged
merged 74 commits into from
Jun 9, 2023
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
8911cc0
add file for states
matmair May 19, 2023
c3e62be
move general definition out
matmair May 19, 2023
d7cbce0
add some tests and docs
matmair May 19, 2023
0f0b10d
add tests for invalid definitions
matmair May 19, 2023
1220dcb
make status_label tag generic
matmair May 19, 2023
8c0adc4
move templatetags
matmair May 19, 2023
e8ebaf9
remove unused tag
matmair May 19, 2023
a21a46d
rename test file
matmair May 19, 2023
3368a84
make status label a lookup
matmair May 19, 2023
6701ec2
rename tags
matmair May 19, 2023
e68f98c
move import structure
matmair May 19, 2023
5469150
add missing tag
matmair May 19, 2023
d3ca854
collect states dynamically
matmair May 19, 2023
d79808c
fix context function
matmair May 19, 2023
1d74e93
move api function out
matmair May 19, 2023
51e4e3f
add tests for tags
matmair May 20, 2023
6bb1358
rename tests
matmair May 20, 2023
92652e6
refactor imports
matmair May 20, 2023
dccadbc
Add test for API function
matmair May 21, 2023
70c75ed
improve errors and add tests for imporved errors
matmair May 21, 2023
559b993
make test calls simpler
matmair May 21, 2023
d70f6a4
Merge branch 'inventree:master' into refactor-states
matmair May 22, 2023
cffb37c
refactor definitions to use enums
matmair May 22, 2023
6fdaa78
switch to enum
matmair May 22, 2023
dccd066
refactor definitions to use enums
matmair May 22, 2023
66b84af
fix lookup
matmair May 22, 2023
2d06fe0
fix tag name
matmair May 22, 2023
018e9cb
make _TAG lookup a function
matmair May 22, 2023
310c2c3
cleanup BaseEnum
matmair May 22, 2023
0e136b0
make _TAG definition simpler
matmair May 22, 2023
93e86f0
restructure status codes to enum
matmair May 22, 2023
405a2e9
reduce LoC
matmair May 22, 2023
00793bf
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair May 27, 2023
57a2a38
type status codes as int
matmair May 27, 2023
95d568a
add specific function for template context
matmair May 27, 2023
f5dc0d6
Add definition for lookups
matmair May 27, 2023
97af442
fix filter lookup
matmair May 27, 2023
8954b10
TEST: "fix" action lookup
matmair May 27, 2023
a588c71
Add missing migrations
matmair May 27, 2023
35e8af3
Make all group code references explict
matmair May 27, 2023
0dfc67a
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair May 30, 2023
1b36f23
change default on models to value
matmair May 30, 2023
f0d0e2b
switch to IntEnum
matmair May 30, 2023
f07e637
move groups into a seperate class
matmair May 30, 2023
9aa04a0
only request _TAG if it exsists
matmair May 30, 2023
46ad640
use value and list
matmair May 30, 2023
e9eab4e
use dedicated groups
matmair May 30, 2023
117e272
fix stock assigment
matmair May 30, 2023
130c95d
fix order code
matmair May 30, 2023
c7bb1ba
more fixes
matmair May 30, 2023
65baf44
fix borked change
matmair May 30, 2023
e3352ef
fix render lookup
matmair May 30, 2023
c685185
add group
matmair Jun 2, 2023
4641f71
fix import
matmair Jun 2, 2023
3b9a428
fix syntax
matmair Jun 4, 2023
3781593
clenup
matmair Jun 4, 2023
f456296
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair Jun 4, 2023
e082f20
fix migrations
matmair Jun 4, 2023
c9933ff
fix typo
matmair Jun 4, 2023
8d53d4a
fix wrong value usage
matmair Jun 4, 2023
01c384b
fix test
matmair Jun 4, 2023
92c9140
remove group section
matmair Jun 4, 2023
2cf8f69
remove group section
matmair Jun 4, 2023
5bfece6
add more test cases
matmair Jun 4, 2023
83f534a
Add more docstring
matmair Jun 5, 2023
4725239
move choices out of migrations
matmair Jun 5, 2023
ff62693
change import ordeR?
matmair Jun 5, 2023
c74b046
last try before I revert
matmair Jun 5, 2023
e93eee4
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair Jun 6, 2023
d283170
Update part.migrations.0112
SchrodingersGat Jun 7, 2023
1dc9f38
Add unit test for migration
SchrodingersGat Jun 7, 2023
fe74215
Update reference to PR
SchrodingersGat Jun 7, 2023
229102b
Merge branch 'new-migration-fix' of https://github.com/SchrodingersGa…
matmair Jun 8, 2023
c4c3253
Merge branch 'master' of https://github.com/inventree/InvenTree into …
matmair Jun 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 0 additions & 41 deletions InvenTree/InvenTree/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,47 +306,6 @@ def post(self, request, *args, **kwargs):
return Response(results)


class StatusView(APIView):
"""Generic API endpoint for discovering information on 'status codes' for a particular model.

This class should be implemented as a subclass for each type of status.
For example, the API endpoint /stock/status/ will have information about
all available 'StockStatus' codes
"""

permission_classes = [
permissions.IsAuthenticated,
]

# Override status_class for implementing subclass
MODEL_REF = 'statusmodel'

def get_status_model(self, *args, **kwargs):
"""Return the StatusCode moedl based on extra parameters passed to the view"""

status_model = self.kwargs.get(self.MODEL_REF, None)

if status_model is None:
raise ValidationError(f"StatusView view called without '{self.MODEL_REF}' parameter")

return status_model

def get(self, request, *args, **kwargs):
"""Perform a GET request to learn information about status codes"""

status_class = self.get_status_model()

if not status_class:
raise NotImplementedError("status_class not defined for this endpoint")

data = {
'class': status_class.__name__,
'values': status_class.dict(),
}

return Response(data)


class MetadataView(RetrieveUpdateAPI):
"""Generic API endpoint for reading and editing metadata for a model"""

Expand Down
18 changes: 4 additions & 14 deletions InvenTree/InvenTree/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
"""Provides extra global data to all templates."""

import InvenTree.status
from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus,
ReturnOrderLineStatus, ReturnOrderStatus,
SalesOrderStatus, StockHistoryCode,
StockStatus)
from generic.states import StatusCode
from InvenTree.helpers import inheritors
from users.models import RuleSet, check_user_role


Expand Down Expand Up @@ -57,16 +55,8 @@ def status_codes(request):

request._inventree_status_codes = True

return {
# Expose the StatusCode classes to the templates
'ReturnOrderStatus': ReturnOrderStatus,
'ReturnOrderLineStatus': ReturnOrderLineStatus,
'SalesOrderStatus': SalesOrderStatus,
'PurchaseOrderStatus': PurchaseOrderStatus,
'BuildStatus': BuildStatus,
'StockStatus': StockStatus,
'StockHistoryCode': StockHistoryCode,
}
classes = inheritors(StatusCode)
matmair marked this conversation as resolved.
Show resolved Hide resolved
return {cls.__name__: cls for cls in classes}


def user_roles(request):
Expand Down
6 changes: 6 additions & 0 deletions InvenTree/InvenTree/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,12 @@ def inheritors(cls):
return subcls


def inheritors_attr(cls, attr):
"""Return the attribute from each inheriting class."""
classes = inheritors(cls)
return {getattr(cls, attr): cls for cls in classes}


def notify_responsible(instance, sender, content: NotificationBody = InvenTreeNotificationBodies.NewOrder, exclude=None):
"""Notify all responsible parties of a change in an instance.

Expand Down
1 change: 1 addition & 0 deletions InvenTree/InvenTree/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@
'stock.apps.StockConfig',
'users.apps.UsersConfig',
'plugin.apps.PluginAppConfig',
'generic',
'InvenTree.apps.InvenTreeConfig', # InvenTree app runs last

# Core django modules
Expand Down
128 changes: 8 additions & 120 deletions InvenTree/InvenTree/status_codes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,130 +2,12 @@

from django.utils.translation import gettext_lazy as _


class StatusCode:
"""Base class for representing a set of StatusCodes.

This is used to map a set of integer values to text.
"""

colors = {}

@classmethod
def render(cls, key, large=False):
"""Render the value as a HTML label."""
# If the key cannot be found, pass it back
if key not in cls.options.keys():
return key

value = cls.options.get(key, key)
color = cls.colors.get(key, 'secondary')

span_class = f'badge rounded-pill bg-{color}'

return "<span class='{cl}'>{value}</span>".format(
cl=span_class,
value=value
)

@classmethod
def list(cls):
"""Return the StatusCode options as a list of mapped key / value items."""
return list(cls.dict().values())

@classmethod
def text(cls, key):
"""Text for supplied status code."""
return cls.options.get(key, None)

@classmethod
def items(cls):
"""All status code items."""
return cls.options.items()

@classmethod
def keys(cls):
"""All status code keys."""
return cls.options.keys()

@classmethod
def labels(cls):
"""All status code labels."""
return cls.options.values()

@classmethod
def names(cls):
"""Return a map of all 'names' of status codes in this class

Will return a dict object, with the attribute name indexed to the integer value.

e.g.
{
'PENDING': 10,
'IN_PROGRESS': 20,
}
"""
keys = cls.keys()
status_names = {}

for d in dir(cls):
if d.startswith('_'):
continue
if d != d.upper():
continue

value = getattr(cls, d, None)

if value is None:
continue
if callable(value):
continue
if type(value) != int:
continue
if value not in keys:
continue

status_names[d] = value

return status_names

@classmethod
def dict(cls):
"""Return a dict representation containing all required information"""
values = {}

for name, value, in cls.names().items():
entry = {
'key': value,
'name': name,
'label': cls.label(value),
}

if hasattr(cls, 'colors'):
if color := cls.colors.get(value, None):
entry['color'] = color

values[name] = entry

return values

@classmethod
def label(cls, value):
"""Return the status code label associated with the provided value."""
return cls.options.get(value, value)

@classmethod
def value(cls, label):
"""Return the value associated with the provided label."""
for k in cls.options.keys():
if cls.options[k].lower() == label.lower():
return k

raise ValueError("Label not found")
from generic.states import StatusCode


class PurchaseOrderStatus(StatusCode):
"""Defines a set of status codes for a PurchaseOrder."""
_TAG = 'purchase_order'

# Order status codes
PENDING = 10 # Order is pending (not yet placed)
Expand Down Expand Up @@ -169,6 +51,7 @@ class PurchaseOrderStatus(StatusCode):

class SalesOrderStatus(StatusCode):
"""Defines a set of status codes for a SalesOrder."""
_TAG = 'sales_order'

PENDING = 10 # Order is pending
IN_PROGRESS = 15 # Order has been issued, and is in progress
Expand Down Expand Up @@ -209,6 +92,7 @@ class SalesOrderStatus(StatusCode):

class StockStatus(StatusCode):
"""Status codes for Stock."""
_TAG = 'stock'

OK = 10 # Item is OK
ATTENTION = 50 # Item requires attention
Expand Down Expand Up @@ -250,6 +134,7 @@ class StockStatus(StatusCode):

class StockHistoryCode(StatusCode):
"""Status codes for StockHistory."""
_TAG = 'stock_history'

LEGACY = 0

Expand Down Expand Up @@ -350,6 +235,7 @@ class StockHistoryCode(StatusCode):

class BuildStatus(StatusCode):
"""Build status codes."""
_TAG = 'build'

PENDING = 10 # Build is pending / active
PRODUCTION = 20 # BuildOrder is in production
Expand Down Expand Up @@ -378,6 +264,7 @@ class BuildStatus(StatusCode):

class ReturnOrderStatus(StatusCode):
"""Defines a set of status codes for a ReturnOrder"""
_TAG = 'return_order'

# Order is pending, waiting for receipt of items
PENDING = 10
Expand Down Expand Up @@ -410,6 +297,7 @@ class ReturnOrderStatus(StatusCode):

class ReturnOrderLineStatus(StatusCode):
"""Defines a set of status codes for a ReturnOrderLineItem"""
_TAG = 'return_order_line'

PENDING = 10

Expand Down
3 changes: 2 additions & 1 deletion InvenTree/build/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
from django_filters.rest_framework import DjangoFilterBackend
from django_filters import rest_framework as rest_filters

from InvenTree.api import AttachmentMixin, APIDownloadMixin, ListCreateDestroyAPIView, MetadataView, StatusView
from InvenTree.api import AttachmentMixin, APIDownloadMixin, ListCreateDestroyAPIView, MetadataView
from generic.states import StatusView
from InvenTree.helpers import str2bool, isNull, DownloadFile
from InvenTree.status_codes import BuildStatus
from InvenTree.mixins import CreateAPI, RetrieveUpdateDestroyAPI, ListCreateAPI
Expand Down
6 changes: 3 additions & 3 deletions InvenTree/build/templates/build/build_base.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

{% load static %}
{% load i18n %}
{% load status_codes %}
{% load generic %}
{% load inventree_extras %}

{% block page_title %}
Expand Down Expand Up @@ -150,7 +150,7 @@
<td><span class='fas fa-info'></span></td>
<td>{% trans "Status" %}</td>
<td>
{% build_status_label build.status %}
{% status_label 'build' build.status %}
</td>
</tr>
{% if build.target_date %}
Expand Down Expand Up @@ -217,7 +217,7 @@

{% block page_data %}
<h3>
{% build_status_label build.status large=True %}
{% status_label 'build' build.status large=True %}
{% if build.is_overdue %}
<span class='badge rounded-pill bg-danger'>{% trans "Overdue" %}</span>
{% endif %}
Expand Down
4 changes: 2 additions & 2 deletions InvenTree/build/templates/build/detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
{% load static %}
{% load i18n %}
{% load inventree_extras %}
{% load status_codes %}
{% load generic %}

{% block sidebar %}
{% include "build/sidebar.html" %}
Expand Down Expand Up @@ -60,7 +60,7 @@ <h4>{% trans "Build Details" %}</h4>
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Status" %}</td>
<td>{% build_status_label build.status %}</td>
<td>{% status_label 'build' build.status %}</td>
</tr>
<tr>
<td><span class='fas fa-check-circle'></span></td>
Expand Down
4 changes: 4 additions & 0 deletions InvenTree/generic/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""The generic module provides high-level functionality that is used in multiple places.

The generic module is split into sub-modules. Each sub-module provides a specific set of functionality. Each sub-module should be 100% tested within the sub-module.
"""
15 changes: 15 additions & 0 deletions InvenTree/generic/states/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
"""States are used to track the logical state of an object.

The logic value of a state is stored in the database as an integer. The logic value is used for buisness logic and should not be easily changed therefore.
There is a rendered state for each state value. The rendered state is used for display purposes and can be changed easily.

States can be extended with custom options for each InvenTree instance - those options are stored in the database and need to link back to state values.
"""

from .api import StatusView
from .states import StatusCode

__all__ = [
StatusView,
StatusCode,
]
Loading