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,27 @@
1
+ import io
2
+
3
+ from django.core import management
4
+ from django.test import TestCase
5
+ from model_bakery import baker
6
+
7
+ from inventory.management.commands import tree
8
+ from inventory.models import ItemModel
9
+
10
+
11
+ class ManagementCommandTestCase(TestCase):
12
+ def test_tree_command(self):
13
+ baker.make(ItemModel, name='Foo Bar')
14
+ ItemModel.objects.update(path_str='OLD', path=['OLD'])
15
+
16
+ output = io.StringIO()
17
+
18
+ management.call_command(tree.Command(), stdout=output)
19
+
20
+ output = output.getvalue()
21
+ assert 'Repair tree information' in output
22
+
23
+ assert "Old information about model: 'Item'" in output
24
+ assert "{'level': 1, 'path_str': 'OLD', 'path': ['OLD'], 'name': 'Foo Bar'}" in output
25
+
26
+ assert "New information about model: 'Item'" in output
27
+ assert "{'level': 1, 'path_str': 'foobar', 'path': ['Foo Bar'], 'name': 'Foo Bar'}" in output
@@ -0,0 +1,40 @@
1
+ import random
2
+
3
+ from inventory.parent_tree import ValuesListTree
4
+
5
+
6
+ def test_values_list_tree():
7
+ values_list = [
8
+ (1, '1.', None),
9
+ (2, '1.1.', 1),
10
+ (3, '1.1.1', 2),
11
+ (4, '1.1.2', 2),
12
+ (5, '1.2.', 1),
13
+ (6, '2.', None),
14
+ ]
15
+ random.shuffle(values_list)
16
+ values = [{'pk': entry[0], 'name': entry[1], 'parent__pk': entry[2], 'path': ''} for entry in values_list]
17
+ tree = ValuesListTree(values)
18
+
19
+ tree_path = tree.get_tree_path()
20
+ assert tree_path == [
21
+ '1.',
22
+ '1. / 1.1.',
23
+ '1. / 1.1. / 1.1.1',
24
+ '1. / 1.1. / 1.1.2',
25
+ '1. / 1.2.',
26
+ '2.',
27
+ ]
28
+ update_path_info = tree.get_update_path_info()
29
+ assert update_path_info == {
30
+ 1: ['1.'],
31
+ 2: ['1.', '1.1.'],
32
+ 3: ['1.', '1.1.', '1.1.1'],
33
+ 4: ['1.', '1.1.', '1.1.2'],
34
+ 5: ['1.', '1.2.'],
35
+ 6: ['2.'],
36
+ }
37
+
38
+ node_three = tree.nodes[2]
39
+ assert str(node_three) == 'pk:3 name:"1.1.1" path:"1. / 1.1. / 1.1.1"'
40
+ assert repr(node_three) == '<TreeNode pk:3 name:"1.1.1" path:"1. / 1.1. / 1.1.1">'
@@ -0,0 +1,139 @@
1
+ from bx_django_utils.test_utils.assert_queries import AssertQueries
2
+ from django.db.models import QuerySet
3
+ from django.test import TestCase
4
+
5
+ from inventory.admin import ItemModelAdmin, LocationModelAdmin
6
+ from inventory.models import ItemModel, LocationModel
7
+ from inventory_project.tests.fixtures import get_normal_user
8
+
9
+
10
+ class TreeModelTests(TestCase):
11
+ @classmethod
12
+ def setUpTestData(cls):
13
+ cls.normaluser = get_normal_user()
14
+
15
+ def test_parent_tree_model(self):
16
+ for main_item_no in range(1, 3):
17
+ main_item = ItemModel.objects.create(
18
+ user=self.normaluser,
19
+ name=f'{main_item_no}.',
20
+ )
21
+ main_item.full_clean()
22
+
23
+ for sub_item_no in range(1, 3):
24
+ sub_item = ItemModel.objects.create(
25
+ parent=main_item,
26
+ user=self.normaluser,
27
+ name=f'{main_item_no}.{sub_item_no}.',
28
+ )
29
+ sub_item.full_clean()
30
+
31
+ for sub_sub_item_no in range(1, 3):
32
+ sub_sub_item = ItemModel.objects.create(
33
+ parent=sub_item,
34
+ user=self.normaluser,
35
+ name=f'{main_item_no}.{sub_item_no}.{sub_sub_item_no}.',
36
+ )
37
+ sub_sub_item.full_clean()
38
+
39
+ data = list(ItemModel.objects.values_list('level', 'path_str', 'name'))
40
+ assert data == [
41
+ (1, '1', '1.'),
42
+ (2, '1 0 11', '1.1.'),
43
+ (3, '1 0 11 0 111', '1.1.1.'),
44
+ (3, '1 0 11 0 112', '1.1.2.'),
45
+ (2, '1 0 12', '1.2.'),
46
+ (3, '1 0 12 0 121', '1.2.1.'),
47
+ (3, '1 0 12 0 122', '1.2.2.'),
48
+ (1, '2', '2.'),
49
+ (2, '2 0 21', '2.1.'),
50
+ (3, '2 0 21 0 211', '2.1.1.'),
51
+ (3, '2 0 21 0 212', '2.1.2.'),
52
+ (2, '2 0 22', '2.2.'),
53
+ (3, '2 0 22 0 221', '2.2.1.'),
54
+ (3, '2 0 22 0 222', '2.2.2.'),
55
+ ]
56
+
57
+ item_2_1 = ItemModel.objects.get(name='2.1.')
58
+
59
+ related_qs = ItemModel.tree_objects.related_objects(instance=item_2_1)
60
+ data = list(related_qs.values_list('name', flat=True))
61
+ assert data == ['2.', '2.1.', '2.1.1.', '2.1.2.', '2.2.', '2.2.1.', '2.2.2.']
62
+
63
+ item_2_1.name = 'NEW 2.1. Name'
64
+ with AssertQueries() as queries:
65
+ item_2_1.save()
66
+
67
+ data = list(ItemModel.objects.values_list('level', 'path_str', 'name'))
68
+ assert data == [
69
+ (1, '1', '1.'),
70
+ (2, '1 0 11', '1.1.'),
71
+ (3, '1 0 11 0 111', '1.1.1.'),
72
+ (3, '1 0 11 0 112', '1.1.2.'),
73
+ (2, '1 0 12', '1.2.'),
74
+ (3, '1 0 12 0 121', '1.2.1.'),
75
+ (3, '1 0 12 0 122', '1.2.2.'),
76
+ (1, '2', '2.'),
77
+ (2, '2 0 22', '2.2.'),
78
+ (3, '2 0 22 0 221', '2.2.1.'),
79
+ (3, '2 0 22 0 222', '2.2.2.'),
80
+ (2, '2 0 new21name', 'NEW 2.1. Name'),
81
+ (3, '2 0 new21name 0 211', '2.1.1.'),
82
+ (3, '2 0 new21name 0 212', '2.1.2.'),
83
+ ]
84
+
85
+ itemmodel_count = 1 # full_clean(): Check if parent exists
86
+ itemmodel_count += 1 # VersionProtectBaseModel: Check version
87
+ itemmodel_count += 1 # VersionProtectBaseModel: Save new version
88
+ itemmodel_count += 1 # Get info for tree update
89
+ itemmodel_count += 1 # Fetch the items to update
90
+ itemmodel_count += 1 # Bulk update save
91
+
92
+ queries.assert_queries(
93
+ table_counts={
94
+ 'inventory_itemmodel': itemmodel_count,
95
+ 'auth_user': 1, # full_clean(): Check if user exists
96
+ },
97
+ double_tables=False,
98
+ duplicated=True,
99
+ similar=True,
100
+ )
101
+
102
+ def test_related_objects(self):
103
+ item = ItemModel()
104
+ qs = ItemModel.tree_objects.related_objects(instance=item)
105
+ assert isinstance(qs, QuerySet)
106
+ assert qs.query.is_empty() is True
107
+
108
+ def test_parent_tree_model_ordering(self):
109
+ assert LocationModel._meta.ordering == ('path_str',)
110
+ assert LocationModelAdmin.ordering == ('path_str',)
111
+
112
+ assert ItemModel._meta.ordering == ('path_str',)
113
+ assert ItemModelAdmin.ordering == ('path_str',)
114
+
115
+ def create(name, parent=None):
116
+ instance = ItemModel.objects.create(user=self.normaluser, name=name, parent=parent)
117
+ instance.full_clean()
118
+ return instance
119
+
120
+ # Create a "Special" case for the correct ordering:
121
+ # 1. all "PC-1" entries
122
+ # 2. all "PC1640" entries
123
+ #
124
+ # The correct order depends on the seperator, here: " 0 "
125
+
126
+ pc1 = create(name='PC-1')
127
+ pc1640 = create(name='PC1640 SD')
128
+ create(name='FZ-502 Rev A 5.25″ Floppy', parent=pc1)
129
+ create(name='1,44MB / 3.5" Floppy FD-235HF- 3800-U', parent=pc1)
130
+ create(name='PC 1640ECD', parent=pc1640)
131
+
132
+ data = list(ItemModel.objects.values_list('level', 'path_str', 'name'))
133
+ assert data == [
134
+ (1, 'pc1', 'PC-1'),
135
+ (2, 'pc1 0 144mb35floppyfd235hf3800u', '1,44MB / 3.5" Floppy FD-235HF- 3800-U'),
136
+ (2, 'pc1 0 fz502reva525floppy', 'FZ-502 Rev A 5.25″ Floppy'),
137
+ (1, 'pc1640sd', 'PC1640 SD'),
138
+ (2, 'pc1640sd 0 pc1640ecd', 'PC 1640ECD'),
139
+ ]
@@ -0,0 +1,12 @@
1
+ from pathlib import Path
2
+
3
+ from bx_py_utils.path import assert_is_dir
4
+
5
+ import inventory
6
+
7
+
8
+ PACKAGE_ROOT = Path(inventory.__file__).parent.parent
9
+ assert_is_dir(PACKAGE_ROOT / 'inventory')
10
+
11
+
12
+ __version__ = inventory.__version__
@@ -0,0 +1,17 @@
1
+ """
2
+ Allow your-cool-package to be executable
3
+ through `python -m inventory`.
4
+ """
5
+ from manage_django_project.manage import execute_django_from_command_line
6
+
7
+
8
+ def main():
9
+ """
10
+ entrypoint installed via pyproject.toml and [project.scripts] section.
11
+ Must be set in ./manage.py and PROJECT_SHELL_SCRIPT
12
+ """
13
+ execute_django_from_command_line()
14
+
15
+
16
+ if __name__ == '__main__':
17
+ main()
@@ -0,0 +1,41 @@
1
+ import os
2
+ import sys
3
+
4
+ from django import __version__ as django_version
5
+
6
+ from inventory import __version__
7
+
8
+
9
+ def main(argv):
10
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'inventory_project.settings.local')
11
+
12
+ if '--version' not in argv:
13
+ print(f'PyInventory v{__version__} (Django v{django_version})', file=sys.stderr)
14
+ print(f'DJANGO_SETTINGS_MODULE={os.environ["DJANGO_SETTINGS_MODULE"]!r}', file=sys.stderr)
15
+
16
+ try:
17
+ from django.core.management import execute_from_command_line
18
+ except ImportError as exc:
19
+ raise ImportError(
20
+ 'Couldn\'t import Django. Are you sure it\'s installed and '
21
+ 'available on your PYTHONPATH environment variable? Did you '
22
+ 'forget to activate a virtual environment?'
23
+ ) from exc
24
+ try:
25
+ execute_from_command_line(argv)
26
+ except Exception as err:
27
+ from bx_py_utils.error_handling import print_exc_plus
28
+
29
+ print_exc_plus(err)
30
+ raise
31
+
32
+
33
+ def start_test_server():
34
+ """
35
+ Entrypoint for "[tool.poetry.scripts]" script started by devshell command.
36
+ """
37
+ main(argv=[__file__, "run_testserver"] + sys.argv[1:])
38
+
39
+
40
+ if __name__ == '__main__':
41
+ main(argv=sys.argv)
@@ -0,0 +1,23 @@
1
+ import logging
2
+ from pathlib import Path
3
+
4
+ from debug_toolbar.middleware import show_toolbar
5
+ from django.conf import settings
6
+
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ def djdt_show(request):
12
+ """
13
+ Determining whether the Django Debug Toolbar should show or not.
14
+ """
15
+ if not settings.DEBUG:
16
+ return False
17
+
18
+ if Path('/.dockerenv').exists():
19
+ # We run in a docker container
20
+ # skip the `request.META['REMOTE_ADDR'] in settings.INTERNAL_IPS` check.
21
+ return True
22
+
23
+ return show_toolbar(request)
@@ -0,0 +1,21 @@
1
+ from poetry_publish.publish import poetry_publish
2
+ from poetry_publish.utils.subprocess_utils import verbose_check_call
3
+
4
+ # https://github.com/jedie/PyInventory
5
+ import inventory
6
+ from inventory_project import PACKAGE_ROOT
7
+
8
+
9
+ def publish():
10
+ """
11
+ Publish to PyPi
12
+ Call this via:
13
+ $ poetry run publish
14
+ """
15
+ verbose_check_call('make', 'pytest') # don't publish if tests fail
16
+ verbose_check_call('make', 'fix-code-style') # don't publish if code style wrong
17
+
18
+ poetry_publish(
19
+ package_root=PACKAGE_ROOT,
20
+ version=inventory.__version__,
21
+ )
File without changes
@@ -0,0 +1,74 @@
1
+ # flake8: noqa: E405
2
+
3
+ """
4
+ Django settings for local development
5
+ """
6
+
7
+ import os as __os
8
+ import sys as __sys
9
+
10
+ from inventory_project.settings.prod import * # noqa
11
+
12
+
13
+ # SECURITY WARNING: don't run with debug turned on in production!
14
+ DEBUG = True
15
+
16
+
17
+ # Serve static/media files for local development:
18
+ SERVE_FILES = True
19
+
20
+
21
+ # Disable caches:
22
+ CACHES = {'default': {'BACKEND': 'django.core.cache.backends.dummy.DummyCache'}}
23
+
24
+ # Required for the debug toolbar to be displayed:
25
+ INTERNAL_IPS = ('127.0.0.1', '0.0.0.0', 'localhost')
26
+
27
+ ALLOWED_HOSTS = INTERNAL_IPS
28
+
29
+ DATABASES = {
30
+ 'default': {
31
+ 'ENGINE': 'django.db.backends.sqlite3',
32
+ 'NAME': str(BASE_PATH / 'inventory-database.sqlite3'),
33
+ # https://docs.djangoproject.com/en/dev/ref/databases/#database-is-locked-errors
34
+ 'timeout': 30,
35
+ }
36
+ }
37
+ print(f'Use Database: {DATABASES["default"]["NAME"]!r}', file=__sys.stderr)
38
+
39
+ # _____________________________________________________________________________
40
+
41
+ # Download map via geotiler in inventory.gpx_tools.gpxpy2map.generate_map
42
+ MAP_DOWNLOAD = True
43
+
44
+ if __os.environ.get('AUTOLOGIN') == '1':
45
+ # Auto login for dev. server:
46
+ MIDDLEWARE = MIDDLEWARE.copy()
47
+ MIDDLEWARE += ['django_tools.middlewares.local_auto_login.AlwaysLoggedInAsSuperUserMiddleware']
48
+
49
+ # _____________________________________________________________________________
50
+ # Manage Django Project
51
+
52
+ INSTALLED_APPS.append('manage_django_project')
53
+
54
+ # _____________________________________________________________________________
55
+ # Django-Debug-Toolbar
56
+
57
+
58
+ INSTALLED_APPS.append('debug_toolbar')
59
+ MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')
60
+
61
+ DEBUG_TOOLBAR_PATCH_SETTINGS = True
62
+ from debug_toolbar.settings import CONFIG_DEFAULTS as DEBUG_TOOLBAR_CONFIG # noqa
63
+
64
+
65
+ # Disable some more panels that will slow down the page:
66
+ DEBUG_TOOLBAR_CONFIG['DISABLE_PANELS'].add('debug_toolbar.panels.sql.SQLPanel')
67
+ DEBUG_TOOLBAR_CONFIG['DISABLE_PANELS'].add('debug_toolbar.panels.cache.CachePanel')
68
+
69
+ # don't load jquery from ajax.googleapis.com, just use django's version:
70
+ DEBUG_TOOLBAR_CONFIG['JQUERY_URL'] = STATIC_URL + 'admin/js/vendor/jquery/jquery.min.js'
71
+
72
+ DEBUG_TOOLBAR_CONFIG['SHOW_TEMPLATE_CONTEXT'] = True
73
+ DEBUG_TOOLBAR_CONFIG['SHOW_COLLAPSED'] = True # Show toolbar collapsed by default.
74
+ DEBUG_TOOLBAR_CONFIG['SHOW_TOOLBAR_CALLBACK'] = 'inventory_project.middlewares.djdt_show'