From 0fd1390fe05474a848b56092549ab4dbeb3a70cd Mon Sep 17 00:00:00 2001 From: Oliver <oliver.henry.walters@gmail.com> Date: Sat, 22 Oct 2022 12:45:50 +1100 Subject: [PATCH 1/4] Workflow fix (#3830) * Specify minimum python version for docker build workflow (cherry picked from commit 18c55b30b81aef66c131a960586b52d655a621f9) * Specify python version for translation checker (cherry picked from commit 14360507f5c9d77ed8321755aab25a79fbcf10f9) * Disable social media workflow (has never worked, anyway) (cherry picked from commit 56fbcbeae25b165978d149aa484ae7335f8f67a4) --- .github/workflows/check_translations.yaml | 6 ++++++ .github/workflows/docker.yaml | 7 ++++++- .github/workflows/{release.yml => release.yml.disabled} | 0 3 files changed, 12 insertions(+), 1 deletion(-) rename .github/workflows/{release.yml => release.yml.disabled} (100%) diff --git a/.github/workflows/check_translations.yaml b/.github/workflows/check_translations.yaml index 910ecdda9b..b408ab5d74 100644 --- a/.github/workflows/check_translations.yaml +++ b/.github/workflows/check_translations.yaml @@ -21,10 +21,16 @@ jobs: INVENTREE_MEDIA_ROOT: ./media INVENTREE_STATIC_ROOT: ./static INVENTREE_BACKUP_DIR: ./backup + python_version: 3.9 steps: - name: Checkout Code uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v3.1.0 + - name: Set Up Python ${{ env.python_version }} + uses: actions/setup-python@13ae5bb136fac2878aff31522b9efb785519f984 # pin@v4.3.0 + with: + python-version: ${{ env.python_version }} + cache: 'pip' - name: Install Dependencies run: | sudo apt-get update diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml index 9d30cb9a79..f6be48d665 100644 --- a/.github/workflows/docker.yaml +++ b/.github/workflows/docker.yaml @@ -30,10 +30,15 @@ jobs: id-token: write env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + python_version: 3.9 steps: - name: Check out repo uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # pin@v3.1.0 + - name: Set Up Python ${{ env.python_version }} + uses: actions/setup-python@13ae5bb136fac2878aff31522b9efb785519f984 # pin@v4.3.0 + with: + python-version: ${{ env.python_version }} + cache: 'pip' - name: Version Check run: | pip install requests diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml.disabled similarity index 100% rename from .github/workflows/release.yml rename to .github/workflows/release.yml.disabled From 9d39d5b00f22e5eef7e0ffa6cb6a0f00b926a7f1 Mon Sep 17 00:00:00 2001 From: Oliver <oliver.henry.walters@gmail.com> Date: Sat, 22 Oct 2022 14:16:55 +1100 Subject: [PATCH 2/4] Improve redraw speed of build allocation table (#3831) - Reload table data in one go, rather than one row at a time - Reduces redraw time (in one example) from ~4s to ~0.05s --- InvenTree/templates/js/translated/build.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/InvenTree/templates/js/translated/build.js b/InvenTree/templates/js/translated/build.js index fa1ec5bab4..223372a767 100644 --- a/InvenTree/templates/js/translated/build.js +++ b/InvenTree/templates/js/translated/build.js @@ -1471,13 +1471,16 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { // How many rows are fully allocated? var allocated_rows = 0; - bom_items.forEach(function(row) { - $(table).bootstrapTable('updateByUniqueId', row.pk, row, true); + for (var idx = 0; idx < bom_items.length; idx++) { + var row = bom_items[idx]; if (isRowFullyAllocated(row)) { - allocated_rows += 1; + allocated_rows++; } - }); + } + + // Reload table data + $(table).bootstrapTable('load', bom_items); // Find the top-level progess bar for this build output var output_progress_bar = $(`#output-progress-${outputId}`); From c120de90ae2336978fb818c86c9850740c67973d Mon Sep 17 00:00:00 2001 From: Oliver <oliver.henry.walters@gmail.com> Date: Sat, 22 Oct 2022 18:56:23 +1100 Subject: [PATCH 3/4] Build table improvements (#3833) * Add extra columns to build order table * Optimize build table update * Improve loading speed of 'test results' in build output table --- InvenTree/templates/js/translated/build.js | 41 +++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/InvenTree/templates/js/translated/build.js b/InvenTree/templates/js/translated/build.js index 223372a767..13532db35b 100644 --- a/InvenTree/templates/js/translated/build.js +++ b/InvenTree/templates/js/translated/build.js @@ -1036,9 +1036,12 @@ function loadBuildOutputTable(build_info, options={}) { // Now that the allocations have been grouped by stock item, // we can update each row in the table, // using the pk value of each row (stock item) + + var data = []; + rows.forEach(function(row) { row.allocations = allocations[row.pk] || []; - $(table).bootstrapTable('updateByUniqueId', row.pk, row, true); + data.push(row); var n_completed_lines = 0; @@ -1066,6 +1069,9 @@ function loadBuildOutputTable(build_info, options={}) { } }); }); + + // Reload table with updated data + $(table).bootstrapTable('load', data); } } ); @@ -1108,6 +1114,7 @@ function loadBuildOutputTable(build_info, options={}) { { success: function(results) { + var data = []; // Iterate through each row and find matching test results rows.forEach(function(row) { var test_results = {}; @@ -1124,8 +1131,10 @@ function loadBuildOutputTable(build_info, options={}) { row.passed_tests = test_results; - $(table).bootstrapTable('updateByUniqueId', row.pk, row, true); + data.push(row); }); + + $(table).bootstrapTable('load', row); } } ); @@ -1466,7 +1475,6 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { function redrawAllocationData() { // Force a refresh of each row in the table // Note we cannot call 'refresh' because we are passing data from memory - // var rows = $(table).bootstrapTable('getData'); // How many rows are fully allocated? var allocated_rows = 0; @@ -1678,7 +1686,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { setupCallbacks(); }, sortable: true, - showColumns: false, + showColumns: true, detailView: true, detailFilter: function(index, row) { return allocatedQuantity(row) > 0; @@ -1809,6 +1817,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { field: 'sub_part_detail.full_name', title: '{% trans "Required Part" %}', sortable: true, + switchable: false, formatter: function(value, row) { var url = `/part/${row.sub_part}/`; var thumb = row.sub_part_detail.thumbnail; @@ -1833,16 +1842,37 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { field: 'reference', title: '{% trans "Reference" %}', sortable: true, + switchable: true, + }, + { + field: 'consumable', + title: '{% trans "Consumable" %}', + sortable: true, + switchable: true, + formatter: function(value) { + return yesNoLabel(value); + } + }, + { + field: 'optional', + title: '{% trans "Optional" %}', + sortable: true, + switchable: true, + formatter: function(value) { + return yesNoLabel(value); + } }, { field: 'quantity', title: '{% trans "Quantity Per" %}', sortable: true, + switchable: false, }, { field: 'available_stock', title: '{% trans "Available" %}', sortable: true, + switchable: true, formatter: function(value, row) { var url = `/part/${row.sub_part_detail.pk}/?display=part-stock`; @@ -1906,6 +1936,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { field: 'allocated', title: '{% trans "Allocated" %}', sortable: true, + switchable: false, formatter: function(value, row) { var required = requiredQuantity(row); var allocated = row.consumable ? required : allocatedQuantity(row); @@ -1946,6 +1977,8 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { { field: 'actions', title: '{% trans "Actions" %}', + switchable: false, + sortable: false, formatter: function(value, row) { if (row.consumable) { From a898ebce4038d5967c1e37503bb58121c469e9a8 Mon Sep 17 00:00:00 2001 From: Oliver <oliver.henry.walters@gmail.com> Date: Sat, 22 Oct 2022 18:56:38 +1100 Subject: [PATCH 4/4] Fix email notification setting (#3832) * Coerce setting value to a boolean * Ignore inactive users when sending notification emails * Only send UI notifications to active users * Fixes for unit tests --- InvenTree/build/test_build.py | 6 ++++-- InvenTree/common/notifications.py | 5 +++-- InvenTree/common/tests.py | 5 +++-- InvenTree/order/test_sales_order.py | 2 +- InvenTree/order/tests.py | 7 ++++++- .../plugin/builtin/integration/core_notifications.py | 8 +++++++- 6 files changed, 24 insertions(+), 9 deletions(-) diff --git a/InvenTree/build/test_build.py b/InvenTree/build/test_build.py index 23925eba17..6561e08903 100644 --- a/InvenTree/build/test_build.py +++ b/InvenTree/build/test_build.py @@ -557,11 +557,13 @@ class BuildTest(BuildTestBase): category='build.new_build', ) - self.assertEqual(messages.count(), 2) + self.assertEqual(messages.count(), 1) self.assertFalse(messages.filter(user__pk=2).exists()) - self.assertTrue(messages.filter(user__pk=3).exists()) + # Inactive users do not receive notifications + self.assertFalse(messages.filter(user__pk=3).exists()) + self.assertTrue(messages.filter(user__pk=4).exists()) diff --git a/InvenTree/common/notifications.py b/InvenTree/common/notifications.py index 1d4e7ae47b..0928fad6d0 100644 --- a/InvenTree/common/notifications.py +++ b/InvenTree/common/notifications.py @@ -243,8 +243,9 @@ class UIMessageNotification(SingleNotificationMethod): METHOD_NAME = 'ui_message' def get_targets(self): - """Just return the targets - no tricks here.""" - return self.targets + """Only send notifications for active users""" + + return [target for target in self.targets if target.is_active] def send(self, target): """Send a UI notification to a user.""" diff --git a/InvenTree/common/tests.py b/InvenTree/common/tests.py index 9720508d10..b305073045 100644 --- a/InvenTree/common/tests.py +++ b/InvenTree/common/tests.py @@ -778,7 +778,8 @@ class NotificationTest(InvenTreeAPITestCase): messages = NotificationMessage.objects.all() # As there are three staff users (including the 'test' user) we expect 30 notifications - self.assertEqual(messages.count(), 30) + # However, one user is marked as i nactive + self.assertEqual(messages.count(), 20) # Only 10 messages related to *this* user my_notifications = messages.filter(user=self.user) @@ -822,7 +823,7 @@ class NotificationTest(InvenTreeAPITestCase): # Only 7 notifications should have been deleted, # as the notifications associated with other users must remain untouched - self.assertEqual(NotificationMessage.objects.count(), 23) + self.assertEqual(NotificationMessage.objects.count(), 13) self.assertEqual(NotificationMessage.objects.filter(user=self.user).count(), 3) diff --git a/InvenTree/order/test_sales_order.py b/InvenTree/order/test_sales_order.py index 2d00f69ac6..98db41ff42 100644 --- a/InvenTree/order/test_sales_order.py +++ b/InvenTree/order/test_sales_order.py @@ -267,7 +267,7 @@ class SalesOrderTest(TestCase): category='order.overdue_sales_order', ) - self.assertEqual(len(messages), 2) + self.assertEqual(len(messages), 1) def test_new_so_notification(self): """Test that a notification is sent when a new SalesOrder is created. diff --git a/InvenTree/order/tests.py b/InvenTree/order/tests.py index 8c66b3a53a..4a758c3d6e 100644 --- a/InvenTree/order/tests.py +++ b/InvenTree/order/tests.py @@ -326,7 +326,12 @@ class OrderTest(TestCase): user__id=user_id, ) - self.assertTrue(messages.exists()) + # User ID 3 is inactive, and thus should not receive notifications + if user_id == 3: + self.assertFalse(messages.exists()) + continue + else: + self.assertTrue(messages.exists()) msg = messages.first() diff --git a/InvenTree/plugin/builtin/integration/core_notifications.py b/InvenTree/plugin/builtin/integration/core_notifications.py index d21dc8c46f..2176ab9feb 100644 --- a/InvenTree/plugin/builtin/integration/core_notifications.py +++ b/InvenTree/plugin/builtin/integration/core_notifications.py @@ -6,6 +6,7 @@ from django.utils.translation import gettext_lazy as _ from allauth.account.models import EmailAddress import common.models +import InvenTree.helpers import InvenTree.tasks from plugin import InvenTreePlugin from plugin.mixins import BulkNotificationMethod, SettingsMixin @@ -61,7 +62,12 @@ class CoreNotificationsPlugin(SettingsMixin, InvenTreePlugin): allowed_users = [] for user in self.targets: - allows_emails = self.usersetting(user) + + if not user.is_active: + # Ignore any users who have been deactivated + continue + + allows_emails = InvenTree.helpers.str2bool(self.usersetting(user)) if allows_emails: allowed_users.append(user)