PyInventory 0.19.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- PyInventory-0.19.0.dist-info/AUTHORS +14 -0
- PyInventory-0.19.0.dist-info/LICENSE +674 -0
- PyInventory-0.19.0.dist-info/METADATA +347 -0
- PyInventory-0.19.0.dist-info/RECORD +101 -0
- PyInventory-0.19.0.dist-info/WHEEL +5 -0
- PyInventory-0.19.0.dist-info/entry_points.txt +2 -0
- PyInventory-0.19.0.dist-info/top_level.txt +2 -0
- inventory/__init__.py +7 -0
- inventory/admin/__init__.py +3 -0
- inventory/admin/base.py +104 -0
- inventory/admin/item.py +169 -0
- inventory/admin/location.py +78 -0
- inventory/admin/memo.py +76 -0
- inventory/admin/tagulous_fix.py +45 -0
- inventory/apps.py +18 -0
- inventory/ckeditor_upload.py +15 -0
- inventory/context_processors.py +5 -0
- inventory/forms.py +36 -0
- inventory/locale/ca/LC_MESSAGES/django.mo +0 -0
- inventory/locale/ca/LC_MESSAGES/django.po +297 -0
- inventory/locale/de/LC_MESSAGES/django.mo +0 -0
- inventory/locale/de/LC_MESSAGES/django.po +294 -0
- inventory/locale/en/LC_MESSAGES/django.mo +0 -0
- inventory/locale/en/LC_MESSAGES/django.po +294 -0
- inventory/locale/es/LC_MESSAGES/django.mo +0 -0
- inventory/locale/es/LC_MESSAGES/django.po +297 -0
- inventory/management/__init__.py +0 -0
- inventory/management/commands/__init__.py +0 -0
- inventory/management/commands/seed_data.py +135 -0
- inventory/management/commands/tree.py +62 -0
- inventory/middlewares.py +21 -0
- inventory/migrations/0001_initial.py +596 -0
- inventory/migrations/0002_auto_20201017_2211.py +87 -0
- inventory/migrations/0003_auto_20201024_1830.py +23 -0
- inventory/migrations/0004_item_user_images.py +129 -0
- inventory/migrations/0005_serve_uploads_by_django_tools.py +77 -0
- inventory/migrations/0006_refactor_image_model.py +46 -0
- inventory/migrations/0007_add_file_attachment.py +128 -0
- inventory/migrations/0008_last_check_datetime.py +23 -0
- inventory/migrations/0009_add_memo.py +517 -0
- inventory/migrations/0010_version_protect_models.py +37 -0
- inventory/migrations/0011_parent_tree1.py +97 -0
- inventory/migrations/0012_parent_tree2.py +20 -0
- inventory/migrations/0013_alter_itemmodel_location.py +26 -0
- inventory/migrations/__init__.py +0 -0
- inventory/models/__init__.py +3 -0
- inventory/models/base.py +239 -0
- inventory/models/item.py +228 -0
- inventory/models/links.py +104 -0
- inventory/models/location.py +24 -0
- inventory/models/memo.py +109 -0
- inventory/parent_tree.py +71 -0
- inventory/permissions.py +60 -0
- inventory/request_dict.py +16 -0
- inventory/signals.py +15 -0
- inventory/string_utils.py +15 -0
- inventory/templates/admin/item/related_items.html +18 -0
- inventory/templates/admin/location/items.html +18 -0
- inventory/tests/__init__.py +0 -0
- inventory/tests/fixtures/__init__.py +0 -0
- inventory/tests/fixtures/users.py +11 -0
- inventory/tests/test_admin_location.py +34 -0
- inventory/tests/test_admin_location_empty_change_list_1.snapshot.html +84 -0
- inventory/tests/test_item_images.py +76 -0
- inventory/tests/test_link_model.py +72 -0
- inventory/tests/test_management_command_seed_data.py +49 -0
- inventory/tests/test_management_command_tree.py +27 -0
- inventory/tests/test_parent_tree.py +40 -0
- inventory/tests/test_parent_tree_model.py +139 -0
- inventory_project/__init__.py +12 -0
- inventory_project/__main__.py +17 -0
- inventory_project/manage.py +41 -0
- inventory_project/middlewares.py +23 -0
- inventory_project/publish.py +21 -0
- inventory_project/settings/__init__.py +0 -0
- inventory_project/settings/local.py +74 -0
- inventory_project/settings/prod.py +393 -0
- inventory_project/settings/tests.py +45 -0
- inventory_project/templates/admin/base_site.html +22 -0
- inventory_project/templates/admin/login.html +32 -0
- inventory_project/tests/__init__.py +0 -0
- inventory_project/tests/fixtures.py +40 -0
- inventory_project/tests/mocks.py +15 -0
- inventory_project/tests/playwright_utils.py +22 -0
- inventory_project/tests/test_admin.py +15 -0
- inventory_project/tests/test_admin_item.py +240 -0
- inventory_project/tests/test_admin_item_auto_group_items_1.snapshot.html +349 -0
- inventory_project/tests/test_admin_item_auto_group_items_2.snapshot.html +232 -0
- inventory_project/tests/test_admin_item_login_1.snapshot.html +40 -0
- inventory_project/tests/test_admin_item_normal_user_create_minimal_item_1.snapshot.html +637 -0
- inventory_project/tests/test_admin_item_normal_user_create_minimal_item_2.snapshot.html +930 -0
- inventory_project/tests/test_admin_memo.py +153 -0
- inventory_project/tests/test_admin_memo_normal_user_create_minimal_item_1.snapshot.html +365 -0
- inventory_project/tests/test_command_shell_help_django4.2.3.snapshot.txt +60 -0
- inventory_project/tests/test_inventory_commands.py +26 -0
- inventory_project/tests/test_migrations.py +22 -0
- inventory_project/tests/test_models_item.py +24 -0
- inventory_project/tests/test_playwright_admin.py +157 -0
- inventory_project/tests/test_project_setup.py +102 -0
- inventory_project/urls.py +21 -0
- inventory_project/wsgi.py +9 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import itertools
|
|
2
|
+
import logging
|
|
3
|
+
from collections import Counter
|
|
4
|
+
|
|
5
|
+
from django.contrib.auth.hashers import UNUSABLE_PASSWORD_PREFIX
|
|
6
|
+
from django.contrib.auth.models import User
|
|
7
|
+
from django.core.management.base import BaseCommand
|
|
8
|
+
|
|
9
|
+
from inventory.models import ItemModel, LocationModel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
SEED_DATA_USER_PREFIX = 'seed-data-user-'
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SetupLogger:
|
|
16
|
+
def __init__(self, level):
|
|
17
|
+
self.level = level
|
|
18
|
+
|
|
19
|
+
def __enter__(self):
|
|
20
|
+
self.old_level = logging.root.manager.disable
|
|
21
|
+
logging.disable(self.level)
|
|
22
|
+
|
|
23
|
+
def __exit__(self, exit_type, exit_value, exit_traceback):
|
|
24
|
+
logging.disable(self.old_level)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def iter_location_chain(user, location_count):
|
|
28
|
+
room_no = itertools.count(start=1)
|
|
29
|
+
cupboard_no = itertools.count(start=1)
|
|
30
|
+
drawer_no = itertools.count(start=1)
|
|
31
|
+
|
|
32
|
+
location_no = 0
|
|
33
|
+
while True:
|
|
34
|
+
room = LocationModel.objects.create(user=user, name=f'Room {next(room_no)}')
|
|
35
|
+
room.full_clean()
|
|
36
|
+
for _ in range(2):
|
|
37
|
+
cupboard = LocationModel.objects.create(user=user, name=f'Cupboard {next(cupboard_no)}', parent=room)
|
|
38
|
+
cupboard.full_clean()
|
|
39
|
+
for _ in range(2):
|
|
40
|
+
drawer = LocationModel.objects.create(user=user, name=f'Drawer {next(drawer_no)}', parent=cupboard)
|
|
41
|
+
drawer.full_clean()
|
|
42
|
+
yield drawer
|
|
43
|
+
location_no += 1
|
|
44
|
+
if location_no >= location_count:
|
|
45
|
+
return
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ItemCreator:
|
|
49
|
+
def __init__(self):
|
|
50
|
+
self.equipment_no = itertools.count(start=1)
|
|
51
|
+
self.item_no = itertools.count(start=1)
|
|
52
|
+
self.part_no = itertools.count(start=1)
|
|
53
|
+
|
|
54
|
+
self.part_per_location = Counter()
|
|
55
|
+
|
|
56
|
+
def create_items(self, user, location, item_count):
|
|
57
|
+
assert user
|
|
58
|
+
assert location
|
|
59
|
+
while True:
|
|
60
|
+
equipment = ItemModel.objects.create(
|
|
61
|
+
user=user,
|
|
62
|
+
location=location,
|
|
63
|
+
name=f'Equipment {next(self.equipment_no):03}',
|
|
64
|
+
)
|
|
65
|
+
equipment.full_clean()
|
|
66
|
+
yield equipment
|
|
67
|
+
|
|
68
|
+
while True:
|
|
69
|
+
item = ItemModel.objects.create(
|
|
70
|
+
user=user,
|
|
71
|
+
location=location,
|
|
72
|
+
name=f'Item {next(self.item_no):03}',
|
|
73
|
+
parent=equipment,
|
|
74
|
+
)
|
|
75
|
+
item.full_clean()
|
|
76
|
+
yield item
|
|
77
|
+
|
|
78
|
+
while True:
|
|
79
|
+
part = ItemModel.objects.create(
|
|
80
|
+
user=user,
|
|
81
|
+
location=location,
|
|
82
|
+
name=f'Part {next(self.part_no):03}',
|
|
83
|
+
parent=item,
|
|
84
|
+
)
|
|
85
|
+
part.full_clean()
|
|
86
|
+
yield part
|
|
87
|
+
self.part_per_location[location] += 1
|
|
88
|
+
if self.part_per_location[location] >= item_count:
|
|
89
|
+
return
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class Command(BaseCommand):
|
|
93
|
+
help = 'Fill database with example data'
|
|
94
|
+
|
|
95
|
+
def add_arguments(self, parser):
|
|
96
|
+
parser.add_argument('--user-count', type=int, default=3, choices=range(1, 10), help='User count')
|
|
97
|
+
parser.add_argument('--location-count', type=int, default=3, choices=range(1, 20), help='Location count')
|
|
98
|
+
parser.add_argument('--item-count', type=int, default=4, choices=range(1, 40), help='Item count')
|
|
99
|
+
|
|
100
|
+
def handle(self, **options):
|
|
101
|
+
self.stdout.write(self.help)
|
|
102
|
+
|
|
103
|
+
user_count = options['user_count']
|
|
104
|
+
location_count = options['location_count']
|
|
105
|
+
item_count = options['item_count']
|
|
106
|
+
|
|
107
|
+
verbosity = options['verbosity']
|
|
108
|
+
if verbosity > 2:
|
|
109
|
+
log_level = logging.DEBUG
|
|
110
|
+
else:
|
|
111
|
+
log_level = logging.WARNING
|
|
112
|
+
|
|
113
|
+
with SetupLogger(level=log_level):
|
|
114
|
+
existing_users = User.objects.filter(username__startswith=SEED_DATA_USER_PREFIX)
|
|
115
|
+
for user in existing_users:
|
|
116
|
+
self.stdout.write(f'Clean data from user {user}...')
|
|
117
|
+
info = user.delete()
|
|
118
|
+
self.stdout.write(f'done: {info}')
|
|
119
|
+
|
|
120
|
+
item_creator = ItemCreator()
|
|
121
|
+
|
|
122
|
+
for user_no in range(1, user_count + 1):
|
|
123
|
+
self.stdout.write('_' * 100)
|
|
124
|
+
user = User.objects.create_user(
|
|
125
|
+
username=f'{SEED_DATA_USER_PREFIX}{user_no}',
|
|
126
|
+
email=f'{SEED_DATA_USER_PREFIX}{user_no}@test.tld',
|
|
127
|
+
password=f'{UNUSABLE_PASSWORD_PREFIX} no password',
|
|
128
|
+
)
|
|
129
|
+
self.stdout.write(f'Create seed data for user {user}')
|
|
130
|
+
|
|
131
|
+
for location in iter_location_chain(user, location_count):
|
|
132
|
+
for item in item_creator.create_items(user, location, item_count):
|
|
133
|
+
self.stdout.write(f'{location} | {item}')
|
|
134
|
+
|
|
135
|
+
self.stdout.write('\nSeed data created.')
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from argparse import OPTIONAL
|
|
3
|
+
|
|
4
|
+
from django.apps import apps
|
|
5
|
+
from django.core.management.base import BaseCommand
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class PrintDuration:
|
|
9
|
+
def __init__(self, stdout):
|
|
10
|
+
self.stdout = stdout
|
|
11
|
+
|
|
12
|
+
def __enter__(self):
|
|
13
|
+
self.start_time = time.monotonic()
|
|
14
|
+
|
|
15
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
16
|
+
duration = (time.monotonic() - self.start_time) * 1000
|
|
17
|
+
self.stdout.write(f'(Done in: {duration:.1f}ms)')
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Command(BaseCommand):
|
|
21
|
+
help = 'Repair tree information'
|
|
22
|
+
|
|
23
|
+
def add_arguments(self, parser):
|
|
24
|
+
parser.add_argument(
|
|
25
|
+
'model_name',
|
|
26
|
+
metavar='model_name',
|
|
27
|
+
nargs=OPTIONAL,
|
|
28
|
+
default='itemmodel',
|
|
29
|
+
choices=['itemmodel', 'locationmodel'],
|
|
30
|
+
help='Model Name (default: "%(default)s")',
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
def handle(self, *args, **options):
|
|
34
|
+
self.stdout.write()
|
|
35
|
+
self.stdout.write('=' * 100)
|
|
36
|
+
self.stdout.write(self.help)
|
|
37
|
+
self.stdout.write('-' * 100)
|
|
38
|
+
|
|
39
|
+
model_name = options['model_name']
|
|
40
|
+
ModelClass = apps.get_model(app_label='inventory', model_name=model_name)
|
|
41
|
+
|
|
42
|
+
self.print_info(ModelClass, text='Old information about model:')
|
|
43
|
+
|
|
44
|
+
self.stdout.write('_' * 100)
|
|
45
|
+
self.stdout.write(f'Clean tree information on model: {ModelClass._meta.verbose_name!r}')
|
|
46
|
+
with PrintDuration(self.stdout):
|
|
47
|
+
ModelClass.objects.update(path=None, path_str=None, level=None)
|
|
48
|
+
|
|
49
|
+
self.stdout.write('_' * 100)
|
|
50
|
+
self.stdout.write(f'Repair tree model: {ModelClass._meta.verbose_name!r}')
|
|
51
|
+
with PrintDuration(self.stdout):
|
|
52
|
+
ModelClass.tree_objects.update_tree_info()
|
|
53
|
+
|
|
54
|
+
self.print_info(ModelClass, text='New information about model:')
|
|
55
|
+
|
|
56
|
+
def print_info(self, ModelClass, text):
|
|
57
|
+
self.stdout.write('_' * 100)
|
|
58
|
+
self.stdout.write(f'{text} {ModelClass._meta.verbose_name!r}')
|
|
59
|
+
with PrintDuration(self.stdout):
|
|
60
|
+
data = ModelClass.objects.values('level', 'path_str', 'path', 'name')
|
|
61
|
+
for entry in data:
|
|
62
|
+
self.stdout.write(repr(entry))
|
inventory/middlewares.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from inventory.request_dict import clear_request_dict, get_request_dict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class RequestDictMiddleware:
|
|
5
|
+
"""
|
|
6
|
+
Make the "current user" information available everywhere via threading.local()
|
|
7
|
+
Access e.g.:
|
|
8
|
+
user = get_request_dict()['user']
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, get_response):
|
|
12
|
+
self.get_response = get_response
|
|
13
|
+
|
|
14
|
+
def __call__(self, request):
|
|
15
|
+
get_request_dict().update(user=request.user)
|
|
16
|
+
|
|
17
|
+
response = self.get_response(request)
|
|
18
|
+
|
|
19
|
+
clear_request_dict()
|
|
20
|
+
|
|
21
|
+
return response
|