PyInventory 0.21.4__tar.gz → 0.22.1__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.1}/.gitignore +2 -3
  2. {pyinventory-0.21.4 → pyinventory-0.22.1}/.pre-commit-config.yaml +1 -1
  3. {pyinventory-0.21.4 → pyinventory-0.22.1}/.run/Template Django tests.run.xml +1 -0
  4. {pyinventory-0.21.4 → pyinventory-0.22.1}/.run/manage.py --help.run.xml +2 -2
  5. pyinventory-0.22.1/.run/manage.py make_messages.run.xml +25 -0
  6. {pyinventory-0.21.4 → pyinventory-0.22.1}/.run/manage.py update_req.run.xml +2 -2
  7. {pyinventory-0.21.4 → pyinventory-0.22.1}/.run/unittests ___all___.run.xml +1 -0
  8. {pyinventory-0.21.4 → pyinventory-0.22.1}/PKG-INFO +11 -4
  9. {pyinventory-0.21.4 → pyinventory-0.22.1}/README.md +10 -3
  10. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/__init__.py +2 -2
  11. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/admin/item.py +11 -14
  12. pyinventory-0.22.1/inventory/locale/ca/LC_MESSAGES/django.mo +0 -0
  13. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/locale/ca/LC_MESSAGES/django.po +19 -1
  14. pyinventory-0.22.1/inventory/locale/de/LC_MESSAGES/django.mo +0 -0
  15. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/locale/de/LC_MESSAGES/django.po +21 -3
  16. pyinventory-0.22.1/inventory/locale/en/LC_MESSAGES/django.mo +0 -0
  17. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/locale/en/LC_MESSAGES/django.po +21 -3
  18. pyinventory-0.22.1/inventory/locale/es/LC_MESSAGES/django.mo +0 -0
  19. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/locale/es/LC_MESSAGES/django.po +19 -1
  20. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/management/commands/seed_data.py +20 -3
  21. pyinventory-0.22.1/inventory/migrations/0015_itemmaincategory_itemmodel_category.py +32 -0
  22. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/models/item.py +33 -0
  23. pyinventory-0.22.1/inventory/persistent_filters.py +72 -0
  24. pyinventory-0.22.1/inventory/tests/test_management_command_seed_data.py +69 -0
  25. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/settings/local.py +0 -3
  26. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/settings/prod.py +14 -2
  27. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_item.py +87 -0
  28. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_item_auto_group_items_1.snapshot.html +10 -2
  29. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_item_normal_user_create_minimal_item_1.snapshot.html +30 -10
  30. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_item_normal_user_create_minimal_item_2.snapshot.html +31 -10
  31. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_superuser_admin_index_1.snapshot.html +17 -0
  32. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_inventory_commands_help_1.snapshot.txt +3 -3
  33. {pyinventory-0.21.4 → pyinventory-0.22.1}/pyproject.toml +5 -4
  34. pyinventory-0.22.1/uv.lock +2239 -0
  35. pyinventory-0.21.4/inventory/locale/ca/LC_MESSAGES/django.mo +0 -0
  36. pyinventory-0.21.4/inventory/locale/de/LC_MESSAGES/django.mo +0 -0
  37. pyinventory-0.21.4/inventory/locale/en/LC_MESSAGES/django.mo +0 -0
  38. pyinventory-0.21.4/inventory/locale/es/LC_MESSAGES/django.mo +0 -0
  39. pyinventory-0.21.4/inventory/tests/test_management_command_seed_data.py +0 -49
  40. pyinventory-0.21.4/uv.lock +0 -2124
  41. {pyinventory-0.21.4 → pyinventory-0.22.1}/.editorconfig +0 -0
  42. {pyinventory-0.21.4 → pyinventory-0.22.1}/.github/workflows/tests.yml +0 -0
  43. {pyinventory-0.21.4 → pyinventory-0.22.1}/.idea/.gitignore +0 -0
  44. {pyinventory-0.21.4 → pyinventory-0.22.1}/.pre-commit-hooks.yaml +0 -0
  45. {pyinventory-0.21.4 → pyinventory-0.22.1}/.run/Template Python.run.xml +0 -0
  46. {pyinventory-0.21.4 → pyinventory-0.22.1}/AUTHORS +0 -0
  47. {pyinventory-0.21.4 → pyinventory-0.22.1}/LICENSE +0 -0
  48. {pyinventory-0.21.4 → pyinventory-0.22.1}/dist/.gitignore +0 -0
  49. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/admin/__init__.py +0 -0
  50. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/admin/base.py +0 -0
  51. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/admin/location.py +0 -0
  52. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/admin/memo.py +0 -0
  53. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/admin/tagulous_fix.py +0 -0
  54. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/apps.py +0 -0
  55. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/checks.py +0 -0
  56. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/ckeditor_upload.py +0 -0
  57. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/constants.py +0 -0
  58. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/context_processors.py +0 -0
  59. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/forms.py +0 -0
  60. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/management/__init__.py +0 -0
  61. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/management/commands/__init__.py +0 -0
  62. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/management/commands/tree.py +0 -0
  63. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/middlewares.py +0 -0
  64. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0001_initial.py +0 -0
  65. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0002_auto_20201017_2211.py +0 -0
  66. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0003_auto_20201024_1830.py +0 -0
  67. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0004_item_user_images.py +0 -0
  68. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0005_serve_uploads_by_django_tools.py +0 -0
  69. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0006_refactor_image_model.py +0 -0
  70. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0007_add_file_attachment.py +0 -0
  71. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0008_last_check_datetime.py +0 -0
  72. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0009_add_memo.py +0 -0
  73. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0010_version_protect_models.py +0 -0
  74. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0011_parent_tree1.py +0 -0
  75. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0012_parent_tree2.py +0 -0
  76. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0013_alter_itemmodel_location.py +0 -0
  77. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/0014_alter_itemmodel_description_and_more.py +0 -0
  78. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/migrations/__init__.py +0 -0
  79. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/models/__init__.py +0 -0
  80. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/models/base.py +0 -0
  81. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/models/links.py +0 -0
  82. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/models/location.py +0 -0
  83. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/models/memo.py +0 -0
  84. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/parent_tree.py +0 -0
  85. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/permissions.py +0 -0
  86. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/request_dict.py +0 -0
  87. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/signals.py +0 -0
  88. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/string_utils.py +0 -0
  89. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/templates/admin/item/related_items.html +0 -0
  90. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/templates/admin/location/items.html +0 -0
  91. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/__init__.py +0 -0
  92. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/fixtures/__init__.py +0 -0
  93. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/fixtures/users.py +0 -0
  94. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_admin_location.py +0 -0
  95. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_admin_location_empty_change_list_1.snapshot.html +0 -0
  96. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_item_images.py +0 -0
  97. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_link_model.py +0 -0
  98. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_management_command_tree.py +0 -0
  99. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_parent_tree.py +0 -0
  100. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory/tests/test_parent_tree_model.py +0 -0
  101. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/__init__.py +0 -0
  102. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/__main__.py +0 -0
  103. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/middlewares.py +0 -0
  104. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/settings/__init__.py +0 -0
  105. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/settings/tests.py +0 -0
  106. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/templates/admin/base_site.html +0 -0
  107. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/templates/admin/login.html +0 -0
  108. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/__init__.py +0 -0
  109. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/fixtures.py +0 -0
  110. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/mocks.py +0 -0
  111. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/playwright_utils.py +0 -0
  112. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin.py +0 -0
  113. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_item_login_1.snapshot.html +0 -0
  114. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_memo.py +0 -0
  115. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_admin_memo_normal_user_create_minimal_item_1.snapshot.html +0 -0
  116. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_inventory_commands.py +0 -0
  117. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_migrations.py +0 -0
  118. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_models_item.py +0 -0
  119. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_playwright_admin.py +0 -0
  120. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_project_setup.py +0 -0
  121. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/tests/test_readme_history.py +0 -0
  122. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/urls.py +0 -0
  123. {pyinventory-0.21.4 → pyinventory-0.22.1}/inventory_project/wsgi.py +0 -0
  124. {pyinventory-0.21.4 → pyinventory-0.22.1}/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.1
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,13 @@ To make a new release, do this:
196
196
 
