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.
- PyInventory-0.19.0.dist-info/AUTHORS +14 -0
- PyInventory-0.19.0.dist-info/LICENSE +674 -0
- PyInventory-0.19.0.dist-info/METADATA +347 -0
- PyInventory-0.19.0.dist-info/RECORD +101 -0
- PyInventory-0.19.0.dist-info/WHEEL +5 -0
- PyInventory-0.19.0.dist-info/entry_points.txt +2 -0
- PyInventory-0.19.0.dist-info/top_level.txt +2 -0
- inventory/__init__.py +7 -0
- inventory/admin/__init__.py +3 -0
- inventory/admin/base.py +104 -0
- inventory/admin/item.py +169 -0
- inventory/admin/location.py +78 -0
- inventory/admin/memo.py +76 -0
- inventory/admin/tagulous_fix.py +45 -0
- inventory/apps.py +18 -0
- inventory/ckeditor_upload.py +15 -0
- inventory/context_processors.py +5 -0
- inventory/forms.py +36 -0
- inventory/locale/ca/LC_MESSAGES/django.mo +0 -0
- inventory/locale/ca/LC_MESSAGES/django.po +297 -0
- inventory/locale/de/LC_MESSAGES/django.mo +0 -0
- inventory/locale/de/LC_MESSAGES/django.po +294 -0
- inventory/locale/en/LC_MESSAGES/django.mo +0 -0
- inventory/locale/en/LC_MESSAGES/django.po +294 -0
- inventory/locale/es/LC_MESSAGES/django.mo +0 -0
- inventory/locale/es/LC_MESSAGES/django.po +297 -0
- inventory/management/__init__.py +0 -0
- inventory/management/commands/__init__.py +0 -0
- inventory/management/commands/seed_data.py +135 -0
- inventory/management/commands/tree.py +62 -0
- inventory/middlewares.py +21 -0
- inventory/migrations/0001_initial.py +596 -0
- inventory/migrations/0002_auto_20201017_2211.py +87 -0
- inventory/migrations/0003_auto_20201024_1830.py +23 -0
- inventory/migrations/0004_item_user_images.py +129 -0
- inventory/migrations/0005_serve_uploads_by_django_tools.py +77 -0
- inventory/migrations/0006_refactor_image_model.py +46 -0
- inventory/migrations/0007_add_file_attachment.py +128 -0
- inventory/migrations/0008_last_check_datetime.py +23 -0
- inventory/migrations/0009_add_memo.py +517 -0
- inventory/migrations/0010_version_protect_models.py +37 -0
- inventory/migrations/0011_parent_tree1.py +97 -0
- inventory/migrations/0012_parent_tree2.py +20 -0
- inventory/migrations/0013_alter_itemmodel_location.py +26 -0
- inventory/migrations/__init__.py +0 -0
- inventory/models/__init__.py +3 -0
- inventory/models/base.py +239 -0
- inventory/models/item.py +228 -0
- inventory/models/links.py +104 -0
- inventory/models/location.py +24 -0
- inventory/models/memo.py +109 -0
- inventory/parent_tree.py +71 -0
- inventory/permissions.py +60 -0
- inventory/request_dict.py +16 -0
- inventory/signals.py +15 -0
- inventory/string_utils.py +15 -0
- inventory/templates/admin/item/related_items.html +18 -0
- inventory/templates/admin/location/items.html +18 -0
- inventory/tests/__init__.py +0 -0
- inventory/tests/fixtures/__init__.py +0 -0
- inventory/tests/fixtures/users.py +11 -0
- inventory/tests/test_admin_location.py +34 -0
- inventory/tests/test_admin_location_empty_change_list_1.snapshot.html +84 -0
- inventory/tests/test_item_images.py +76 -0
- inventory/tests/test_link_model.py +72 -0
- inventory/tests/test_management_command_seed_data.py +49 -0
- inventory/tests/test_management_command_tree.py +27 -0
- inventory/tests/test_parent_tree.py +40 -0
- inventory/tests/test_parent_tree_model.py +139 -0
- inventory_project/__init__.py +12 -0
- inventory_project/__main__.py +17 -0
- inventory_project/manage.py +41 -0
- inventory_project/middlewares.py +23 -0
- inventory_project/publish.py +21 -0
- inventory_project/settings/__init__.py +0 -0
- inventory_project/settings/local.py +74 -0
- inventory_project/settings/prod.py +393 -0
- inventory_project/settings/tests.py +45 -0
- inventory_project/templates/admin/base_site.html +22 -0
- inventory_project/templates/admin/login.html +32 -0
- inventory_project/tests/__init__.py +0 -0
- inventory_project/tests/fixtures.py +40 -0
- inventory_project/tests/mocks.py +15 -0
- inventory_project/tests/playwright_utils.py +22 -0
- inventory_project/tests/test_admin.py +15 -0
- inventory_project/tests/test_admin_item.py +240 -0
- inventory_project/tests/test_admin_item_auto_group_items_1.snapshot.html +349 -0
- inventory_project/tests/test_admin_item_auto_group_items_2.snapshot.html +232 -0
- inventory_project/tests/test_admin_item_login_1.snapshot.html +40 -0
- inventory_project/tests/test_admin_item_normal_user_create_minimal_item_1.snapshot.html +637 -0
- inventory_project/tests/test_admin_item_normal_user_create_minimal_item_2.snapshot.html +930 -0
- inventory_project/tests/test_admin_memo.py +153 -0
- inventory_project/tests/test_admin_memo_normal_user_create_minimal_item_1.snapshot.html +365 -0
- inventory_project/tests/test_command_shell_help_django4.2.3.snapshot.txt +60 -0
- inventory_project/tests/test_inventory_commands.py +26 -0
- inventory_project/tests/test_migrations.py +22 -0
- inventory_project/tests/test_models_item.py +24 -0
- inventory_project/tests/test_playwright_admin.py +157 -0
- inventory_project/tests/test_project_setup.py +102 -0
- inventory_project/urls.py +21 -0
- inventory_project/wsgi.py +9 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
from unittest import mock
|
|
2
|
+
|
|
3
|
+
from bx_django_utils.test_utils.html_assertion import HtmlAssertionMixin, assert_html_response_snapshot
|
|
4
|
+
from django.contrib.auth.models import User
|
|
5
|
+
from django.template.defaulttags import CsrfTokenNode, NowNode
|
|
6
|
+
from django.test import TestCase, override_settings
|
|
7
|
+
from django_tools.unittest_utils.mockup import ImageDummy
|
|
8
|
+
from model_bakery import baker
|
|
9
|
+
from override_storage import locmem_stats_override_storage
|
|
10
|
+
from reversion.models import Revision
|
|
11
|
+
|
|
12
|
+
from inventory.models import MemoImageModel, MemoModel
|
|
13
|
+
from inventory.permissions import get_or_create_normal_user_group
|
|
14
|
+
from inventory_project.tests.mocks import MockInventoryVersionString
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class AdminAnonymousTests(TestCase):
|
|
18
|
+
def test_login(self):
|
|
19
|
+
response = self.client.get('/admin/inventory/memomodel/add/', secure=True, HTTP_ACCEPT_LANGUAGE='en')
|
|
20
|
+
self.assertRedirects(
|
|
21
|
+
response,
|
|
22
|
+
expected_url='/admin/login/?next=/admin/inventory/memomodel/add/',
|
|
23
|
+
fetch_redirect_response=False,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@override_settings(SECURE_SSL_REDIRECT=False)
|
|
28
|
+
class AdminTestCase(HtmlAssertionMixin, TestCase):
|
|
29
|
+
@classmethod
|
|
30
|
+
def setUpTestData(cls):
|
|
31
|
+
cls.normaluser = baker.make(User, username='NormalUser', is_staff=True, is_active=True, is_superuser=False)
|
|
32
|
+
assert cls.normaluser.user_permissions.count() == 0
|
|
33
|
+
group = get_or_create_normal_user_group()[0]
|
|
34
|
+
cls.normaluser.groups.set([group])
|
|
35
|
+
|
|
36
|
+
def test_normal_user_create_minimal_item(self):
|
|
37
|
+
self.client.force_login(self.normaluser)
|
|
38
|
+
|
|
39
|
+
with mock.patch.object(NowNode, 'render', return_value='MockedNowNode'), mock.patch.object(
|
|
40
|
+
CsrfTokenNode, 'render', return_value='MockedCsrfTokenNode'
|
|
41
|
+
), MockInventoryVersionString():
|
|
42
|
+
response = self.client.get('/admin/inventory/memomodel/add/')
|
|
43
|
+
assert response.status_code == 200
|
|
44
|
+
self.assert_html_parts(response, parts=('<title>Add Memo | PyInventory vMockedVersionString</title>',))
|
|
45
|
+
assert_html_response_snapshot(response=response, validate=False)
|
|
46
|
+
|
|
47
|
+
assert MemoModel.objects.count() == 0
|
|
48
|
+
|
|
49
|
+
response = self.client.post(
|
|
50
|
+
path='/admin/inventory/memomodel/add/',
|
|
51
|
+
data={
|
|
52
|
+
'version': 0, # VersionProtectBaseModel field
|
|
53
|
+
'name': 'The Memo Name',
|
|
54
|
+
'memo': 'This is a test Memo',
|
|
55
|
+
'memoimagemodel_set-TOTAL_FORMS': '0',
|
|
56
|
+
'memoimagemodel_set-INITIAL_FORMS': '0',
|
|
57
|
+
'memoimagemodel_set-MIN_NUM_FORMS': '0',
|
|
58
|
+
'memoimagemodel_set-MAX_NUM_FORMS': '1000',
|
|
59
|
+
'memoimagemodel_set-__prefix__-position': '0',
|
|
60
|
+
'memofilemodel_set-TOTAL_FORMS': '0',
|
|
61
|
+
'memofilemodel_set-INITIAL_FORMS': '0',
|
|
62
|
+
'memofilemodel_set-MIN_NUM_FORMS': '0',
|
|
63
|
+
'memofilemodel_set-MAX_NUM_FORMS': '1000',
|
|
64
|
+
'memofilemodel_set-__prefix__-position': '0',
|
|
65
|
+
'memolinkmodel_set-TOTAL_FORMS': '0',
|
|
66
|
+
'memolinkmodel_set-INITIAL_FORMS': '0',
|
|
67
|
+
'memolinkmodel_set-MIN_NUM_FORMS': '0',
|
|
68
|
+
'memolinkmodel_set-MAX_NUM_FORMS': '1000',
|
|
69
|
+
'memolinkmodel_set-__prefix__-position': '0',
|
|
70
|
+
'_save': 'Save',
|
|
71
|
+
},
|
|
72
|
+
)
|
|
73
|
+
assert response.status_code == 302, response.content.decode('utf-8') # Form error?
|
|
74
|
+
self.assertRedirects(response, expected_url='/admin/inventory/memomodel/')
|
|
75
|
+
|
|
76
|
+
data = list(MemoModel.objects.values_list('name', 'memo'))
|
|
77
|
+
assert data == [('The Memo Name', 'This is a test Memo')]
|
|
78
|
+
|
|
79
|
+
item = MemoModel.objects.first()
|
|
80
|
+
|
|
81
|
+
self.assert_messages(
|
|
82
|
+
response,
|
|
83
|
+
expected_messages=[
|
|
84
|
+
f'The Memo “<a href="/admin/inventory/memomodel/{item.pk}/change/">The Memo Name</a>”'
|
|
85
|
+
' was added successfully.'
|
|
86
|
+
],
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
assert item.user_id == self.normaluser.pk
|
|
90
|
+
|
|
91
|
+
# django-revision integration:
|
|
92
|
+
comments = list(Revision.objects.order_by('date_created').values_list('comment', flat=True))
|
|
93
|
+
self.assertEqual(comments, ['Added.'])
|
|
94
|
+
|
|
95
|
+
def test_new_item_with_image(self):
|
|
96
|
+
"""
|
|
97
|
+
https://github.com/jedie/PyInventory/issues/33
|
|
98
|
+
"""
|
|
99
|
+
self.client.force_login(self.normaluser)
|
|
100
|
+
|
|
101
|
+
img = ImageDummy(width=1, height=1, format='png').in_memory_image_file(filename='test.png')
|
|
102
|
+
|
|
103
|
+
with locmem_stats_override_storage() as storage_stats:
|
|
104
|
+
response = self.client.post(
|
|
105
|
+
path='/admin/inventory/memomodel/add/',
|
|
106
|
+
data={
|
|
107
|
+
'version': 0, # VersionProtectBaseModel field
|
|
108
|
+
'name': 'The Memo Name',
|
|
109
|
+
'memo': 'This is a test Memo',
|
|
110
|
+
'memoimagemodel_set-TOTAL_FORMS': '1',
|
|
111
|
+
'memoimagemodel_set-INITIAL_FORMS': '0',
|
|
112
|
+
'memoimagemodel_set-MIN_NUM_FORMS': '0',
|
|
113
|
+
'memoimagemodel_set-MAX_NUM_FORMS': '1000',
|
|
114
|
+
'memoimagemodel_set-0-position': '0',
|
|
115
|
+
'memoimagemodel_set-__prefix__-position': '0',
|
|
116
|
+
'memoimagemodel_set-0-image': img,
|
|
117
|
+
'memofilemodel_set-TOTAL_FORMS': '0',
|
|
118
|
+
'memofilemodel_set-INITIAL_FORMS': '0',
|
|
119
|
+
'memofilemodel_set-MIN_NUM_FORMS': '0',
|
|
120
|
+
'memofilemodel_set-MAX_NUM_FORMS': '1000',
|
|
121
|
+
'memofilemodel_set-__prefix__-position': '0',
|
|
122
|
+
'memolinkmodel_set-TOTAL_FORMS': '0',
|
|
123
|
+
'memolinkmodel_set-INITIAL_FORMS': '0',
|
|
124
|
+
'memolinkmodel_set-MIN_NUM_FORMS': '0',
|
|
125
|
+
'memolinkmodel_set-MAX_NUM_FORMS': '1000',
|
|
126
|
+
'memolinkmodel_set-__prefix__-position': '0',
|
|
127
|
+
'_save': 'Save',
|
|
128
|
+
},
|
|
129
|
+
)
|
|
130
|
+
assert response.status_code == 302, response.content.decode('utf-8') # Form error?
|
|
131
|
+
memo = MemoModel.objects.first() or MemoModel()
|
|
132
|
+
self.assert_messages(
|
|
133
|
+
response,
|
|
134
|
+
expected_messages=[
|
|
135
|
+
f'The Memo “<a href="/admin/inventory/memomodel/{memo.pk}/change/">The Memo Name</a>”'
|
|
136
|
+
' was added successfully.'
|
|
137
|
+
],
|
|
138
|
+
)
|
|
139
|
+
self.assertRedirects(response, expected_url='/admin/inventory/memomodel/')
|
|
140
|
+
|
|
141
|
+
data = list(MemoModel.objects.values_list('name', 'memo'))
|
|
142
|
+
assert data == [('The Memo Name', 'This is a test Memo')]
|
|
143
|
+
|
|
144
|
+
assert memo.user_id == self.normaluser.pk
|
|
145
|
+
|
|
146
|
+
assert MemoImageModel.objects.count() == 1
|
|
147
|
+
image = MemoImageModel.objects.first()
|
|
148
|
+
assert image.name == 'test.png'
|
|
149
|
+
assert image.memo == memo
|
|
150
|
+
assert image.user_id == self.normaluser.pk
|
|
151
|
+
|
|
152
|
+
# Test image file should be stored:
|
|
153
|
+
self.assertEqual(storage_stats.save_cnt, 1)
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
<div class="colM" id="content">
|
|
2
|
+
<h1>
|
|
3
|
+
Add Memo
|
|
4
|
+
</h1>
|
|
5
|
+
<div id="content-main">
|
|
6
|
+
<form enctype="multipart/form-data" id="memomodel_form" method="post" novalidate="">
|
|
7
|
+
MockedCsrfTokenNode
|
|
8
|
+
<div>
|
|
9
|
+
<fieldset class="module aligned collapse">
|
|
10
|
+
<h2>
|
|
11
|
+
Internals
|
|
12
|
+
</h2>
|
|
13
|
+
<div class="form-row field-id field-version">
|
|
14
|
+
<div class="flex-container form-multiline">
|
|
15
|
+
<div>
|
|
16
|
+
<div class="flex-container fieldBox field-id">
|
|
17
|
+
<label>
|
|
18
|
+
ID:
|
|
19
|
+
</label>
|
|
20
|
+
<div class="readonly">
|
|
21
|
+
-
|
|
22
|
+
</div>
|
|
23
|
+
</div>
|
|
24
|
+
<div class="help">
|
|
25
|
+
<div>
|
|
26
|
+
ID
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div>
|
|
31
|
+
<div class="flex-container fieldBox field-version">
|
|
32
|
+
<label class="required inline" for="id_version">
|
|
33
|
+
Version:
|
|
34
|
+
</label>
|
|
35
|
+
<input id="id_version" name="version" required="" type="hidden" value="0"/>
|
|
36
|
+
0
|
|
37
|
+
</div>
|
|
38
|
+
<div class="help" id="id_version_helptext">
|
|
39
|
+
<div>
|
|
40
|
+
Internal version number of this entry. Used to protect the overwriting of an older entry.
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
</div>
|
|
46
|
+
<div class="form-row field-user">
|
|
47
|
+
<div>
|
|
48
|
+
<div class="flex-container">
|
|
49
|
+
<label>
|
|
50
|
+
User:
|
|
51
|
+
</label>
|
|
52
|
+
<div class="readonly">
|
|
53
|
+
-
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="help">
|
|
57
|
+
<div>
|
|
58
|
+
The user who is the owner of this entry and can manage it (will be set automatically)
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</fieldset>
|
|
64
|
+
<fieldset class="module aligned collapse">
|
|
65
|
+
<h2>
|
|
66
|
+
Meta
|
|
67
|
+
</h2>
|
|
68
|
+
<div class="form-row field-create_dt">
|
|
69
|
+
<div>
|
|
70
|
+
<div class="flex-container">
|
|
71
|
+
<label>
|
|
72
|
+
Create date:
|
|
73
|
+
</label>
|
|
74
|
+
<div class="readonly">
|
|
75
|
+
-
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
<div class="help">
|
|
79
|
+
<div>
|
|
80
|
+
(will be set automatically)
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="form-row field-update_dt">
|
|
86
|
+
<div>
|
|
87
|
+
<div class="flex-container">
|
|
88
|
+
<label>
|
|
89
|
+
Last update:
|
|
90
|
+
</label>
|
|
91
|
+
<div class="readonly">
|
|
92
|
+
-
|
|
93
|
+
</div>
|
|
94
|
+
</div>
|
|
95
|
+
<div class="help">
|
|
96
|
+
<div>
|
|
97
|
+
(will be set automatically)
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</fieldset>
|
|
103
|
+
<fieldset class="module aligned">
|
|
104
|
+
<h2>
|
|
105
|
+
Basic
|
|
106
|
+
</h2>
|
|
107
|
+
<div class="form-row field-name">
|
|
108
|
+
<div>
|
|
109
|
+
<div class="flex-container">
|
|
110
|
+
<label class="required" for="id_name">
|
|
111
|
+
Name:
|
|
112
|
+
</label>
|
|
113
|
+
<input class="vTextField" id="id_name" maxlength="255" name="name" required="" type="text"/>
|
|
114
|
+
</div>
|
|
115
|
+
<div class="help" id="id_name_helptext">
|
|
116
|
+
<div>
|
|
117
|
+
Name
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
<div class="form-row field-memo">
|
|
123
|
+
<div>
|
|
124
|
+
<div class="flex-container">
|
|
125
|
+
<label for="id_memo">
|
|
126
|
+
Description:
|
|
127
|
+
</label>
|
|
128
|
+
<div class="django-ckeditor-widget" data-field-id="id_memo" style="display: inline-block;">
|
|
129
|
+
<textarea cols="40" data-config='{"skin": "moono-lisa", "toolbar_Basic": [["Source", "-", "Bold", "Italic"]], "toolbar_Full": [["Styles", "Format", "Bold", "Italic", "Underline", "Strike", "SpellChecker", "Undo", "Redo"], ["Link", "Unlink", "Anchor"], ["Image", "Flash", "Table", "HorizontalRule"], ["TextColor", "BGColor"], ["Smiley", "SpecialChar"], ["Source"]], "toolbar": "PyInventoryToolbarConfig", "height": "25em", "width": "100%", "filebrowserWindowWidth": 940, "filebrowserWindowHeight": 725, "removeButtons": "Language,Cut,Copy,Paste,Undo,Redo,Anchor", "removePlugins": ["a11yhelp", "adobeair", "ajax", "autoembed", "autolink", "bbcode", "bidi", "clipboard", "codesnippet", "codesnippetgeshi", "contextmenu", "copyformatting", "devtools", "dialog", "dialogadvtab", "div", "divarea", "docprops", "embed", "embedbase", "embedsemantic", "enterkey", "exportpdf", "find", "flash", "forms", "htmlwriter", "iframe", "iframedialog", "language", "magicline", "mathjax", "newpage", "notification", "notificationaggregator", "pagebreak", "pastefromgdocs", "pastefromword", "pastetext", "pastetools", "placeholder", "preview", "print", "save", "scayt", "selectall", "sharedspace", "smiley", "sourcedialog", "specialchar", "stylescombo", "stylesheetparser", "tab", "templates", "uicolor", "widget", "wsc", "xml"], "toolbar_PyInventoryToolbarConfig": [{"name": "basicstyles", "items": ["Bold", "Italic", "Underline", "Strike", "-", "RemoveFormat"]}, {"name": "paragraph", "items": ["NumberedList", "BulletedList", "-", "Outdent", "Indent", "-", "Blockquote", "-", "JustifyLeft", "JustifyCenter", "JustifyRight", "JustifyBlock"]}, {"name": "links", "items": ["Link", "Unlink", "Anchor"]}, {"name": "insert", "items": ["Image", "Table", "HorizontalRule"]}, "/", {"name": "styles", "items": ["Styles", "Format", "Font", "FontSize"]}, {"name": "colors", "items": ["TextColor", "BGColor"]}, {"name": "tools", "items": ["Maximize", "ShowBlocks", "Source"]}, {"name": "about", "items": ["About"]}], "filebrowserUploadUrl": "/ckeditor/upload/", "filebrowserBrowseUrl": "/ckeditor/browse/", "language": "en"}' data-external-plugin-resources="[]" data-id="id_memo" data-processed="0" data-type="ckeditortype" id="id_memo" name="memo" rows="10"></textarea>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
<div class="help" id="id_memo_helptext">
|
|
133
|
+
<div>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="form-row field-tags">
|
|
139
|
+
<div>
|
|
140
|
+
<div class="flex-container">
|
|
141
|
+
<label for="id_tags">
|
|
142
|
+
Tags:
|
|
143
|
+
</label>
|
|
144
|
+
<div class="related-widget-wrapper" data-model-ref="Memo Tags">
|
|
145
|
+
<input autocomplete="off" data-tag-list="[]" data-tag-options='{"case_sensitive": false, "force_lowercase": false, "max_count": 10, "space_delimiter": false, "required": false}' data-tagulous="true" data-theme="admin-autocomplete" id="id_tags" name="tags" type="text"/>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
<div class="help" id="id_tags_helptext">
|
|
149
|
+
<div>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
</fieldset>
|
|
155
|
+
<div class="js-inline-admin-formset inline-group" data-inline-formset='{"name": "#memoimagemodel_set", "options": {"prefix": "memoimagemodel_set", "addText": "Add another Image", "deleteText": "Remove"}}' data-inline-type="tabular" id="memoimagemodel_set-group">
|
|
156
|
+
<div class="tabular inline-related">
|
|
157
|
+
<input id="id_memoimagemodel_set-TOTAL_FORMS" name="memoimagemodel_set-TOTAL_FORMS" type="hidden" value="0"/>
|
|
158
|
+
<input id="id_memoimagemodel_set-INITIAL_FORMS" name="memoimagemodel_set-INITIAL_FORMS" type="hidden" value="0"/>
|
|
159
|
+
<input id="id_memoimagemodel_set-MIN_NUM_FORMS" name="memoimagemodel_set-MIN_NUM_FORMS" type="hidden" value="0"/>
|
|
160
|
+
<input id="id_memoimagemodel_set-MAX_NUM_FORMS" name="memoimagemodel_set-MAX_NUM_FORMS" type="hidden" value="1000"/>
|
|
161
|
+
<fieldset class="module sortable">
|
|
162
|
+
<h2>
|
|
163
|
+
Images
|
|
164
|
+
</h2>
|
|
165
|
+
<table>
|
|
166
|
+
<thead>
|
|
167
|
+
<tr>
|
|
168
|
+
<th class="original">
|
|
169
|
+
</th>
|
|
170
|
+
<th class="column-position hidden">
|
|
171
|
+
Position
|
|
172
|
+
</th>
|
|
173
|
+
<th class="column-preview">
|
|
174
|
+
Preview
|
|
175
|
+
</th>
|
|
176
|
+
<th class="column-image required">
|
|
177
|
+
Image
|
|
178
|
+
<img alt="( )" class="help help-tooltip" height="10" src="/static/admin/img/icon-unknown.svg" title=" " width="10"/>
|
|
179
|
+
</th>
|
|
180
|
+
<th class="column-name">
|
|
181
|
+
Name
|
|
182
|
+
<img alt="(BaseItemAttachmentModel.name.help_text)" class="help help-tooltip" height="10" src="/static/admin/img/icon-unknown.svg" title="BaseItemAttachmentModel.name.help_text" width="10"/>
|
|
183
|
+
</th>
|
|
184
|
+
<th class="column-tags">
|
|
185
|
+
Tags
|
|
186
|
+
<img alt="( )" class="help help-tooltip" height="10" src="/static/admin/img/icon-unknown.svg" title=" " width="10"/>
|
|
187
|
+
</th>
|
|
188
|
+
<th>
|
|
189
|
+
Delete?
|
|
190
|
+
</th>
|
|
191
|
+
</tr>
|
|
192
|
+
</thead>
|
|
193
|
+
<tbody>
|
|
194
|
+
<tr class="form-row empty-form" id="memoimagemodel_set-empty">
|
|
195
|
+
<td class="original">
|
|
196
|
+
<input id="id_memoimagemodel_set-__prefix__-id" name="memoimagemodel_set-__prefix__-id" type="hidden"/>
|
|
197
|
+
<input id="id_memoimagemodel_set-__prefix__-memo" name="memoimagemodel_set-__prefix__-memo" type="hidden"/>
|
|
198
|
+
</td>
|
|
199
|
+
<td class="field-position hidden">
|
|
200
|
+
<input class="_reorder_" id="id_memoimagemodel_set-__prefix__-position" name="memoimagemodel_set-__prefix__-position" type="hidden" value="0"/>
|
|
201
|
+
</td>
|
|
202
|
+
<td class="field-preview">
|
|
203
|
+
<p>
|
|
204
|
+
-
|
|
205
|
+
</p>
|
|
206
|
+
</td>
|
|
207
|
+
<td class="field-image">
|
|
208
|
+
<input accept="image/*" id="id_memoimagemodel_set-__prefix__-image" name="memoimagemodel_set-__prefix__-image" type="file"/>
|
|
209
|
+
</td>
|
|
210
|
+
<td class="field-name">
|
|
211
|
+
<input class="vTextField" id="id_memoimagemodel_set-__prefix__-name" maxlength="255" name="memoimagemodel_set-__prefix__-name" type="text"/>
|
|
212
|
+
</td>
|
|
213
|
+
<td class="field-tags">
|
|
214
|
+
<div class="related-widget-wrapper" data-model-ref="Image Tags">
|
|
215
|
+
<input autocomplete="off" data-tag-list="[]" data-tag-options='{"case_sensitive": false, "force_lowercase": false, "max_count": 10, "space_delimiter": false, "required": false}' data-tagulous="true" data-theme="admin-autocomplete" id="id_memoimagemodel_set-__prefix__-tags" name="memoimagemodel_set-__prefix__-tags" type="text"/>
|
|
216
|
+
</div>
|
|
217
|
+
</td>
|
|
218
|
+
<td class="delete">
|
|
219
|
+
</td>
|
|
220
|
+
</tr>
|
|
221
|
+
</tbody>
|
|
222
|
+
</table>
|
|
223
|
+
</fieldset>
|
|
224
|
+
</div>
|
|
225
|
+
</div>
|
|
226
|
+
<div class="js-inline-admin-formset inline-group" data-inline-formset='{"name": "#memofilemodel_set", "options": {"prefix": "memofilemodel_set", "addText": "Add another File", "deleteText": "Remove"}}' data-inline-type="tabular" id="memofilemodel_set-group">
|
|
227
|
+
<div class="tabular inline-related">
|
|
228
|
+
<input id="id_memofilemodel_set-TOTAL_FORMS" name="memofilemodel_set-TOTAL_FORMS" type="hidden" value="0"/>
|
|
229
|
+
<input id="id_memofilemodel_set-INITIAL_FORMS" name="memofilemodel_set-INITIAL_FORMS" type="hidden" value="0"/>
|
|
230
|
+
<input id="id_memofilemodel_set-MIN_NUM_FORMS" name="memofilemodel_set-MIN_NUM_FORMS" type="hidden" value="0"/>
|
|
231
|
+
<input id="id_memofilemodel_set-MAX_NUM_FORMS" name="memofilemodel_set-MAX_NUM_FORMS" type="hidden" value="1000"/>
|
|
232
|
+
<fieldset class="module sortable">
|
|
233
|
+
<h2>
|
|
234
|
+
Files
|
|
235
|
+
</h2>
|
|
236
|
+
<table>
|
|
237
|
+
<thead>
|
|
238
|
+
<tr>
|
|
239
|
+
<th class="original">
|
|
240
|
+
</th>
|
|
241
|
+
<th class="column-position hidden">
|
|
242
|
+
Position
|
|
243
|
+
</th>
|
|
244
|
+
<th class="column-file required">
|
|
245
|
+
File
|
|
246
|
+
<img alt="( )" class="help help-tooltip" height="10" src="/static/admin/img/icon-unknown.svg" title=" " width="10"/>
|
|
247
|
+
</th>
|
|
248
|
+
<th class="column-name">
|
|
249
|
+
Name
|
|
250
|
+
<img alt="(BaseItemAttachmentModel.name.help_text)" class="help help-tooltip" height="10" src="/static/admin/img/icon-unknown.svg" title="BaseItemAttachmentModel.name.help_text" width="10"/>
|
|
251
|
+
</th>
|
|
252
|
+
<th class="column-tags">
|
|
253
|
+
Tags
|
|
254
|
+
<img alt="( )" class="help help-tooltip" height="10" src="/static/admin/img/icon-unknown.svg" title=" " width="10"/>
|
|
255
|
+
</th>
|
|
256
|
+
<th>
|
|
257
|
+
Delete?
|
|
258
|
+
</th>
|
|
259
|
+
</tr>
|
|
260
|
+
</thead>
|
|
261
|
+
<tbody>
|
|
262
|
+
<tr class="form-row empty-form" id="memofilemodel_set-empty">
|
|
263
|
+
<td class="original">
|
|
264
|
+
<input id="id_memofilemodel_set-__prefix__-id" name="memofilemodel_set-__prefix__-id" type="hidden"/>
|
|
265
|
+
<input id="id_memofilemodel_set-__prefix__-memo" name="memofilemodel_set-__prefix__-memo" type="hidden"/>
|
|
266
|
+
</td>
|
|
267
|
+
<td class="field-position hidden">
|
|
268
|
+
<input class="_reorder_" id="id_memofilemodel_set-__prefix__-position" name="memofilemodel_set-__prefix__-position" type="hidden" value="0"/>
|
|
269
|
+
</td>
|
|
270
|
+
<td class="field-file">
|
|
271
|
+
<input id="id_memofilemodel_set-__prefix__-file" name="memofilemodel_set-__prefix__-file" type="file"/>
|
|
272
|
+
</td>
|
|
273
|
+
<td class="field-name">
|
|
274
|
+
<input class="vTextField" id="id_memofilemodel_set-__prefix__-name" maxlength="255" name="memofilemodel_set-__prefix__-name" type="text"/>
|
|
275
|
+
</td>
|
|
276
|
+
<td class="field-tags">
|
|
277
|
+
<div class="related-widget-wrapper" data-model-ref="File Tags">
|
|
278
|
+
<input autocomplete="off" data-tag-list="[]" data-tag-options='{"case_sensitive": false, "force_lowercase": false, "max_count": 10, "space_delimiter": false, "required": false}' data-tagulous="true" data-theme="admin-autocomplete" id="id_memofilemodel_set-__prefix__-tags" name="memofilemodel_set-__prefix__-tags" type="text"/>
|
|
279
|
+
</div>
|
|
280
|
+
</td>
|
|
281
|
+
<td class="delete">
|
|
282
|
+
</td>
|
|
283
|
+
</tr>
|
|
284
|
+
</tbody>
|
|
285
|
+
</table>
|
|
286
|
+
</fieldset>
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
<div class="js-inline-admin-formset inline-group" data-inline-formset='{"name": "#memolinkmodel_set", "options": {"prefix": "memolinkmodel_set", "addText": "Add another Link", "deleteText": "Remove"}}' data-inline-type="tabular" id="memolinkmodel_set-group">
|
|
290
|
+
<div class="tabular inline-related last-related">
|
|
291
|
+
<input id="id_memolinkmodel_set-TOTAL_FORMS" name="memolinkmodel_set-TOTAL_FORMS" type="hidden" value="0"/>
|
|
292
|
+
<input id="id_memolinkmodel_set-INITIAL_FORMS" name="memolinkmodel_set-INITIAL_FORMS" type="hidden" value="0"/>
|
|
293
|
+
<input id="id_memolinkmodel_set-MIN_NUM_FORMS" name="memolinkmodel_set-MIN_NUM_FORMS" type="hidden" value="0"/>
|
|
294
|
+
<input id="id_memolinkmodel_set-MAX_NUM_FORMS" name="memolinkmodel_set-MAX_NUM_FORMS" type="hidden" value="1000"/>
|
|
295
|
+
<fieldset class="module sortable">
|
|
296
|
+
<h2>
|
|
297
|
+
Links
|
|
298
|
+
</h2>
|
|
299
|
+
<table>
|
|
300
|
+
<thead>
|
|
301
|
+
<tr>
|
|
302
|
+
<th class="original">
|
|
303
|
+
</th>
|
|
304
|
+
<th class="column-tags">
|
|
305
|
+
Tags
|
|
306
|
+
<img alt="( )" class="help help-tooltip" height="10" src="/static/admin/img/icon-unknown.svg" title=" " width="10"/>
|
|
307
|
+
</th>
|
|
308
|
+
<th class="column-name">
|
|
309
|
+
Name
|
|
310
|
+
<img alt="( )" class="help help-tooltip" height="10" src="/static/admin/img/icon-unknown.svg" title=" " width="10"/>
|
|
311
|
+
</th>
|
|
312
|
+
<th class="column-url required">
|
|
313
|
+
URL
|
|
314
|
+
<img alt="( )" class="help help-tooltip" height="10" src="/static/admin/img/icon-unknown.svg" title=" " width="10"/>
|
|
315
|
+
</th>
|
|
316
|
+
<th class="column-position hidden">
|
|
317
|
+
Position
|
|
318
|
+
</th>
|
|
319
|
+
<th>
|
|
320
|
+
Delete?
|
|
321
|
+
</th>
|
|
322
|
+
</tr>
|
|
323
|
+
</thead>
|
|
324
|
+
<tbody>
|
|
325
|
+
<tr class="form-row empty-form" id="memolinkmodel_set-empty">
|
|
326
|
+
<td class="original">
|
|
327
|
+
<input id="id_memolinkmodel_set-__prefix__-id" name="memolinkmodel_set-__prefix__-id" type="hidden"/>
|
|
328
|
+
<input id="id_memolinkmodel_set-__prefix__-memo" name="memolinkmodel_set-__prefix__-memo" type="hidden"/>
|
|
329
|
+
</td>
|
|
330
|
+
<td class="field-tags">
|
|
331
|
+
<div class="related-widget-wrapper" data-model-ref="Link Tags">
|
|
332
|
+
<input autocomplete="off" data-tag-list="[]" data-tag-options='{"case_sensitive": false, "force_lowercase": false, "max_count": 10, "space_delimiter": false, "required": false}' data-tagulous="true" data-theme="admin-autocomplete" id="id_memolinkmodel_set-__prefix__-tags" name="memolinkmodel_set-__prefix__-tags" type="text"/>
|
|
333
|
+
</div>
|
|
334
|
+
</td>
|
|
335
|
+
<td class="field-name">
|
|
336
|
+
<input class="vTextField" id="id_memolinkmodel_set-__prefix__-name" maxlength="255" name="memolinkmodel_set-__prefix__-name" type="text"/>
|
|
337
|
+
</td>
|
|
338
|
+
<td class="field-url">
|
|
339
|
+
<input class="vURLField" id="id_memolinkmodel_set-__prefix__-url" maxlength="200" name="memolinkmodel_set-__prefix__-url" type="url"/>
|
|
340
|
+
</td>
|
|
341
|
+
<td class="field-position hidden">
|
|
342
|
+
<input class="_reorder_" id="id_memolinkmodel_set-__prefix__-position" name="memolinkmodel_set-__prefix__-position" type="hidden" value="0"/>
|
|
343
|
+
</td>
|
|
344
|
+
<td class="delete">
|
|
345
|
+
</td>
|
|
346
|
+
</tr>
|
|
347
|
+
</tbody>
|
|
348
|
+
</table>
|
|
349
|
+
</fieldset>
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
<div class="submit-row">
|
|
353
|
+
<input class="default" name="_save" type="submit" value="Save"/>
|
|
354
|
+
<input name="_addanother" type="submit" value="Save and add another"/>
|
|
355
|
+
<input name="_continue" type="submit" value="Save and continue editing"/>
|
|
356
|
+
</div>
|
|
357
|
+
<script async="" data-model-name="memomodel" id="django-admin-form-add-constants" src="/static/admin/js/change_form.js">
|
|
358
|
+
</script>
|
|
359
|
+
<script data-prepopulated-fields="[]" id="django-admin-prepopulated-fields-constants" src="/static/admin/js/prepopulate_init.js">
|
|
360
|
+
</script>
|
|
361
|
+
</div>
|
|
362
|
+
</form>
|
|
363
|
+
</div>
|
|
364
|
+
<br class="clear"/>
|
|
365
|
+
</div>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
|
|
2
|
+
Documented commands (use 'help -v' for verbose/'help <topic>' for details):
|
|
3
|
+
|
|
4
|
+
adminsortable2
|
|
5
|
+
==============
|
|
6
|
+
reorder
|
|
7
|
+
|
|
8
|
+
ckeditor_uploader
|
|
9
|
+
=================
|
|
10
|
+
generateckeditorthumbnails
|
|
11
|
+
|
|
12
|
+
dbbackup
|
|
13
|
+
========
|
|
14
|
+
dbbackup dbrestore listbackups mediabackup mediarestore
|
|
15
|
+
|
|
16
|
+
django.contrib.auth
|
|
17
|
+
===================
|
|
18
|
+
changepassword createsuperuser
|
|
19
|
+
|
|
20
|
+
django.contrib.contenttypes
|
|
21
|
+
===========================
|
|
22
|
+
remove_stale_contenttypes
|
|
23
|
+
|
|
24
|
+
django.contrib.sessions
|
|
25
|
+
=======================
|
|
26
|
+
clearsessions
|
|
27
|
+
|
|
28
|
+
django.contrib.staticfiles
|
|
29
|
+
==========================
|
|
30
|
+
collectstatic findstatic runserver
|
|
31
|
+
|
|
32
|
+
django.core
|
|
33
|
+
===========
|
|
34
|
+
check flush optimizemigration squashmigrations
|
|
35
|
+
compilemessages inspectdb sendtestemail startapp
|
|
36
|
+
createcachetable loaddata showmigrations startproject
|
|
37
|
+
dbshell makemessages sqlflush test
|
|
38
|
+
diffsettings makemigrations sqlmigrate testserver
|
|
39
|
+
dumpdata migrate sqlsequencereset
|
|
40
|
+
|
|
41
|
+
inventory
|
|
42
|
+
=========
|
|
43
|
+
seed_data tree
|
|
44
|
+
|
|
45
|
+
manage_django_project
|
|
46
|
+
=====================
|
|
47
|
+
code_style install publish safety update_req
|
|
48
|
+
coverage project_info run_dev_server tox
|
|
49
|
+
|
|
50
|
+
reversion
|
|
51
|
+
=========
|
|
52
|
+
createinitialrevisions deleterevisions
|
|
53
|
+
|
|
54
|
+
tagulous
|
|
55
|
+
========
|
|
56
|
+
initial_tags
|
|
57
|
+
|
|
58
|
+
Uncategorized
|
|
59
|
+
=============
|
|
60
|
+
alias help history macro quit set shortcuts
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import django
|
|
2
|
+
from bx_py_utils.test_utils.snapshot import assert_text_snapshot
|
|
3
|
+
from manage_django_project.tests.cmd2_test_utils import BaseShellTestCase
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class PyInventoryDevShellTestCase(BaseShellTestCase):
|
|
7
|
+
def test_help(self):
|
|
8
|
+
stdout, stderr = self.execute(command='help')
|
|
9
|
+
self.assertEqual(stderr, '')
|
|
10
|
+
self.assertIn('Documented commands', stdout)
|
|
11
|
+
|
|
12
|
+
# Django commands:
|
|
13
|
+
self.assertIn('django.core', stdout)
|
|
14
|
+
self.assertIn('makemessages', stdout)
|
|
15
|
+
self.assertIn('makemigrations', stdout)
|
|
16
|
+
|
|
17
|
+
# manage_django_project:
|
|
18
|
+
self.assertIn('manage_django_project', stdout)
|
|
19
|
+
self.assertIn('run_dev_server', stdout)
|
|
20
|
+
|
|
21
|
+
# Own commands:
|
|
22
|
+
self.assertIn('inventory', stdout)
|
|
23
|
+
self.assertIn('seed_data', stdout)
|
|
24
|
+
self.assertIn('tree', stdout)
|
|
25
|
+
|
|
26
|
+
assert_text_snapshot(got=stdout, snapshot_name=f'test_command_shell_help_django{django.__version__}')
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import io
|
|
2
|
+
|
|
3
|
+
from django.core import management
|
|
4
|
+
from django.core.management.commands import makemigrations
|
|
5
|
+
from django.test import TestCase, override_settings
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestMigrations(TestCase):
|
|
9
|
+
databases = [
|
|
10
|
+
'default',
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
@override_settings(MIGRATION_MODULES={})
|
|
14
|
+
def test_missing_migrations(self):
|
|
15
|
+
output = io.StringIO()
|
|
16
|
+
try:
|
|
17
|
+
management.call_command(
|
|
18
|
+
makemigrations.Command(), dry_run=True, check_changes=True, verbosity=1, stdout=output
|
|
19
|
+
)
|
|
20
|
+
except SystemExit as err:
|
|
21
|
+
if err.code != 0:
|
|
22
|
+
raise AssertionError(output.getvalue())
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from ckeditor.widgets import CKEditorWidget
|
|
2
|
+
from ckeditor_uploader.fields import RichTextUploadingField, RichTextUploadingFormField
|
|
3
|
+
from django.forms import modelform_factory
|
|
4
|
+
from django.test import TestCase
|
|
5
|
+
|
|
6
|
+
from inventory.models import ItemModel
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ItemModelTestCase(TestCase):
|
|
10
|
+
def test_item_description_ckeditor(self):
|
|
11
|
+
item = ItemModel()
|
|
12
|
+
opts = item._meta
|
|
13
|
+
model_description_field = opts.get_field('description')
|
|
14
|
+
self.assertIsInstance(model_description_field, RichTextUploadingField)
|
|
15
|
+
|
|
16
|
+
def test_item_description_form_ckeditor(self):
|
|
17
|
+
ItemForm = modelform_factory(ItemModel, fields=('description',))
|
|
18
|
+
form = ItemForm()
|
|
19
|
+
form_field = form.fields['description']
|
|
20
|
+
self.assertIsInstance(form_field, RichTextUploadingFormField)
|
|
21
|
+
widget = form_field.widget
|
|
22
|
+
|
|
23
|
+
# Note: django-ckeditor 6.3.1-6.3.x broke the widget loading:
|
|
24
|
+
self.assertIsInstance(widget, CKEditorWidget)
|