PyInventory 0.21.4__tar.gz → 0.22.0__tar.gz

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 (124) hide show
  1. {pyinventory-0.21.4 → pyinventory-0.22.0}/.gitignore +2 -3
  2. {pyinventory-0.21.4 → pyinventory-0.22.0}/.pre-commit-config.yaml +1 -1
  3. {pyinventory-0.21.4 → pyinventory-0.22.0}/.run/Template Django tests.run.xml +1 -0
  4. {pyinventory-0.21.4 → pyinventory-0.22.0}/.run/manage.py --help.run.xml +2 -2
  5. pyinventory-0.22.0/.run/manage.py make_messages.run.xml +25 -0
  6. {pyinventory-0.21.4 → pyinventory-0.22.0}/.run/manage.py update_req.run.xml +2 -2
  7. {pyinventory-0.21.4 → pyinventory-0.22.0}/.run/unittests ___all___.run.xml +1 -0
  8. {pyinventory-0.21.4 → pyinventory-0.22.0}/PKG-INFO +8 -4
  9. {pyinventory-0.21.4 → pyinventory-0.22.0}/README.md +7 -3
  10. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/__init__.py +2 -2
  11. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/admin/item.py +11 -14
  12. pyinventory-0.22.0/inventory/locale/ca/LC_MESSAGES/django.mo +0 -0
  13. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/locale/ca/LC_MESSAGES/django.po +19 -1
  14. pyinventory-0.22.0/inventory/locale/de/LC_MESSAGES/django.mo +0 -0
  15. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/locale/de/LC_MESSAGES/django.po +21 -3
  16. pyinventory-0.22.0/inventory/locale/en/LC_MESSAGES/django.mo +0 -0
  17. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/locale/en/LC_MESSAGES/django.po +21 -3
  18. pyinventory-0.22.0/inventory/locale/es/LC_MESSAGES/django.mo +0 -0
  19. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/locale/es/LC_MESSAGES/django.po +19 -1
  20. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/management/commands/seed_data.py +20 -3
  21. pyinventory-0.22.0/inventory/migrations/0015_itemmaincategory_itemmodel_category.py +32 -0
  22. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/models/item.py +33 -0
  23. pyinventory-0.22.0/inventory/persistent_filters.py +47 -0
  24. pyinventory-0.22.0/inventory/tests/test_management_command_seed_data.py +69 -0
  25. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/settings/local.py +0 -3
  26. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_admin_item.py +87 -0
  27. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_admin_item_auto_group_items_1.snapshot.html +10 -2
  28. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_admin_item_normal_user_create_minimal_item_1.snapshot.html +30 -10
  29. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_admin_item_normal_user_create_minimal_item_2.snapshot.html +30 -10
  30. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_admin_superuser_admin_index_1.snapshot.html +17 -0
  31. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_inventory_commands_help_1.snapshot.txt +3 -3
  32. {pyinventory-0.21.4 → pyinventory-0.22.0}/pyproject.toml +5 -4
  33. pyinventory-0.22.0/uv.lock +2240 -0
  34. pyinventory-0.21.4/inventory/locale/ca/LC_MESSAGES/django.mo +0 -0
  35. pyinventory-0.21.4/inventory/locale/de/LC_MESSAGES/django.mo +0 -0
  36. pyinventory-0.21.4/inventory/locale/en/LC_MESSAGES/django.mo +0 -0
  37. pyinventory-0.21.4/inventory/locale/es/LC_MESSAGES/django.mo +0 -0
  38. pyinventory-0.21.4/inventory/tests/test_management_command_seed_data.py +0 -49
  39. pyinventory-0.21.4/uv.lock +0 -2124
  40. {pyinventory-0.21.4 → pyinventory-0.22.0}/.editorconfig +0 -0
  41. {pyinventory-0.21.4 → pyinventory-0.22.0}/.github/workflows/tests.yml +0 -0
  42. {pyinventory-0.21.4 → pyinventory-0.22.0}/.idea/.gitignore +0 -0
  43. {pyinventory-0.21.4 → pyinventory-0.22.0}/.pre-commit-hooks.yaml +0 -0
  44. {pyinventory-0.21.4 → pyinventory-0.22.0}/.run/Template Python.run.xml +0 -0
  45. {pyinventory-0.21.4 → pyinventory-0.22.0}/AUTHORS +0 -0
  46. {pyinventory-0.21.4 → pyinventory-0.22.0}/LICENSE +0 -0
  47. {pyinventory-0.21.4 → pyinventory-0.22.0}/dist/.gitignore +0 -0
  48. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/admin/__init__.py +0 -0
  49. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/admin/base.py +0 -0
  50. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/admin/location.py +0 -0
  51. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/admin/memo.py +0 -0
  52. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/admin/tagulous_fix.py +0 -0
  53. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/apps.py +0 -0
  54. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/checks.py +0 -0
  55. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/ckeditor_upload.py +0 -0
  56. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/constants.py +0 -0
  57. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/context_processors.py +0 -0
  58. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/forms.py +0 -0
  59. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/management/__init__.py +0 -0
  60. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/management/commands/__init__.py +0 -0
  61. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/management/commands/tree.py +0 -0
  62. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/middlewares.py +0 -0
  63. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0001_initial.py +0 -0
  64. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0002_auto_20201017_2211.py +0 -0
  65. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0003_auto_20201024_1830.py +0 -0
  66. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0004_item_user_images.py +0 -0
  67. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0005_serve_uploads_by_django_tools.py +0 -0
  68. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0006_refactor_image_model.py +0 -0
  69. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0007_add_file_attachment.py +0 -0
  70. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0008_last_check_datetime.py +0 -0
  71. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0009_add_memo.py +0 -0
  72. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0010_version_protect_models.py +0 -0
  73. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0011_parent_tree1.py +0 -0
  74. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0012_parent_tree2.py +0 -0
  75. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0013_alter_itemmodel_location.py +0 -0
  76. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/0014_alter_itemmodel_description_and_more.py +0 -0
  77. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/migrations/__init__.py +0 -0
  78. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/models/__init__.py +0 -0
  79. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/models/base.py +0 -0
  80. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/models/links.py +0 -0
  81. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/models/location.py +0 -0
  82. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/models/memo.py +0 -0
  83. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/parent_tree.py +0 -0
  84. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/permissions.py +0 -0
  85. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/request_dict.py +0 -0
  86. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/signals.py +0 -0
  87. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/string_utils.py +0 -0
  88. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/templates/admin/item/related_items.html +0 -0
  89. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/templates/admin/location/items.html +0 -0
  90. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/tests/__init__.py +0 -0
  91. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/tests/fixtures/__init__.py +0 -0
  92. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/tests/fixtures/users.py +0 -0
  93. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/tests/test_admin_location.py +0 -0
  94. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/tests/test_admin_location_empty_change_list_1.snapshot.html +0 -0
  95. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/tests/test_item_images.py +0 -0
  96. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/tests/test_link_model.py +0 -0
  97. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/tests/test_management_command_tree.py +0 -0
  98. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/tests/test_parent_tree.py +0 -0
  99. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory/tests/test_parent_tree_model.py +0 -0
  100. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/__init__.py +0 -0
  101. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/__main__.py +0 -0
  102. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/middlewares.py +0 -0
  103. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/settings/__init__.py +0 -0
  104. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/settings/prod.py +0 -0
  105. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/settings/tests.py +0 -0
  106. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/templates/admin/base_site.html +0 -0
  107. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/templates/admin/login.html +0 -0
  108. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/__init__.py +0 -0
  109. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/fixtures.py +0 -0
  110. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/mocks.py +0 -0
  111. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/playwright_utils.py +0 -0
  112. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_admin.py +0 -0
  113. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_admin_item_login_1.snapshot.html +0 -0
  114. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_admin_memo.py +0 -0
  115. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_admin_memo_normal_user_create_minimal_item_1.snapshot.html +0 -0
  116. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_inventory_commands.py +0 -0
  117. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_migrations.py +0 -0
  118. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_models_item.py +0 -0
  119. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_playwright_admin.py +0 -0
  120. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_project_setup.py +0 -0
  121. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/tests/test_readme_history.py +0 -0
  122. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/urls.py +0 -0
  123. {pyinventory-0.21.4 → pyinventory-0.22.0}/inventory_project/wsgi.py +0 -0
  124. {pyinventory-0.21.4 → pyinventory-0.22.0}/manage.py +0 -0