197
197
  [comment]: <> (✂✂✂ auto generated history start ✂✂✂)
198
198
 
199
+ * [v0.22.1](https://github.com/jedie/PyInventory/compare/v0.22.0...v0.22.1)
200
+ * 2025-11-18 - Fix #207 "All" Category can't be selected
201
+ * 2025-11-18 - Update requirements
202
+ * [v0.22.0](https://github.com/jedie/PyInventory/compare/v0.21.4...v0.22.0)
203
+ * 2025-10-16 - Add tests for PersistentRelatedFieldListFilter
204
+ * 2025-09-21 - NEW: Add a persistent "Main Category"
205
+ * 2025-09-21 - fix for django-admin-sortable2/issues/363
199
206
  * [v0.21.4](https://github.com/jedie/PyInventory/compare/v0.21.3...v0.21.4)
200
207
  * 2025-09-21 - Because of PyPi download errors: release as v0.21.4
201
208
  * [v0.21.3](https://github.com/jedie/PyInventory/compare/v0.21.2...v0.21.3)
@@ -210,15 +217,15 @@ To make a new release, do this:
210
217
  * 2025-09-20 - pre-commit: remove "default_install_hook_types"
211
218
  * 2025-09-20 - Remove obsolete config files
212
219
  * 2025-09-20 - Update ReadmeHistoryTestCase
220
+
221
+ <details><summary>Expand older history entries ...</summary>
222
+
213
223
  * [v0.21.2](https://github.com/jedie/PyInventory/compare/v0.21.1...v0.21.2)
214
224
  * 2025-09-09 - Update project, e.g.: Darker -> Ruff and fix tests
215
225
  * 2025-05-01 - Fix local dev server: Don't enforce https
216
226
  * [v0.21.1](https://github.com/jedie/PyInventory/compare/v0.21.0...v0.21.1)
217
227
  * 2025-05-01 - Replace setuptools with hatchling
218
228
  * 2025-04-30 - Update requirements and some small code parts
219
-
220
- <details><summary>Expand older history entries ...</summary>
221
-
222
229
  * [v0.21.0](https://github.com/jedie/PyInventory/compare/v0.20.1...v0.21.0)
223
230
  * 2025-03-23 - Bugfix publish: setuptools missing
224
231
  * 2025-03-23 - Migrate "pip-tools" -> "uv" and remove tox
@@ -166,6 +166,13 @@ To make a new release, do this:
166
166
 
167
167
  [comment]: <> (✂✂✂ auto generated history start ✂✂✂)
168
168
 
169
+ * [v0.22.1](https://github.com/jedie/PyInventory/compare/v0.22.0...v0.22.1)
170
+ * 2025-11-18 - Fix #207 "All" Category can't be selected
171
+ * 2025-11-18 - Update requirements
172
+ * [v0.22.0](https://github.com/jedie/PyInventory/compare/v0.21.4...v0.22.0)
173
+ * 2025-10-16 - Add tests for PersistentRelatedFieldListFilter
174
+ * 2025-09-21 - NEW: Add a persistent "Main Category"
175
+ * 2025-09-21 - fix for django-admin-sortable2/issues/363
169
176
  * [v0.21.4](https://github.com/jedie/PyInventory/compare/v0.21.3...v0.21.4)
170
177
  * 2025-09-21 - Because of PyPi download errors: release as v0.21.4
171
178
  * [v0.21.3](https://github.com/jedie/PyInventory/compare/v0.21.2...v0.21.3)
@@ -180,15 +187,15 @@ To make a new release, do this:
180
187
  * 2025-09-20 - pre-commit: remove "default_install_hook_types"
181
188
  * 2025-09-20 - Remove obsolete config files
182
189
  * 2025-09-20 - Update ReadmeHistoryTestCase
190
+
191
+ <details><summary>Expand older history entries ...</summary>
192
+
183
193
  * [v0.21.2](https://github.com/jedie/PyInventory/compare/v0.21.1...v0.21.2)
184
194
  * 2025-09-09 - Update project, e.g.: Darker -> Ruff and fix tests
185
195
  * 2025-05-01 - Fix local dev server: Don't enforce https
186
196
  * [v0.21.1](https://github.com/jedie/PyInventory/compare/v0.21.0...v0.21.1)
187
197
  * 2025-05-01 - Replace setuptools with hatchling
188
198
  * 2025-04-30 - Update requirements and some small code parts
189
-
190
- <details><summary>Expand older history entries ...</summary>
191
-
192
199
  * [v0.21.0](https://github.com/jedie/PyInventory/compare/v0.20.1...v0.21.0)
193
200
  * 2025-03-23 - Bugfix publish: setuptools missing
194
201
  * 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.1'
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,72 @@
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
+
42
+ # Our own parameter to display all choices:
43
+ self.lookup_kwarg_display_all = '%s__all' % field_path
44
+ display_all = bool(params.pop(self.lookup_kwarg_display_all, None))
45
+
46
+ super().__init__(field, request, params, model, model_admin, field_path)
47
+
48
+ if display_all:
49
+ logger.info('Display all choice for %s, ignore persistent filter for %r', opts.model_name, field_path)
50
+ else:
51
+ current_parameter = persistent_parameter(request, opts, parameter_name=self.lookup_kwarg)
52
+ if current_parameter and not self.lookup_val and not self.lookup_val_isnull:
53
+ logger.info('Restore %r filter for %s with %r', field_path, opts.model_name, current_parameter)
54
+ self.lookup_val = [current_parameter]
55
+ self.used_parameters = {self.lookup_kwarg: self.lookup_val}
56
+
57
+ @property
58
+ def include_empty_choice(self):
59
+ return True
60
+
61
+ def choices(self, changelist):
62
+ choices = iter(super().choices(changelist))
63
+
64
+ # Change the "All" choice to our own "display all" choice:
65
+ all_choice = next(choices)
66
+ all_choice['query_string'] = changelist.get_query_string(
67
+ new_params={self.lookup_kwarg_display_all: True},
68
+ remove=[self.lookup_kwarg, self.lookup_kwarg_isnull],
69
+ )
70
+ yield all_choice
71
+
72
+ yield from choices