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.
Files changed (101) hide show
  1. PyInventory-0.19.0.dist-info/AUTHORS +14 -0
  2. PyInventory-0.19.0.dist-info/LICENSE +674 -0
  3. PyInventory-0.19.0.dist-info/METADATA +347 -0
  4. PyInventory-0.19.0.dist-info/RECORD +101 -0
  5. PyInventory-0.19.0.dist-info/WHEEL +5 -0
  6. PyInventory-0.19.0.dist-info/entry_points.txt +2 -0
  7. PyInventory-0.19.0.dist-info/top_level.txt +2 -0
  8. inventory/__init__.py +7 -0
  9. inventory/admin/__init__.py +3 -0
  10. inventory/admin/base.py +104 -0
  11. inventory/admin/item.py +169 -0
  12. inventory/admin/location.py +78 -0
  13. inventory/admin/memo.py +76 -0
  14. inventory/admin/tagulous_fix.py +45 -0
  15. inventory/apps.py +18 -0
  16. inventory/ckeditor_upload.py +15 -0
  17. inventory/context_processors.py +5 -0
  18. inventory/forms.py +36 -0
  19. inventory/locale/ca/LC_MESSAGES/django.mo +0 -0
  20. inventory/locale/ca/LC_MESSAGES/django.po +297 -0
  21. inventory/locale/de/LC_MESSAGES/django.mo +0 -0
  22. inventory/locale/de/LC_MESSAGES/django.po +294 -0
  23. inventory/locale/en/LC_MESSAGES/django.mo +0 -0
  24. inventory/locale/en/LC_MESSAGES/django.po +294 -0
  25. inventory/locale/es/LC_MESSAGES/django.mo +0 -0
  26. inventory/locale/es/LC_MESSAGES/django.po +297 -0
  27. inventory/management/__init__.py +0 -0
  28. inventory/management/commands/__init__.py +0 -0
  29. inventory/management/commands/seed_data.py +135 -0
  30. inventory/management/commands/tree.py +62 -0
  31. inventory/middlewares.py +21 -0
  32. inventory/migrations/0001_initial.py +596 -0
  33. inventory/migrations/0002_auto_20201017_2211.py +87 -0
  34. inventory/migrations/0003_auto_20201024_1830.py +23 -0
  35. inventory/migrations/0004_item_user_images.py +129 -0
  36. inventory/migrations/0005_serve_uploads_by_django_tools.py +77 -0
  37. inventory/migrations/0006_refactor_image_model.py +46 -0
  38. inventory/migrations/0007_add_file_attachment.py +128 -0
  39. inventory/migrations/0008_last_check_datetime.py +23 -0
  40. inventory/migrations/0009_add_memo.py +517 -0
  41. inventory/migrations/0010_version_protect_models.py +37 -0
  42. inventory/migrations/0011_parent_tree1.py +97 -0
  43. inventory/migrations/0012_parent_tree2.py +20 -0
  44. inventory/migrations/0013_alter_itemmodel_location.py +26 -0
  45. inventory/migrations/__init__.py +0 -0
  46. inventory/models/__init__.py +3 -0
  47. inventory/models/base.py +239 -0
  48. inventory/models/item.py +228 -0
  49. inventory/models/links.py +104 -0
  50. inventory/models/location.py +24 -0
  51. inventory/models/memo.py +109 -0
  52. inventory/parent_tree.py +71 -0
  53. inventory/permissions.py +60 -0
  54. inventory/request_dict.py +16 -0
  55. inventory/signals.py +15 -0
  56. inventory/string_utils.py +15 -0
  57. inventory/templates/admin/item/related_items.html +18 -0
  58. inventory/templates/admin/location/items.html +18 -0
  59. inventory/tests/__init__.py +0 -0
  60. inventory/tests/fixtures/__init__.py +0 -0
  61. inventory/tests/fixtures/users.py +11 -0
  62. inventory/tests/test_admin_location.py +34 -0
  63. inventory/tests/test_admin_location_empty_change_list_1.snapshot.html +84 -0
  64. inventory/tests/test_item_images.py +76 -0
  65. inventory/tests/test_link_model.py +72 -0
  66. inventory/tests/test_management_command_seed_data.py +49 -0
  67. inventory/tests/test_management_command_tree.py +27 -0
  68. inventory/tests/test_parent_tree.py +40 -0
  69. inventory/tests/test_parent_tree_model.py +139 -0
  70. inventory_project/__init__.py +12 -0
  71. inventory_project/__main__.py +17 -0
  72. inventory_project/manage.py +41 -0
  73. inventory_project/middlewares.py +23 -0
  74. inventory_project/publish.py +21 -0
  75. inventory_project/settings/__init__.py +0 -0
  76. inventory_project/settings/local.py +74 -0
  77. inventory_project/settings/prod.py +393 -0
  78. inventory_project/settings/tests.py +45 -0
  79. inventory_project/templates/admin/base_site.html +22 -0
  80. inventory_project/templates/admin/login.html +32 -0
  81. inventory_project/tests/__init__.py +0 -0
  82. inventory_project/tests/fixtures.py +40 -0
  83. inventory_project/tests/mocks.py +15 -0
  84. inventory_project/tests/playwright_utils.py +22 -0
  85. inventory_project/tests/test_admin.py +15 -0
  86. inventory_project/tests/test_admin_item.py +240 -0
  87. inventory_project/tests/test_admin_item_auto_group_items_1.snapshot.html +349 -0
  88. inventory_project/tests/test_admin_item_auto_group_items_2.snapshot.html +232 -0
  89. inventory_project/tests/test_admin_item_login_1.snapshot.html +40 -0
  90. inventory_project/tests/test_admin_item_normal_user_create_minimal_item_1.snapshot.html +637 -0
  91. inventory_project/tests/test_admin_item_normal_user_create_minimal_item_2.snapshot.html +930 -0
  92. inventory_project/tests/test_admin_memo.py +153 -0
  93. inventory_project/tests/test_admin_memo_normal_user_create_minimal_item_1.snapshot.html +365 -0
  94. inventory_project/tests/test_command_shell_help_django4.2.3.snapshot.txt +60 -0
  95. inventory_project/tests/test_inventory_commands.py +26 -0
  96. inventory_project/tests/test_migrations.py +22 -0
  97. inventory_project/tests/test_models_item.py +24 -0
  98. inventory_project/tests/test_playwright_admin.py +157 -0
  99. inventory_project/tests/test_project_setup.py +102 -0
  100. inventory_project/urls.py +21 -0
  101. 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))
@@ -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