@@ -9,7 +9,6 @@ __pycache__
9
9
  !.github
10
10
  !.run
11
11
  !.editorconfig
12
- !.flake8
13
12
  !.gitignore
14
13
  !.pre-commit-config.yaml
15
14
  !.pre-commit-hooks.yaml
@@ -20,8 +19,8 @@ __pycache__
20
19
  !/backups/.gitkeep
21
20
 
22
21
  # from test projects:
23
- **/static/*
24
- **/media/*
22
+ /static/
23
+ /media/
25
24
  *.sqlite3
26
25
  *.json
27
26
 
@@ -2,6 +2,6 @@
2
2
  # See https://pre-commit.com for more information
3
3
  repos:
4
4
  - repo: https://github.com/jedie/cli-base-utilities
5
- rev: v0.23.3
5
+ rev: v0.25.0
6
6
  hooks:
7
7
  - id: update-readme-history
@@ -6,6 +6,7 @@
6
6
  <option name="PARENT_ENVS" value="true" />
7
7
  <envs>
8
8
  <env name="PYTHONUNBUFFERED" value="1" />
9
+ <env name="RAISE_SNAPSHOT_ERRORS" value="0" />
9
10
  <env name="DJANGO_SETTINGS_MODULE" value="inventory_project.settings.tests" />
10
11
  </envs>
11
12
  <option name="SDK_HOME" value="" />
@@ -10,8 +10,8 @@
10
10
  <option name="SDK_HOME" value="" />
11
11
  <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
12
12
  <option name="IS_MODULE_SDK" value="true" />
13
- <option name="ADD_CONTENT_ROOTS" value="true" />
14
- <option name="ADD_SOURCE_ROOTS" value="true" />
13
+ <option name="ADD_CONTENT_ROOTS" value="false" />
14
+ <option name="ADD_SOURCE_ROOTS" value="false" />
15
15
  <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
16
16
  <option name="SCRIPT_NAME" value="$PROJECT_DIR$/manage.py" />
17
17
  <option name="PARAMETERS" value="--help" />
@@ -0,0 +1,25 @@
1
+ <component name="ProjectRunConfigurationManager">
2
+ <configuration default="false" name="manage.py make_messages" type="PythonConfigurationType" factoryName="Python">
3
+ <module name="PyInventory" />
4
+ <option name="ENV_FILES" value="" />
5
+ <option name="INTERPRETER_OPTIONS" value="" />
6
+ <option name="PARENT_ENVS" value="true" />
7
+ <envs>
8
+ <env name="PYTHONUNBUFFERED" value="1" />
9
+ </envs>
10
+ <option name="SDK_HOME" value="" />
11
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
12
+ <option name="IS_MODULE_SDK" value="true" />
13
+ <option name="ADD_CONTENT_ROOTS" value="false" />
14
+ <option name="ADD_SOURCE_ROOTS" value="false" />
15
+ <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
16
+ <option name="SCRIPT_NAME" value="$PROJECT_DIR$/manage.py" />
17
+ <option name="PARAMETERS" value="make_messages" />
18
+ <option name="SHOW_COMMAND_LINE" value="false" />
19
+ <option name="EMULATE_TERMINAL" value="true" />
20
+ <option name="MODULE_MODE" value="false" />
21
+ <option name="REDIRECT_INPUT" value="false" />
22
+ <option name="INPUT_FILE" value="" />
23
+ <method v="2" />
24
+ </configuration>
25
+ </component>
@@ -10,8 +10,8 @@
10
10
  <option name="SDK_HOME" value="" />
11
11
  <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
12
12
  <option name="IS_MODULE_SDK" value="true" />
13
- <option name="ADD_CONTENT_ROOTS" value="true" />
14
- <option name="ADD_SOURCE_ROOTS" value="true" />
13
+ <option name="ADD_CONTENT_ROOTS" value="false" />
14
+ <option name="ADD_SOURCE_ROOTS" value="false" />
15
15
  <EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
16
16
  <option name="SCRIPT_NAME" value="$PROJECT_DIR$/manage.py" />
17
17
  <option name="PARAMETERS" value="update_req" />
@@ -6,6 +6,7 @@
6
6
  <option name="PARENT_ENVS" value="true" />
7
7
  <envs>
8
8
  <env name="PYTHONUNBUFFERED" value="1" />
9
+ <env name="RAISE_SNAPSHOT_ERRORS" value="0" />
9
10
  <env name="DJANGO_SETTINGS_MODULE" value="inventory_project.settings.tests" />
10
11
  </envs>
11
12
  <option name="SDK_HOME" value="" />
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: PyInventory
3
- Version: 0.21.4
3
+ Version: 0.22.0
4
4
  Summary: Web based management to catalog things including state and location etc. using Python/Django.
5
5
  Project-URL: Documentation, https://github.com/jedie/PyInventory
6
6
  Project-URL: Source, https://github.com/jedie/PyInventory
@@ -196,6 +196,10 @@ To make a new release, do this:
196
196
 
197
197
  [comment]: <> (✂✂✂ auto generated history start ✂✂✂)
198
198
 
199
+ * [v0.22.0](https://github.com/jedie/PyInventory/compare/v0.21.4...v0.22.0)
200
+ * 2025-10-16 - Add tests for PersistentRelatedFieldListFilter
201
+ * 2025-09-21 - NEW: Add a persistent "Main Category"
202
+ * 2025-09-21 - fix for django-admin-sortable2/issues/363
199
203
  * [v0.21.4](https://github.com/jedie/PyInventory/compare/v0.21.3...v0.21.4)
200
204
  * 2025-09-21 - Because of PyPi download errors: release as v0.21.4
201
205
  * [v0.21.3](https://github.com/jedie/PyInventory/compare/v0.21.2...v0.21.3)
@@ -213,12 +217,12 @@ To make a new release, do this:
213
217
  * [v0.21.2](https://github.com/jedie/PyInventory/compare/v0.21.1...v0.21.2)
214
218
  * 2025-09-09 - Update project, e.g.: Darker -> Ruff and fix tests
215
219
  * 2025-05-01 - Fix local dev server: Don't enforce https
216
- * [v0.21.1](https://github.com/jedie/PyInventory/compare/v0.21.0...v0.21.1)
217
- * 2025-05-01 - Replace setuptools with hatchling
218
- * 2025-04-30 - Update requirements and some small code parts
219
220
 
220
221
  <details><summary>Expand older history entries ...</summary>
221
222
 
223
+ * [v0.21.1](https://github.com/jedie/PyInventory/compare/v0.21.0...v0.21.1)
224
+ * 2025-05-01 - Replace setuptools with hatchling
225
+ * 2025-04-30 - Update requirements and some small code parts
222
226
  * [v0.21.0](https://github.com/jedie/PyInventory/compare/v0.20.1...v0.21.0)
223
227
  * 2025-03-23 - Bugfix publish: setuptools missing
224
228
  * 2025-03-23 - Migrate "pip-tools" -> "uv" and remove tox
@@ -166,6 +166,10 @@ To make a new release, do this:
166
166
 
167
167
  [comment]: <> (✂✂✂ auto generated history start ✂✂✂)
168
168
 
169
+ * [v0.22.0](https://github.com/jedie/PyInventory/compare/v0.21.4...v0.22.0)
170
+ * 2025-10-16 - Add tests for PersistentRelatedFieldListFilter
171
+ * 2025-09-21 - NEW: Add a persistent "Main Category"
172
+ * 2025-09-21 - fix for django-admin-sortable2/issues/363
169
173
  * [v0.21.4](https://github.com/jedie/PyInventory/compare/v0.21.3...v0.21.4)
170
174
  * 2025-09-21 - Because of PyPi download errors: release as v0.21.4
171
175
  * [v0.21.3](https://github.com/jedie/PyInventory/compare/v0.21.2...v0.21.3)
@@ -183,12 +187,12 @@ To make a new release, do this:
183
187
  * [v0.21.2](https://github.com/jedie/PyInventory/compare/v0.21.1...v0.21.2)
184
188
  * 2025-09-09 - Update project, e.g.: Darker -> Ruff and fix tests
185
189
  * 2025-05-01 - Fix local dev server: Don't enforce https
186
- * [v0.21.1](https://github.com/jedie/PyInventory/compare/v0.21.0...v0.21.1)
187
- * 2025-05-01 - Replace setuptools with hatchling
188
- * 2025-04-30 - Update requirements and some small code parts
189
190
 
190
191
  <details><summary>Expand older history entries ...</summary>
191
192
 
193
+ * [v0.21.1](https://github.com/jedie/PyInventory/compare/v0.21.0...v0.21.1)
194
+ * 2025-05-01 - Replace setuptools with hatchling
195
+ * 2025-04-30 - Update requirements and some small code parts
192
196
  * [v0.21.0](https://github.com/jedie/PyInventory/compare/v0.20.1...v0.21.0)
193
197
  * 2025-03-23 - Bugfix publish: setuptools missing
194
198
  * 2025-03-23 - Migrate "pip-tools" -> "uv" and remove tox
@@ -3,10 +3,10 @@
3
3
  Web based management to catalog things including state and location etc. using Python/Django.
4
4
 
5
5
  created 14.20.2020 by Jens Diemer <opensource@jensdiemer.de>
6
- :copyleft: 2020-2024 by the PyInventory team, see AUTHORS for more details.
6
+ :copyleft: 2020-2025 by the PyInventory team, see AUTHORS for more details.
7
7
  :license: GNU GPL v3 or above, see LICENSE for more details.
8
8
  """
9
9
 
10
10
  # See https://packaging.python.org/en/latest/specifications/version-specifiers/
11
- __version__ = '0.21.4'
11
+ __version__ = '0.22.0'
12
12
  __author__ = 'Jens Diemer <PyInventory@jensdiemer.de>'
@@ -1,7 +1,7 @@
1
1
  import logging
2
2
 
3
3
  import tagulous
4
- from adminsortable2.admin import SortableAdminMixin, SortableInlineAdminMixin
4
+ from adminsortable2.admin import SortableAdminBase, SortableAdminMixin, SortableInlineAdminMixin
5
5
  from django.conf import settings
6
6
  from django.contrib import admin
7
7
  from django.template.loader import render_to_string
@@ -20,13 +20,19 @@ from inventory.admin.base import (
20
20
  )
21
21
  from inventory.admin.tagulous_fix import TagulousModelAdminFix
22
22
  from inventory.models import ItemLinkModel, ItemModel
23
- from inventory.models.item import ItemFileModel, ItemImageModel
23
+ from inventory.models.item import ItemFileModel, ItemImageModel, ItemMainCategory
24
+ from inventory.persistent_filters import PersistentRelatedFieldListFilter
24
25
  from inventory.string_utils import ltruncatechars
25
26
 
26
27
 
27
28
  logger = logging.getLogger(__name__)
28
29
 
29
30
 
31
+ @admin.register(ItemMainCategory)
32
+ class ItemMainCategoryAdmin(SortableAdminMixin, admin.ModelAdmin):
33
+ list_display = ('order', 'name')
34
+
35
+
30
36
  class ItemLinkModelInline(UserInlineMixin, SortableInlineAdminMixin, admin.TabularInline):
31
37
  model = ItemLinkModel
32
38
  extra = 0
@@ -46,7 +52,7 @@ class ItemModelResource(ModelResource):
46
52
 
47
53
 
48
54
  @admin.register(ItemModel)
49
- class ItemModelAdmin(TagulousModelAdminFix, ImportExportMixin, SortableAdminMixin, BaseUserAdmin):
55
+ class ItemModelAdmin(TagulousModelAdminFix, ImportExportMixin, SortableAdminBase, BaseUserAdmin):
50
56
  @admin.display(description=_('Related items'))
51
57
  def related_items(self, obj):
52
58
  if obj.pk is None:
@@ -95,6 +101,7 @@ class ItemModelAdmin(TagulousModelAdminFix, ImportExportMixin, SortableAdminMixi
95
101
  ordering = ('path_str',)
96
102
  list_display_links = ()
97
103
  list_filter = (
104
+ ('category', PersistentRelatedFieldListFilter),
98
105
  LimitTreeDepthListFilter,
99
106
  ('kind', admin.RelatedOnlyFieldListFilter),
100
107
  ('location', admin.RelatedOnlyFieldListFilter),
@@ -118,7 +125,7 @@ class ItemModelAdmin(TagulousModelAdminFix, ImportExportMixin, SortableAdminMixi
118
125
  _('Basic'),
119
126
  {
120
127
  'fields': (
121
- 'kind',
128
+ ('category', 'kind'),
122
129
  ('producer', 'name'),
123
130
  'description',
124
131
  'tags',
@@ -161,15 +168,5 @@ class ItemModelAdmin(TagulousModelAdminFix, ImportExportMixin, SortableAdminMixi
161
168
  readonly_fields = ('id', 'create_dt', 'update_dt', 'user', 'related_items')
162
169
  inlines = (ItemImageModelInline, ItemFileModelInline, ItemLinkModelInline)
163
170
 
164
- def get_list_display(self, request):
165
- list_display = list(super().get_list_display(request))
166
-
167
- # FIXME: SortableAdminMixin.get_list_display() adds this, we didn't need here:
168
- # See: https://github.com/jrief/django-admin-sortable2/issues/363
169
- if '_reorder_' in list_display:
170
- list_display.remove('_reorder_')
171
-
172
- return list_display
173
-
174
171
 
175
172
  tagulous.admin.enhance(ItemModel, ItemModelAdmin)
@@ -7,7 +7,7 @@ msgid ""
7
7
  msgstr ""
8
8
  "Project-Id-Version: \n"
9
9
  "Report-Msgid-Bugs-To: \n"
10
- "POT-Creation-Date: 2022-09-30 20:48+0200\n"
10
+ "POT-Creation-Date: 2025-10-16 10:51+0200\n"
11
11
  "PO-Revision-Date: 2022-09-30 20:07+0200\n"
12
12
  "Last-Translator: Jaume López\n"
13
13
  "Language-Team: \n"
@@ -102,6 +102,24 @@ msgstr "Nom"
102
102
  msgid "BaseItemAttachmentModel.name.help_text"
103
103
  msgstr ""
104
104
 
105
+ msgid "ItemMainCategory.name.verbose_name"
106
+ msgstr ""
107
+
108
+ msgid "ItemMainCategory.name.help_text"
109
+ msgstr " "
110
+
111
+ msgid "ItemMainCategory.verbose_name"
112
+ msgstr ""
113
+
114
+ msgid "ItemMainCategory.verbose_name_plural"
115
+ msgstr ""
116
+
117
+ msgid "ItemModel.category.verbose_name"
118
+ msgstr ""
119
+
120
+ msgid "ItemModel.category.help_text"
121
+ msgstr " "
122
+
105
123
  msgid "ItemModel.kind.verbose_name"
106
124
  msgstr "Tipus"
107
125
 
@@ -7,8 +7,8 @@ msgid ""
7
7
  msgstr ""
8
8
  "Project-Id-Version: \n"
9
9
  "Report-Msgid-Bugs-To: \n"
10
- "POT-Creation-Date: 2022-09-30 20:48+0200\n"
11
- "PO-Revision-Date: 2022-09-30 20:48+0200\n"
10
+ "POT-Creation-Date: 2025-10-16 10:51+0200\n"
11
+ "PO-Revision-Date: 2025-09-21 17:34+0200\n"
12
12
  "Last-Translator: Jens Diemer\n"
13
13
  "Language-Team: \n"
14
14
  "Language: de\n"
@@ -16,7 +16,7 @@ msgstr ""
16
16
  "Content-Type: text/plain; charset=UTF-8\n"
17
17
  "Content-Transfer-Encoding: 8bit\n"
18
18
  "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19
- "X-Generator: Poedit 2.3\n"
19
+ "X-Generator: Poedit 3.4.2\n"
20
20
 
21
21
  msgid "Limit tree depth"
22
22
  msgstr "Tiefe des Baumes"
@@ -101,6 +101,24 @@ msgstr "Name"
101
101
  msgid "BaseItemAttachmentModel.name.help_text"
102
102
  msgstr "Optionalen Namen (Wird automatisch aus dem Dateinamen gesetzt)"
103
103
 
104
+ msgid "ItemMainCategory.name.verbose_name"
105
+ msgstr "Kategorie"
106
+
107
+ msgid "ItemMainCategory.name.help_text"
108
+ msgstr " "
109
+
110
+ msgid "ItemMainCategory.verbose_name"
111
+ msgstr "Kategorie"
112
+
113
+ msgid "ItemMainCategory.verbose_name_plural"
114
+ msgstr "Kategorien"
115
+
116
+ msgid "ItemModel.category.verbose_name"
117
+ msgstr "Kategorie"
118
+
119
+ msgid "ItemModel.category.help_text"
120
+ msgstr " "
121
+
104
122
  msgid "ItemModel.kind.verbose_name"
105
123
  msgstr "Art"
106
124
 
@@ -7,8 +7,8 @@ msgid ""
7
7
  msgstr ""
8
8
  "Project-Id-Version: \n"
9
9
  "Report-Msgid-Bugs-To: \n"
10
- "POT-Creation-Date: 2022-09-30 20:48+0200\n"
11
- "PO-Revision-Date: 2021-10-09 19:36+0200\n"
10
+ "POT-Creation-Date: 2025-10-16 10:51+0200\n"
11
+ "PO-Revision-Date: 2025-09-21 17:36+0200\n"
12
12
  "Last-Translator: Jens Diemer\n"
13
13
  "Language-Team: \n"
14
14
  "Language: en\n"
@@ -16,7 +16,7 @@ msgstr ""
16
16
  "Content-Type: text/plain; charset=UTF-8\n"
17
17
  "Content-Transfer-Encoding: 8bit\n"
18
18
  "Plural-Forms: nplurals=2; plural=(n != 1);\n"
19
- "X-Generator: Poedit 2.3\n"
19
+ "X-Generator: Poedit 3.4.2\n"
20
20
 
21
21
  msgid "Limit tree depth"
22
22
  msgstr ""
@@ -101,6 +101,24 @@ msgstr "Name"
101
101
  msgid "BaseItemAttachmentModel.name.help_text"
102
102
  msgstr ""
103
103
 
104
+ msgid "ItemMainCategory.name.verbose_name"
105
+ msgstr "Name"
106
+
107
+ msgid "ItemMainCategory.name.help_text"
108
+ msgstr " "
109
+
110
+ msgid "ItemMainCategory.verbose_name"
111
+ msgstr "Category"
112
+
113
+ msgid "ItemMainCategory.verbose_name_plural"
114
+ msgstr "Categories"
115
+
116
+ msgid "ItemModel.category.verbose_name"
117
+ msgstr "Category"
118
+
119
+ msgid "ItemModel.category.help_text"
120
+ msgstr " "
121
+
104
122
  msgid "ItemModel.kind.verbose_name"
105
123
  msgstr "Kind"
106
124
 
@@ -7,7 +7,7 @@ msgid ""
7
7
  msgstr ""
8
8
  "Project-Id-Version: \n"
9
9
  "Report-Msgid-Bugs-To: \n"
10
- "POT-Creation-Date: 2022-09-30 20:48+0200\n"
10
+ "POT-Creation-Date: 2025-10-16 10:51+0200\n"
11
11
  "PO-Revision-Date: 2021-10-09 19:36+0200\n"
12
12
  "Last-Translator: Jaume López\n"
13
13
  "Language-Team: \n"
@@ -102,6 +102,24 @@ msgstr "Nombre"
102
102
  msgid "BaseItemAttachmentModel.name.help_text"
103
103
  msgstr ""
104
104
 
105
+ msgid "ItemMainCategory.name.verbose_name"
106
+ msgstr ""
107
+
108
+ msgid "ItemMainCategory.name.help_text"
109
+ msgstr " "
110
+
111
+ msgid "ItemMainCategory.verbose_name"
112
+ msgstr ""
113
+
114
+ msgid "ItemMainCategory.verbose_name_plural"
115
+ msgstr ""
116
+
117
+ msgid "ItemModel.category.verbose_name"
118
+ msgstr ""
119
+
120
+ msgid "ItemModel.category.help_text"
121
+ msgstr " "
122
+
105
123
  msgid "ItemModel.kind.verbose_name"
106
124
  msgstr "Tipos"
107
125
 
@@ -7,6 +7,7 @@ from django.contrib.auth.models import User
7
7
  from django.core.management.base import BaseCommand
8
8
 
9
9
  from inventory.models import ItemModel, LocationModel
10
+ from inventory.models.item import ItemMainCategory
10
11
 
11
12
 
12
13
  SEED_DATA_USER_PREFIX = 'seed-data-user-'
@@ -46,7 +47,8 @@ def iter_location_chain(user, location_count):
46
47
 
47
48
 
48
49
  class ItemCreator:
49
- def __init__(self):
50
+ def __init__(self, categories: list[ItemMainCategory]):
51
+ self.categories = itertools.cycle(categories)
50
52
  self.equipment_no = itertools.count(start=1)
51
53
  self.item_no = itertools.count(start=1)
52
54
  self.part_no = itertools.count(start=1)
@@ -57,10 +59,13 @@ class ItemCreator:
57
59
  assert user
58
60
  assert location
59
61
  while True:
62
+ category = next(self.categories)
63
+
60
64
  equipment = ItemModel.objects.create(
61
65
  user=user,
66
+ category=category,
62
67
  location=location,
63
- name=f'Equipment {next(self.equipment_no):03}',
68
+ name=f'{category.name} {next(self.equipment_no):03}',
64
69
  )
65
70
  equipment.full_clean()
66
71
  yield equipment
@@ -68,6 +73,7 @@ class ItemCreator:
68
73
  while True:
69
74
  item = ItemModel.objects.create(
70
75
  user=user,
76
+ category=category,
71
77
  location=location,
72
78
  name=f'Item {next(self.item_no):03}',
73
79
  parent=equipment,
@@ -78,6 +84,7 @@ class ItemCreator:
78
84
  while True:
79
85
  part = ItemModel.objects.create(
80
86
  user=user,
87
+ category=category,
81
88
  location=location,
82
89
  name=f'Part {next(self.part_no):03}',
83
90
  parent=item,
@@ -89,6 +96,14 @@ class ItemCreator:
89
96
  return
90
97
 
91
98
 
99
+ def create_categories() -> list[ItemMainCategory]:
100
+ categories = []
101
+ for category in ('Retrocomputing', 'Photo Equipment', 'Household Goods'):
102
+ instance = ItemMainCategory.objects.get_or_create(name=category)[0]
103
+ categories.append(instance)
104
+ return categories
105
+
106
+
92
107
  class Command(BaseCommand):
93
108
  help = 'Fill database with example data'
94
109
 
@@ -111,13 +126,15 @@ class Command(BaseCommand):
111
126
  log_level = logging.WARNING
112
127
 
113
128
  with SetupLogger(level=log_level):
129
+ categories = create_categories()
130
+
114
131
  existing_users = User.objects.filter(username__startswith=SEED_DATA_USER_PREFIX)
115
132
  for user in existing_users:
116
133
  self.stdout.write(f'Clean data from user {user}...')
117
134
  info = user.delete()
118
135
  self.stdout.write(f'done: {info}')
119
136
 
120
- item_creator = ItemCreator()
137
+ item_creator = ItemCreator(categories)
121
138
 
122
139
  for user_no in range(1, user_count + 1):
123
140
  self.stdout.write('_' * 100)
@@ -0,0 +1,32 @@
1
+ # Generated by Django 5.1.12 on 2025-10-16 08:44
2
+
3
+ import django.db.models.deletion
4
+ from django.db import migrations, models
5
+
6
+
7
+ class Migration(migrations.Migration):
8
+
9
+ dependencies = [
10
+ ('inventory', '0014_alter_itemmodel_description_and_more'),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.CreateModel(
15
+ name='ItemMainCategory',
16
+ fields=[
17
+ ('id', models.SmallAutoField(primary_key=True, serialize=False)),
18
+ ('name', models.CharField(help_text='ItemMainCategory.name.help_text', max_length=32, unique=True, verbose_name='ItemMainCategory.name.verbose_name')),
19
+ ('order', models.SmallIntegerField(default=0)),
20
+ ],
21
+ options={
22
+ 'verbose_name': 'ItemMainCategory.verbose_name',
23
+ 'verbose_name_plural': 'ItemMainCategory.verbose_name_plural',
24
+ 'ordering': ('order', 'name'),
25
+ },
26
+ ),
27
+ migrations.AddField(
28
+ model_name='itemmodel',
29
+ name='category',
30
+ field=models.ForeignKey(blank=True, help_text='ItemModel.category.help_text', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='items', to='inventory.itemmaincategory', verbose_name='ItemModel.category.verbose_name'),
31
+ ),
32
+ ]
@@ -17,6 +17,29 @@ from inventory.models.links import BaseLink
17
17
  logger = logging.getLogger(__name__)
18
18
 
19
19
 
20
+ class ItemMainCategory(models.Model):
21
+ id = models.SmallAutoField(primary_key=True)
22
+ name = models.CharField(
23
+ max_length=32,
24
+ unique=True,
25
+ verbose_name=_('ItemMainCategory.name.verbose_name'),
26
+ help_text=_('ItemMainCategory.name.help_text'),
27
+ )
28
+ order = models.SmallIntegerField(
29
+ default=0,
30
+ blank=False,
31
+ null=False,
32
+ )
33
+
34
+ def __str__(self):
35
+ return self.name
36
+
37
+ class Meta:
38
+ ordering = ('order', 'name')
39
+ verbose_name = _('ItemMainCategory.verbose_name')
40
+ verbose_name_plural = _('ItemMainCategory.verbose_name_plural')
41
+
42
+
20
43
  class ItemQuerySet(models.QuerySet):
21
44
  def sort(self):
22
45
  return self.order_by('kind', 'producer', 'name')
@@ -29,6 +52,16 @@ class ItemModel(BaseParentTreeModel, VersionProtectBaseModel):
29
52
 
30
53
  objects = ItemQuerySet.as_manager()
31
54
 
55
+ category = models.ForeignKey(
56
+ ItemMainCategory,
57
+ null=True,
58
+ blank=True,
59
+ on_delete=models.PROTECT,
60
+ related_name='items',
61
+ verbose_name=_('ItemModel.category.verbose_name'),
62
+ help_text=_('ItemModel.category.help_text'),
63
+ )
64
+
32
65
  kind = tagulous.models.TagField(
33
66
  case_sensitive=False,
34
67
  force_lowercase=False,
@@ -0,0 +1,47 @@
1
+ import logging
2
+
3
+ from django.contrib import admin
4
+ from django.core.cache import cache
5
+ from django.db.models.options import Options
6
+
7
+
8
+ logger = logging.getLogger(__name__)
9
+
10
+
11
+ CURRENT_CATEGORY_CACHE_KEY_PREFIX = 'persistent_parameter'
12
+ CURRENT_CATEGORY_CACHE_TIMEOUT = 60 * 60 * 24 * 14 # 2 weeks
13
+
14
+
15
+ def persistent_parameter(request, opts: Options, parameter_name: str):
16
+ cache_key = (
17
+ f'{CURRENT_CATEGORY_CACHE_KEY_PREFIX}_{request.user.pk}_{opts.app_label}_{opts.model_name}_{parameter_name}'
18
+ )
19
+
20
+ # Collect from GET:
21
+ if parameter_name in request.GET:
22
+ current_value = request.GET[parameter_name] or None
23
+ logger.debug('Store %r to %r', current_value, cache_key)
24
+ cache.set(cache_key, current_value, timeout=CURRENT_CATEGORY_CACHE_TIMEOUT)
25
+ return current_value
26
+
27
+ # Try to restore from cache:
28
+ current_value = cache.get(cache_key)
29
+ logger.debug('Restore %r from %r', current_value, cache_key)
30
+ return current_value
31
+
32
+
33
+ class PersistentRelatedFieldListFilter(admin.RelatedFieldListFilter):
34
+ """
35
+ Stores the last filter value in the cache and restores it on next visit.
36
+ Note: Will not restore "is null" filter state!
37
+ """
38
+
39
+ def __init__(self, field, request, params, model, model_admin, field_path):
40
+ opts: Options = model._meta
41
+ super().__init__(field, request, params, model, model_admin, field_path)
42
+
43
+ current_parameter = persistent_parameter(request, opts, parameter_name=self.lookup_kwarg)
44
+ if current_parameter and not self.lookup_val:
45
+ logger.info('Restore %r filter for %s with %r', field_path, opts.model_name, current_parameter)
46
+ self.lookup_val = [current_parameter]
47
+ self.used_parameters = {self.lookup_kwarg: self.lookup_val}