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,157 @@
1
+ from bx_django_utils.test_utils.playwright import PlaywrightTestCase
2
+ from django.contrib.auth import authenticate
3
+ from django.contrib.auth.models import User
4
+ from django.http import HttpRequest
5
+ from django.test import override_settings
6
+ from override_storage import locmem_stats_override_storage
7
+ from playwright.sync_api import BrowserContext, expect
8
+
9
+ from inventory import __version__
10
+ from inventory.models import ItemImageModel, ItemLinkModel, ItemModel
11
+ from inventory_project.tests.fixtures import TempImageFile, get_normal_user
12
+ from inventory_project.tests.playwright_utils import login
13
+
14
+
15
+ @override_settings(SECURE_SSL_REDIRECT=False)
16
+ class PlaywrightInventoryTestCase(PlaywrightTestCase):
17
+ def test_root_page(self):
18
+ context: BrowserContext = self.browser.new_context(
19
+ ignore_https_errors=True,
20
+ locale='en_US',
21
+ timezone_id='Europe/Berlin',
22
+ )
23
+ with context.new_page() as page:
24
+ page.goto(self.live_server_url)
25
+ expect(page).to_have_url(f'{self.live_server_url}/admin/login/?next=/admin/')
26
+ expect(page).to_have_title(f'Log in | PyInventory v{__version__}')
27
+
28
+ def test_login(self):
29
+ username = 'a-user'
30
+ password = 'ThisIsNotAPassword!'
31
+ superuser = User.objects.create_superuser(username=username, password=password)
32
+ superuser.full_clean()
33
+
34
+ user = authenticate(request=HttpRequest(), username=username, password=password)
35
+ assert isinstance(user, User)
36
+
37
+ context: BrowserContext = self.browser.new_context(
38
+ ignore_https_errors=True,
39
+ locale='en_US',
40
+ timezone_id='Europe/Berlin',
41
+ )
42
+ with context.new_page() as page:
43
+ page.goto(self.live_server_url)
44
+ expect(page).to_have_url(f'{self.live_server_url}/admin/login/?next=/admin/')
45
+ expect(page).to_have_title(f'Log in | PyInventory v{__version__}')
46
+
47
+ page.type('#id_username', username)
48
+ page.type('#id_password', password)
49
+ page.locator('text=Log in').click()
50
+
51
+ expect(page).to_have_url(f'{self.live_server_url}/admin/')
52
+ expect(page).to_have_title(f'Site administration | PyInventory v{__version__}')
53
+
54
+ def test_admin(self):
55
+ superuser = User.objects.create_superuser(username='foo', password='ThisIsNotAPassword!')
56
+ superuser.full_clean()
57
+
58
+ context: BrowserContext = self.browser.new_context(
59
+ ignore_https_errors=True,
60
+ locale='en_US',
61
+ timezone_id='Europe/Berlin',
62
+ )
63
+ with context.new_page() as page:
64
+ login(page, self.client, url=self.live_server_url, user=superuser)
65
+
66
+ page.goto(f'{self.live_server_url}/admin/')
67
+ expect(page).to_have_url(f'{self.live_server_url}/admin/')
68
+ expect(page).to_have_title(f'Site administration | PyInventory v{__version__}')
69
+
70
+ def test_normal_user_create_item(self):
71
+ normal_user = get_normal_user()
72
+
73
+ context: BrowserContext = self.browser.new_context(
74
+ ignore_https_errors=True,
75
+ locale='en_US',
76
+ timezone_id='Europe/Berlin',
77
+ )
78
+ with context.new_page() as page, TempImageFile(
79
+ format='png', size=(1, 1)
80
+ ) as png_image, locmem_stats_override_storage() as storage_stats:
81
+ login(page, self.client, url=self.live_server_url, user=normal_user)
82
+
83
+ page.goto(f'{self.live_server_url}/admin/inventory/itemmodel/add/')
84
+ expect(page).to_have_title(f'Add Item | PyInventory v{__version__}')
85
+
86
+ page.locator('label:has-text("Kind:")')
87
+ kind_field = page.locator('//input[@id="id_kind"]/..//input[@role="searchbox"]')
88
+ kind_field.click()
89
+ kind_field.fill('Mainboard')
90
+ kind_field.press('Enter')
91
+
92
+ page.locator('label:has-text("Producer:")')
93
+ producer_field = page.locator('//input[@id="id_producer"]/..//input[@role="searchbox"]')
94
+ producer_field.click()
95
+ producer_field.fill('Triple D Int.Ltd.')
96
+ producer_field.press('Enter')
97
+
98
+ page.locator('label:has-text("Name:")')
99
+ page.fill('//input[@id="id_name"]', 'TD-20 (8088)')
100
+
101
+ # Add a Link:
102
+ page.get_by_role('link', name='Add another Link').click()
103
+ page.locator('#id_itemlinkmodel_set-0-url').click()
104
+ page.locator('#id_itemlinkmodel_set-0-url').fill('http://test.tld/foo/bar')
105
+ page.locator('#id_itemlinkmodel_set-0-name').click()
106
+ page.locator('#id_itemlinkmodel_set-0-name').fill('The First Link')
107
+ page.locator('#id_itemlinkmodel_set-0-tags').click()
108
+ page.locator('#id_itemlinkmodel_set-0-tags').fill('a-link-tag')
109
+ page.locator('#id_itemlinkmodel_set-0-tags').press('Tab')
110
+
111
+ # Add Image
112
+ page.get_by_role('link', name='Add another Image').click()
113
+ page.locator('#id_itemimagemodel_set-0-image').click()
114
+ page.locator('#id_itemimagemodel_set-0-image').set_input_files(png_image.name)
115
+ page.locator('#id_itemimagemodel_set-0-name').click()
116
+ page.locator('#id_itemimagemodel_set-0-name').fill('The Image Name')
117
+ page.locator('#id_itemimagemodel_set-0-tags').click()
118
+ page.locator('#id_itemimagemodel_set-0-tags').fill('a-image-tag')
119
+ page.locator('#id_itemimagemodel_set-0-tags').press('Tab')
120
+
121
+ assert ItemModel.objects.count() == 0
122
+
123
+ # Save the item:
124
+ page.locator('input:has-text("Save and continue editing")').click()
125
+ page.locator('text=The Tunes Item “A Test Tunes Item” was added successfully. You may edit it again')
126
+ page.locator('text="Triple D Int.Ltd." - TD-20 (8088)')
127
+
128
+ assert ItemModel.objects.count() == 1
129
+ item = ItemModel.objects.first()
130
+ assert item.verbose_name() == 'Mainboard - "Triple D Int.Ltd." - TD-20 (8088)'
131
+
132
+ # Save & continue?
133
+ expect(page).to_have_url(f'{self.live_server_url}/admin/inventory/itemmodel/{item.id}/change/')
134
+
135
+ # Check added image:
136
+ page.locator('text=The Image Name')
137
+ img = page.locator('//a[@class="image_file_input_preview"]/img')
138
+ img.scroll_into_view_if_needed()
139
+ img.is_visible()
140
+ assert img.evaluate('image => image.complete') is True
141
+
142
+ assert item.itemimagemodel_set.count() == 1
143
+ image: ItemImageModel = item.itemimagemodel_set.first()
144
+ assert str(image) == 'The Image Name'
145
+ assert image.user == normal_user
146
+ assert image.tags.get_tag_list() == ['a-image-tag']
147
+
148
+ # Check the added link:
149
+ page.locator('text=The First Link')
150
+ page.locator('text=Currently: http://test.tld/foo/bar')
151
+ links = list(item.itemlinkmodel_set.values_list('name', 'url'))
152
+ assert links == [('The First Link', 'http://test.tld/foo/bar')]
153
+ link: ItemLinkModel = item.itemlinkmodel_set.first()
154
+ assert link.tags.get_tag_list() == ['a-link-tag']
155
+
156
+ # The "png_image" file should be stored:
157
+ self.assertEqual(storage_stats.save_cnt, 1)
@@ -0,0 +1,102 @@
1
+ import subprocess
2
+ from pathlib import Path
3
+ from unittest import TestCase
4
+
5
+ from bx_py_utils.path import assert_is_dir, assert_is_file
6
+ from django.conf import settings
7
+ from django.core import checks
8
+ from django.core.cache import cache
9
+ from django.core.management import call_command
10
+ from manage_django_project.management.commands import code_style
11
+ from manageprojects.test_utils.project_setup import check_editor_config, get_py_max_line_length
12
+ from packaging.version import Version
13
+
14
+ from inventory import __version__
15
+ from manage import BASE_PATH
16
+
17
+
18
+ class ProjectSetupTestCase(TestCase):
19
+ def test_project_path(self):
20
+ project_path = settings.BASE_PATH
21
+ assert_is_dir(project_path)
22
+ assert_is_dir(project_path / 'inventory')
23
+ assert_is_dir(project_path / 'inventory_project')
24
+
25
+ self.assertEqual(project_path, BASE_PATH)
26
+
27
+ def test_template_dirs(self):
28
+ assert len(settings.TEMPLATES) == 1
29
+ dirs = settings.TEMPLATES[0].get('DIRS')
30
+ assert len(dirs) == 1
31
+ template_path = Path(dirs[0]).resolve()
32
+ assert template_path.is_dir()
33
+
34
+ def test_manage_check(self):
35
+ all_issues = checks.run_checks(
36
+ app_configs=None,
37
+ tags=None,
38
+ include_deployment_checks=True,
39
+ databases=None,
40
+ )
41
+ all_issue_ids = {issue.id for issue in all_issues}
42
+ excpeted_issues = {
43
+ 'async.E001', # os.environ['DJANGO_ALLOW_ASYNC_UNSAFE'] exists
44
+ }
45
+ if all_issue_ids != excpeted_issues:
46
+ print('=' * 100)
47
+ for issue in all_issues:
48
+ print(issue)
49
+ print('=' * 100)
50
+ raise AssertionError(f'There are check issues (see blow): {all_issue_ids ^ excpeted_issues}')
51
+
52
+ def test_cache(self):
53
+ # django cache should work in tests, because some tests "depends" on it
54
+ cache_key = 'a-cache-key'
55
+ self.assertIs(cache.get(cache_key), None)
56
+ cache.set(cache_key, 'the cache content', timeout=1)
57
+ self.assertEqual(cache.get(cache_key), 'the cache content', f'Check: {settings.CACHES=}')
58
+ cache.delete(cache_key)
59
+ self.assertIs(cache.get(cache_key), None)
60
+
61
+ def test_settings(self):
62
+ self.assertEqual(settings.SETTINGS_MODULE, 'inventory_project.settings.tests')
63
+ middlewares = [entry.rsplit('.', 1)[-1] for entry in settings.MIDDLEWARE]
64
+ assert 'AlwaysLoggedInAsSuperUserMiddleware' not in middlewares
65
+ assert 'DebugToolbarMiddleware' not in middlewares
66
+
67
+ def test_version(self):
68
+ self.assertIsNotNone(__version__)
69
+
70
+ version = Version(__version__) # Will raise InvalidVersion() if wrong formatted
71
+ self.assertEqual(str(version), __version__)
72
+
73
+ manage_bin = BASE_PATH / 'manage.py'
74
+ assert_is_file(manage_bin)
75
+
76
+ output = subprocess.check_output([manage_bin, 'version'], text=True)
77
+ self.assertIn(__version__, output)
78
+
79
+ def test_manage(self):
80
+ manage_bin = BASE_PATH / 'manage.py'
81
+ assert_is_file(manage_bin)
82
+
83
+ output = subprocess.check_output([manage_bin, 'project_info'], text=True)
84
+ self.assertIn('inventory_project', output)
85
+ self.assertIn('inventory_project.settings.local', output)
86
+ self.assertIn('inventory_project.settings.tests', output)
87
+ self.assertIn(__version__, output)
88
+
89
+ output = subprocess.check_output([manage_bin, 'check'], text=True)
90
+ self.assertIn('System check identified no issues (0 silenced).', output)
91
+
92
+ output = subprocess.check_output([manage_bin, 'makemigrations'], text=True)
93
+ self.assertIn("No changes detected", output)
94
+
95
+ def test_code_style(self):
96
+ call_command(code_style.Command())
97
+
98
+ def test_check_editor_config(self):
99
+ check_editor_config(package_root=BASE_PATH)
100
+
101
+ max_line_length = get_py_max_line_length(package_root=BASE_PATH)
102
+ self.assertEqual(max_line_length, 119)
@@ -0,0 +1,21 @@
1
+ from django.conf import settings
2
+ from django.conf.urls import include
3
+ from django.contrib import admin
4
+ from django.urls import path, re_path
5
+ from django.views.generic import RedirectView
6
+
7
+
8
+ admin.autodiscover()
9
+
10
+ urlpatterns = [ # Don't use i18n_patterns() here
11
+ path('admin/', admin.site.urls),
12
+ re_path(r'^$', RedirectView.as_view(pattern_name='admin:index')),
13
+ path('ckeditor/', include('ckeditor_uploader.urls')), # TODO: check permissions?
14
+ path(settings.MEDIA_URL.lstrip('/'), include('django_tools.serve_media_app.urls')),
15
+ ]
16
+
17
+
18
+ if settings.DEBUG:
19
+ import debug_toolbar
20
+
21
+ urlpatterns = [re_path(r'^__debug__/', include(debug_toolbar.urls))] + urlpatterns
@@ -0,0 +1,9 @@
1
+ """
2
+ WSGI config
3
+ """
4
+
5
+
6
+ from django.core.wsgi import get_wsgi_application
7
+
8
+
9
+ application = get_wsgi_application()