kopia lustrzana https://github.com/jedie/PyInventory
commit
bf14d8e15a
|
@ -16,7 +16,7 @@ end_of_line = crlf
|
|||
insert_final_newline = false
|
||||
|
||||
[*.py]
|
||||
max_line_length = 120
|
||||
max_line_length = 100
|
||||
|
||||
[{Makefile,**.mk}]
|
||||
indent_style = tab
|
||||
|
|
|
@ -1180,43 +1180,6 @@ pytest = ">=5.4.0"
|
|||
docs = ["sphinx", "sphinx-rtd-theme"]
|
||||
testing = ["django", "django-configurations (>=2.0)"]
|
||||
|
||||
[[package]]
|
||||
name = "pytest-flake8"
|
||||
version = "1.1.1"
|
||||
description = "pytest plugin to check FLAKE8 requirements"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
flake8 = ">=4.0"
|
||||
pytest = ">=7.0"
|
||||
|
||||
[[package]]
|
||||
name = "pytest-isort"
|
||||
version = "3.0.0"
|
||||
description = "py.test plugin to check import ordering using isort"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6,<4"
|
||||
|
||||
[package.dependencies]
|
||||
importlib-metadata = {version = "*", markers = "python_version < \"3.8\""}
|
||||
isort = ">=4.0"
|
||||
pytest = ">=5.0"
|
||||
|
||||
[[package]]
|
||||
name = "pytest-parallel"
|
||||
version = "0.1.1"
|
||||
description = "a pytest plugin for parallel and concurrent testing"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[package.dependencies]
|
||||
pytest = ">=3.0.0"
|
||||
tblib = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pytest-playwright"
|
||||
version = "0.3.0"
|
||||
|
@ -1485,14 +1448,6 @@ xls = ["xlrd", "xlwt"]
|
|||
xlsx = ["openpyxl (>=2.6.0)"]
|
||||
yaml = ["pyyaml"]
|
||||
|
||||
[[package]]
|
||||
name = "tblib"
|
||||
version = "1.7.0"
|
||||
description = "Traceback serialization library."
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
|
||||
[[package]]
|
||||
name = "text-unidecode"
|
||||
version = "1.3"
|
||||
|
@ -1698,7 +1653,7 @@ psycopg2-source = ["psycopg2"]
|
|||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = ">=3.7,<4.0.0"
|
||||
content-hash = "6c6771b8abed20e040678f34bc5980e4263422e8dc8938fafef7cc938d868a4b"
|
||||
content-hash = "172f8d60024c52490428eae4c85589ff3d9ad3cf30df7f7b6b37f619960299f8"
|
||||
|
||||
[metadata.files]
|
||||
asgiref = [
|
||||
|
@ -2132,15 +2087,6 @@ pytest-django = [
|
|||
{file = "pytest-django-4.5.2.tar.gz", hash = "sha256:d9076f759bb7c36939dbdd5ae6633c18edfc2902d1a69fdbefd2426b970ce6c2"},
|
||||
{file = "pytest_django-4.5.2-py3-none-any.whl", hash = "sha256:c60834861933773109334fe5a53e83d1ef4828f2203a1d6a0fa9972f4f75ab3e"},
|
||||
]
|
||||
pytest-flake8 = [
|
||||
{file = "pytest-flake8-1.1.1.tar.gz", hash = "sha256:ba4f243de3cb4c2486ed9e70752c80dd4b636f7ccb27d4eba763c35ed0cd316e"},
|
||||
{file = "pytest_flake8-1.1.1-py2.py3-none-any.whl", hash = "sha256:e0661a786f8cbf976c185f706fdaf5d6df0b1667c3bcff8e823ba263618627e7"},
|
||||
]
|
||||
pytest-isort = [
|
||||
{file = "pytest-isort-3.0.0.tar.gz", hash = "sha256:4fe4b26ead2af776730ec23f5870d7421f35aace22a41c4e938586ef4d8787cb"},
|
||||
{file = "pytest_isort-3.0.0-py3-none-any.whl", hash = "sha256:2d96a25a135d6fd084ac36878e7d54f26f27c6987c2c65f0d12809bffade9cb9"},
|
||||
]
|
||||
pytest-parallel = []
|
||||
pytest-playwright = []
|
||||
pytest-randomly = []
|
||||
python-creole = []
|
||||
|
@ -2181,7 +2127,6 @@ tablib = [
|
|||
{file = "tablib-3.2.1-py3-none-any.whl", hash = "sha256:870d7e688f738531a14937a055e8bba404fbc388e77d4d500b2c904075d1019c"},
|
||||
{file = "tablib-3.2.1.tar.gz", hash = "sha256:a57f2770b8c225febec1cb1e65012a69cf30dd28be810e0ff98d024768c7d0f1"},
|
||||
]
|
||||
tblib = []
|
||||
text-unidecode = []
|
||||
texttable = []
|
||||
tokenize-rt = [
|
||||
|
|
|
@ -111,7 +111,7 @@ build-backend = "poetry.core.masonry.api"
|
|||
[tool.darker]
|
||||
src = ['.']
|
||||
revision = "origin/main..."
|
||||
line_length = 120
|
||||
line_length = 100
|
||||
verbose = true
|
||||
skip_string_normalization = true
|
||||
diff = false
|
||||
|
@ -125,14 +125,14 @@ log_level = "INFO"
|
|||
|
||||
|
||||
[tool.flynt]
|
||||
line_length = 120
|
||||
line_length = 100
|
||||
|
||||
|
||||
[tool.isort]
|
||||
# https://pycqa.github.io/isort/docs/configuration/config_files/#pyprojecttoml-preferred-format
|
||||
atomic=true
|
||||
profile='black'
|
||||
line_length=120
|
||||
line_length=100
|
||||
skip_glob=[".*", "*/htmlcov/*","*/migrations/*","*/volumes/*"]
|
||||
known_first_party=["inventory","inventory_project","inventory_tests"]
|
||||
lines_after_imports=2
|
||||
|
|
|
@ -43,6 +43,26 @@ class BaseUserAdmin(CompareVersionAdmin):
|
|||
|
||||
return qs
|
||||
|
||||
def get_list_filter(self, request):
|
||||
list_filter = self.list_filter
|
||||
|
||||
if request.user.is_superuser:
|
||||
# Superuser sees entries from all users -> Add "By user" filter
|
||||
list_filter = list(list_filter)
|
||||
list_filter.insert(0, 'user')
|
||||
|
||||
return list_filter
|
||||
|
||||
def get_list_display(self, request):
|
||||
list_display = self.list_display
|
||||
|
||||
if request.user.is_superuser:
|
||||
# Superuser sees entries from all users -> Display the user in change list
|
||||
list_display = list(list_display)
|
||||
list_display.insert(0, 'user')
|
||||
|
||||
return list_display
|
||||
|
||||
|
||||
class BaseImageModelInline(UserInlineMixin, SortableInlineAdminMixin, admin.TabularInline):
|
||||
def preview(self, instance):
|
||||
|
|
|
@ -65,6 +65,7 @@ class ItemModelAdmin(ImportExportMixin, BaseUserAdmin):
|
|||
def get_queryset(self, request):
|
||||
qs = super().get_queryset(request)
|
||||
qs = qs.prefetch_related(
|
||||
'location',
|
||||
'kind',
|
||||
'producer',
|
||||
)
|
||||
|
|
|
@ -24,5 +24,6 @@ class LocationModelAdmin(ImportExportMixin, BaseUserAdmin):
|
|||
return text
|
||||
|
||||
list_display = ('location', 'create_dt', 'update_dt')
|
||||
list_display_links = ('location',)
|
||||
list_filter = (LimitTreeDepthListFilter,)
|
||||
ordering = ('path_str',)
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
import itertools
|
||||
import logging
|
||||
from collections import Counter
|
||||
|
||||
from django.contrib.auth.hashers import UNUSABLE_PASSWORD_PREFIX
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
from inventory.models import ItemModel, LocationModel
|
||||
|
||||
|
||||
SEED_DATA_USER_PREFIX = 'seed-data-user-'
|
||||
|
||||
|
||||
class SetupLogger:
|
||||
def __init__(self, level):
|
||||
self.level = level
|
||||
|
||||
def __enter__(self):
|
||||
self.old_level = logging.root.manager.disable
|
||||
logging.disable(self.level)
|
||||
|
||||
def __exit__(self, exit_type, exit_value, exit_traceback):
|
||||
logging.disable(self.old_level)
|
||||
|
||||
|
||||
def iter_location_chain(user, location_count):
|
||||
room_no = itertools.count(start=1)
|
||||
cupboard_no = itertools.count(start=1)
|
||||
drawer_no = itertools.count(start=1)
|
||||
|
||||
location_no = 0
|
||||
while True:
|
||||
room = LocationModel.objects.create(user=user, name=f'Room {next(room_no)}')
|
||||
room.full_clean()
|
||||
for _ in range(2):
|
||||
cupboard = LocationModel.objects.create(
|
||||
user=user, name=f'Cupboard {next(cupboard_no)}', parent=room
|
||||
)
|
||||
cupboard.full_clean()
|
||||
for _ in range(2):
|
||||
drawer = LocationModel.objects.create(
|
||||
user=user, name=f'Drawer {next(drawer_no)}', parent=cupboard
|
||||
)
|
||||
drawer.full_clean()
|
||||
yield drawer
|
||||
location_no += 1
|
||||
if location_no >= location_count:
|
||||
return
|
||||
|
||||
|
||||
class ItemCreator:
|
||||
def __init__(self):
|
||||
self.equipment_no = itertools.count(start=1)
|
||||
self.item_no = itertools.count(start=1)
|
||||
self.part_no = itertools.count(start=1)
|
||||
|
||||
self.part_per_location = Counter()
|
||||
|
||||
def create_items(self, user, location, item_count):
|
||||
assert user
|
||||
assert location
|
||||
while True:
|
||||
equipment = ItemModel.objects.create(
|
||||
user=user,
|
||||
location=location,
|
||||
name=f'Equipment {next(self.equipment_no):03}',
|
||||
)
|
||||
equipment.full_clean()
|
||||
yield equipment
|
||||
|
||||
while True:
|
||||
item = ItemModel.objects.create(
|
||||
user=user,
|
||||
location=location,
|
||||
name=f'Item {next(self.item_no):03}',
|
||||
parent=equipment,
|
||||
)
|
||||
item.full_clean()
|
||||
yield item
|
||||
|
||||
while True:
|
||||
part = ItemModel.objects.create(
|
||||
user=user,
|
||||
location=location,
|
||||
name=f'Part {next(self.part_no):03}',
|
||||
parent=item,
|
||||
)
|
||||
part.full_clean()
|
||||
yield part
|
||||
self.part_per_location[location] += 1
|
||||
if self.part_per_location[location] >= item_count:
|
||||
return
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Fill database with example data'
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
'--user-count', type=int, default=3, choices=range(1, 10), help='User count'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--location-count', type=int, default=3, choices=range(1, 20), help='Location count'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--item-count', type=int, default=4, choices=range(1, 40), help='Item count'
|
||||
)
|
||||
|
||||
def handle(self, **options):
|
||||
self.stdout.write(self.help)
|
||||
|
||||
user_count = options['user_count']
|
||||
location_count = options['location_count']
|
||||
item_count = options['item_count']
|
||||
|
||||
verbosity = options['verbosity']
|
||||
if verbosity > 2:
|
||||
log_level = logging.DEBUG
|
||||
else:
|
||||
log_level = logging.WARNING
|
||||
|
||||
with SetupLogger(level=log_level):
|
||||
existing_users = User.objects.filter(username__startswith=SEED_DATA_USER_PREFIX)
|
||||
for user in existing_users:
|
||||
self.stdout.write(f'Clean data from user {user}...')
|
||||
info = user.delete()
|
||||
self.stdout.write(f'done: {info}')
|
||||
|
||||
item_creator = ItemCreator()
|
||||
|
||||
for user_no in range(1, user_count + 1):
|
||||
self.stdout.write('_' * 100)
|
||||
user = User.objects.create_user(
|
||||
username=f'{SEED_DATA_USER_PREFIX}{user_no}',
|
||||
email=f'{SEED_DATA_USER_PREFIX}{user_no}@test.tld',
|
||||
password=f'{UNUSABLE_PASSWORD_PREFIX} no password',
|
||||
)
|
||||
self.stdout.write(f'Create seed data for user {user}')
|
||||
|
||||
for location in iter_location_chain(user, location_count):
|
||||
for item in item_creator.create_items(user, location, item_count):
|
||||
self.stdout.write(f'{location} | {item}')
|
||||
|
||||
self.stdout.write('\nSeed data created.')
|
|
@ -0,0 +1,51 @@
|
|||
import inspect
|
||||
import io
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.core import management
|
||||
from django.test import TestCase
|
||||
|
||||
from inventory.management.commands import seed_data
|
||||
from inventory.models import ItemModel, LocationModel
|
||||
|
||||
|
||||
class ManagementCommandTestCase(TestCase):
|
||||
def test_seed_data_command(self):
|
||||
output = io.StringIO()
|
||||
|
||||
management.call_command(
|
||||
seed_data.Command(), user_count=2, location_count=2, item_count=2, stdout=output
|
||||
)
|
||||
assert User.objects.count() == 2
|
||||
assert LocationModel.objects.count() == 8
|
||||
assert ItemModel.objects.count() == 16
|
||||
|
||||
output = output.getvalue()
|
||||
reference = inspect.cleandoc(
|
||||
'''
|
||||
Fill database with example data
|
||||
____________________________________________________________________________________________________
|
||||
Create seed data for user seed-data-user-1
|
||||
Room 1 › Cupboard 1 › Drawer 1 | Equipment 001
|
||||
Room 1 › Cupboard 1 › Drawer 1 | Equipment 001 › Item 001
|
||||
Room 1 › Cupboard 1 › Drawer 1 | Equipment 001 › Item 001 › Part 001
|
||||
Room 1 › Cupboard 1 › Drawer 1 | Equipment 001 › Item 001 › Part 002
|
||||
Room 1 › Cupboard 1 › Drawer 2 | Equipment 002
|
||||
Room 1 › Cupboard 1 › Drawer 2 | Equipment 002 › Item 002
|
||||
Room 1 › Cupboard 1 › Drawer 2 | Equipment 002 › Item 002 › Part 003
|
||||
Room 1 › Cupboard 1 › Drawer 2 | Equipment 002 › Item 002 › Part 004
|
||||
____________________________________________________________________________________________________
|
||||
Create seed data for user seed-data-user-2
|
||||
Room 1 › Cupboard 1 › Drawer 1 | Equipment 003
|
||||
Room 1 › Cupboard 1 › Drawer 1 | Equipment 003 › Item 003
|
||||
Room 1 › Cupboard 1 › Drawer 1 | Equipment 003 › Item 003 › Part 005
|
||||
Room 1 › Cupboard 1 › Drawer 1 | Equipment 003 › Item 003 › Part 006
|
||||
Room 1 › Cupboard 1 › Drawer 2 | Equipment 004
|
||||
Room 1 › Cupboard 1 › Drawer 2 | Equipment 004 › Item 004
|
||||
Room 1 › Cupboard 1 › Drawer 2 | Equipment 004 › Item 004 › Part 007
|
||||
Room 1 › Cupboard 1 › Drawer 2 | Equipment 004 › Item 004 › Part 008
|
||||
|
||||
Seed data created.
|
||||
'''
|
||||
)
|
||||
assert output.strip() == reference.strip()
|
|
@ -228,6 +228,13 @@ class PyInventoryCommandSet(DevShellBaseCommandSet):
|
|||
},
|
||||
)
|
||||
|
||||
def do_seed_data(self, statement: cmd2.Statement):
|
||||
"""
|
||||
Fill database with example data
|
||||
"""
|
||||
args = ['seed_data', *statement.arg_list]
|
||||
call_manage_py(*args, cwd=PACKAGE_ROOT)
|
||||
|
||||
|
||||
class DevShellCommandSet(OriginDevShellCommandSet):
|
||||
pass
|
||||
|
|
Ładowanie…
Reference in New Issue