From d1d079e2c39402ce6c41f0e10778f0d29324feb0 Mon Sep 17 00:00:00 2001 From: freyamade Date: Tue, 6 May 2025 11:24:10 +0200 Subject: [PATCH 1/8] add tests for what I assume is the issue from toonie --- backend/api/tests/test_bis_list.py | 98 ++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+) diff --git a/backend/api/tests/test_bis_list.py b/backend/api/tests/test_bis_list.py index 938d4dd6..b8209ba7 100644 --- a/backend/api/tests/test_bis_list.py +++ b/backend/api/tests/test_bis_list.py @@ -245,6 +245,51 @@ def test_create_with_sync(self): self.assertEqual(sync_bis.current_body_id, self.gear_id_map['Radiant Host']) self.assertEqual(non_sync_bis.current_body_id, self.gear_id_map['Moonward']) + def test_create_with_ring_swap(self): + """ + Create a new BIS List for the character + """ + url = reverse('api:bis_collection', kwargs={'character_id': self.char.pk}) + self.client.force_authenticate(self.char.user) + + # Try one with PLD first + data = { + 'job_id': 'PLD', + 'bis_mainhand_id': self.gear_id_map['Augmented Historia'], + 'bis_offhand_id': self.gear_id_map['Augmented Historia'], + 'bis_head_id': self.gear_id_map['Babyface Champion'], + 'bis_body_id': self.gear_id_map['Augmented Historia'], + 'bis_hands_id': self.gear_id_map['Babyface Champion'], + 'bis_legs_id': self.gear_id_map['Babyface Champion'], + 'bis_feet_id': self.gear_id_map['Babyface Champion'], + 'bis_earrings_id': self.gear_id_map['Augmented Historia'], + 'bis_necklace_id': self.gear_id_map['Augmented Historia'], + 'bis_bracelet_id': self.gear_id_map['Augmented Historia'], + 'bis_right_ring_id': self.gear_id_map['Augmented Historia'], + 'bis_left_ring_id': self.gear_id_map['Babyface Champion'], + 'current_mainhand_id': self.gear_id_map['Ceremonial'], + 'current_offhand_id': self.gear_id_map['Ceremonial'], + 'current_head_id': self.gear_id_map['Ceremonial'], + 'current_body_id': self.gear_id_map['Ceremonial'], + 'current_hands_id': self.gear_id_map['Ceremonial'], + 'current_legs_id': self.gear_id_map['Ceremonial'], + 'current_feet_id': self.gear_id_map['Ceremonial'], + 'current_earrings_id': self.gear_id_map['Ceremonial'], + 'current_necklace_id': self.gear_id_map['Ceremonial'], + 'current_bracelet_id': self.gear_id_map['Ceremonial'], + 'current_right_ring_id': self.gear_id_map['Babyface Champion'], + 'current_left_ring_id': self.gear_id_map['Ceremonial'], + 'external_link': '', + 'name': 'Swap the Rings', + } + + response = self.client.post(url, data) + self.assertEqual(response.status_code, status.HTTP_201_CREATED, response.content) + self.assertEqual(BISList.objects.count(), 1) + obj = BISList.objects.first() + # Ensure that the request to create has swapped the rings to the correct location + self.assertEqual(obj.bis_left_ring_id, obj.current_left_ring_id) + def test_404(self): """ Test all situations where the endpoint would respond with a 404; @@ -429,6 +474,59 @@ def test_update_400(self): self.assertEqual(content['current_mainhand_id'], [invalid_gear]) self.assertEqual(content['name'], ['Ensure this field has no more than 64 characters.']) + def test_update_with_ring_swap(self): + """ + Update the existing BIS List with a PUT request + """ + url = reverse('api:bis_resource', kwargs={'character_id': self.char.pk, 'pk': self.bis.pk}) + self.client.force_authenticate(self.char.user) + + # Get modern gear for the update + tome_gear = Gear.objects.get(name='Augmented Historia').pk + raid_gear = Gear.objects.get(name='Babyface Champion', has_weapon=False).pk + crafted_gear = Gear.objects.get(name='Ceremonial').pk + + # Send an update request with the rings swapped, ensure they are bis when the request is finished + data = { + 'job_id': 'PLD', + 'bis_mainhand_id': tome_gear, + 'bis_offhand_id': tome_gear, + 'bis_head_id': raid_gear, + 'bis_body_id': tome_gear, + 'bis_hands_id': raid_gear, + 'bis_legs_id': raid_gear, + 'bis_feet_id': raid_gear, + 'bis_earrings_id': tome_gear, + 'bis_necklace_id': tome_gear, + 'bis_bracelet_id': tome_gear, + 'bis_right_ring_id': tome_gear, + 'bis_left_ring_id': raid_gear, + 'current_mainhand_id': crafted_gear, + 'current_offhand_id': crafted_gear, + 'current_head_id': crafted_gear, + 'current_body_id': crafted_gear, + 'current_hands_id': crafted_gear, + 'current_legs_id': crafted_gear, + 'current_feet_id': crafted_gear, + 'current_earrings_id': crafted_gear, + 'current_necklace_id': crafted_gear, + 'current_bracelet_id': crafted_gear, + 'current_right_ring_id': raid_gear, + 'current_left_ring_id': tome_gear, + 'external_link': None, + 'name': 'Update ring swap c:', + } + + response = self.client.put(url, data) + self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT, response.content) + + # Assert the rings are correct + self.bis.refresh_from_db() + self.assertEqual(self.bis.bis_right_ring_id, tome_gear) + self.assertEqual(self.bis.bis_left_ring_id, raid_gear) + self.assertEqual(self.bis.bis_right_ring_id, self.bis.current_right_ring_id) + self.assertEqual(self.bis.bis_left_ring_id, self.bis.current_left_ring_id) + def test_404(self): """ Test all situations where the endpoint would respond with a 404; From 94c3db55d1b4555deeb27674635e300b4a7c1c81 Mon Sep 17 00:00:00 2001 From: freyamade Date: Tue, 6 May 2025 11:43:52 +0200 Subject: [PATCH 2/8] implement ring swapping in the bislist pre_save hook --- backend/api/models/bis_list.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/backend/api/models/bis_list.py b/backend/api/models/bis_list.py index b81b68b4..e19751b0 100644 --- a/backend/api/models/bis_list.py +++ b/backend/api/models/bis_list.py @@ -7,6 +7,7 @@ import auto_prefetch from django.db import models from django.db.models import Q +from django.dispatch import receiver class BISList(auto_prefetch.Model): @@ -181,3 +182,29 @@ def needs_armour_augments(gear_name: str) -> models.QuerySet: | (Q(bis_legs__name=gear_name) & ~Q(current_legs__name=gear_name)) | (Q(bis_feet__name=gear_name) & ~Q(current_feet__name=gear_name)), ) + + +@receiver(models.signals.pre_save, sender=BISList) +def bis_list_ring_swap(sender, instance: BISList, *args, **kwargs): + """ + Check if either of the current_ring slots on the to-be-saved BIS List need to be swapped. + Swapping is required if one of the bis slots matches the opposite current slot and doesn't match its own. + """ + needs_swapping = False + if ( + instance.bis_right_ring_id != instance.current_right_ring_id + and instance.bis_right_ring_id == instance.current_left_ring_id + ): + needs_swapping = True + + if ( + instance.bis_left_ring_id != instance.current_left_ring_id + and instance.bis_left_ring_id == instance.current_right_ring_id + ): + needs_swapping = True + + if not needs_swapping: + return + + # Swap the current rings to make it work + instance.current_left_ring, instance.current_right_ring = instance.current_right_ring, instance.current_left_ring From 7a9e41a85172e790a11d9e051f1242c44ab7209d Mon Sep 17 00:00:00 2001 From: freyamade Date: Tue, 6 May 2025 11:50:54 +0200 Subject: [PATCH 3/8] add a little message to the bottom of the loot solver if the user isn't using the PFLM to inform that it would help and link to settings --- frontend/src/components/loot/solver.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/components/loot/solver.vue b/frontend/src/components/loot/solver.vue index 33ea029d..7998a824 100644 --- a/frontend/src/components/loot/solver.vue +++ b/frontend/src/components/loot/solver.vue @@ -203,6 +203,9 @@ + +
+

If you would like to auto-fill the Loot for a fight, try enabling the "Per Fight Loot Manager" in your Settings page!

From 16d28c1cf7bac0c3e9785fd9c869c5cd7d5daebf Mon Sep 17 00:00:00 2001 From: freyamade Date: Tue, 6 May 2025 11:54:43 +0200 Subject: [PATCH 4/8] update changelog --- frontend/src/components/modals/changelog.vue | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/modals/changelog.vue b/frontend/src/components/modals/changelog.vue index b4791c5d..48148f54 100644 --- a/frontend/src/components/modals/changelog.vue +++ b/frontend/src/components/modals/changelog.vue @@ -14,12 +14,14 @@

{{ version }}

expand_more Quality of Life expand_more

- Team Leaders can now also send Lodestone Update requests via the Team Page for other Team Members, in case people are forgetting to update their BIS. -

  • Thanks toonie for the suggestion!
+ BIS List updates will now automatically swap the current ring slots if, for example, the current left ring matches the bis right ring. +
    +
  • This should hopefully make the Loot Solver / Manager break less often.
  • +
  • This deployment has also re-saved every BIS List in the system to automatically update the lists for everyone.
  • +
  • Thanks toonie for the suggestion!
  • +

- -
expand_more Fixes expand_more
-

Improved error handling in the LootSolver when the Team's Tier mismatched with the BIS Lists in it.

+

Added a message to the Loot Solver that links to the Settings page if you are not using the Per-Fight Loot Manager, just in case you want to use auto-assign but don't know how.

From 1f82250333fc0820f89461de63cd8de962b68684 Mon Sep 17 00:00:00 2001 From: freyamade Date: Tue, 6 May 2025 18:27:42 +0200 Subject: [PATCH 5/8] add a command to call .save() on every bis list --- .../management/commands/save_all_bis_lists.py | 10 ++++ backend/api/tests/test_management.py | 59 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 backend/api/management/commands/save_all_bis_lists.py diff --git a/backend/api/management/commands/save_all_bis_lists.py b/backend/api/management/commands/save_all_bis_lists.py new file mode 100644 index 00000000..657d8f73 --- /dev/null +++ b/backend/api/management/commands/save_all_bis_lists.py @@ -0,0 +1,10 @@ +from django.core.management.base import BaseCommand +from api.models import BISList + + +class Command(BaseCommand): + help = 'Call .save() on all BISLists to handle the ring swapping.' + + def handle(self, *args, **options): + for obj in BISList.objects.all(): + obj.save() diff --git a/backend/api/tests/test_management.py b/backend/api/tests/test_management.py index 0ad56770..ef7e065e 100644 --- a/backend/api/tests/test_management.py +++ b/backend/api/tests/test_management.py @@ -132,3 +132,62 @@ def test_notification_setup(self): self.assertTrue('verify_success' in settings.notifications) self.assertTrue(settings.notifications['verify_success']) self.assertFalse(settings.notifications['verify_fail']) + + def test_save_bis_list_ring_swap(self): + """ + Create a BISList with rings swapped, then run the management command + """ + call_command('seed', stdout=StringIO()) + char = models.Character.objects.create( + avatar_url='https://img.savageaim.com/abcde', + lodestone_id=1234567890, + user=self._get_user(), + name='Team Lead', + verified=True, + world='Lich', + ) + + # Next, create two BIS lists for each character + raid_weapon = models.Gear.objects.get(item_level=605, name='Asphodelos') + raid_gear = models.Gear.objects.get(item_level=600, has_weapon=False) + tome_gear = models.Gear.objects.get(item_level=600, has_weapon=True) + crafted = models.Gear.objects.get(name='Classical') + bis = models.BISList.objects.create( + bis_body=raid_gear, + bis_bracelet=raid_gear, + bis_earrings=raid_gear, + bis_feet=raid_gear, + bis_hands=tome_gear, + bis_head=tome_gear, + bis_legs=tome_gear, + bis_mainhand=raid_weapon, + bis_necklace=tome_gear, + bis_offhand=raid_weapon, + bis_left_ring=tome_gear, + bis_right_ring=raid_gear, + current_body=crafted, + current_bracelet=crafted, + current_earrings=crafted, + current_feet=crafted, + current_hands=crafted, + current_head=crafted, + current_legs=crafted, + current_mainhand=crafted, + current_necklace=crafted, + current_offhand=crafted, + current_left_ring=crafted, + current_right_ring=tome_gear, + job_id='SGE', + owner=char, + ) + models.BISList.objects.update( + current_right_ring_id=tome_gear.id, + current_left_ring_id=crafted.id, + ) + bis.refresh_from_db() + self.assertNotEqual(bis.current_right_ring_id, crafted.id) + self.assertNotEqual(bis.current_left_ring_id, tome_gear.id) + call_command('save_all_bis_lists') + bis.refresh_from_db() + self.assertEqual(bis.current_right_ring_id, crafted.id) + self.assertEqual(bis.current_left_ring_id, tome_gear.id) From 59d0ea7304cb8b8cf10fee9d1c615b98da50bfd7 Mon Sep 17 00:00:00 2001 From: freyamade Date: Tue, 6 May 2025 18:27:54 +0200 Subject: [PATCH 6/8] finalise changelog update --- frontend/src/components/modals/changelog.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/modals/changelog.vue b/frontend/src/components/modals/changelog.vue index 48148f54..0fd67b91 100644 --- a/frontend/src/components/modals/changelog.vue +++ b/frontend/src/components/modals/changelog.vue @@ -17,7 +17,7 @@ BIS List updates will now automatically swap the current ring slots if, for example, the current left ring matches the bis right ring.
  • This should hopefully make the Loot Solver / Manager break less often.
  • -
  • This deployment has also re-saved every BIS List in the system to automatically update the lists for everyone.
  • +
  • This deployment has also re-saved every BIS List in the system to automatically update the lists for everyone. (I really hope this hasn't broken anything...)
  • Thanks toonie for the suggestion!

From 630215ddd22cb85aef385a7534dbb3ba6ff30f56 Mon Sep 17 00:00:00 2001 From: freyamade Date: Tue, 6 May 2025 18:28:15 +0200 Subject: [PATCH 7/8] bump version --- backend/backend/__init__.py | 2 +- frontend/.env | 2 +- frontend/src/main.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/backend/__init__.py b/backend/backend/__init__.py index 88e86fcd..4b3f510e 100644 --- a/backend/backend/__init__.py +++ b/backend/backend/__init__.py @@ -2,4 +2,4 @@ from .celery import app as celery_app -VERSION = '20250417' +VERSION = '20250506' diff --git a/frontend/.env b/frontend/.env index 7586fb24..a99a58f8 100644 --- a/frontend/.env +++ b/frontend/.env @@ -1 +1 @@ -VUE_APP_VERSION="20250417" +VUE_APP_VERSION="20250506" diff --git a/frontend/src/main.ts b/frontend/src/main.ts index c7c48d40..66612cab 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -30,7 +30,7 @@ Sentry.init({ Vue, dsn: 'https://06f41b525a40497a848fb726f6d03244@o242258.ingest.sentry.io/6180221', logErrors: true, - release: 'savageaim@20250417', + release: 'savageaim@20250506', integrations: [ Sentry.browserTracingIntegration(), Sentry.replayIntegration(), From c901cf07814d71da2b5865efef1a5c21842b251e Mon Sep 17 00:00:00 2001 From: freyamade Date: Tue, 6 May 2025 18:31:19 +0200 Subject: [PATCH 8/8] lint python --- backend/api/models/bis_list.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/api/models/bis_list.py b/backend/api/models/bis_list.py index e19751b0..2c34287a 100644 --- a/backend/api/models/bis_list.py +++ b/backend/api/models/bis_list.py @@ -192,13 +192,13 @@ def bis_list_ring_swap(sender, instance: BISList, *args, **kwargs): """ needs_swapping = False if ( - instance.bis_right_ring_id != instance.current_right_ring_id + instance.bis_right_ring_id != instance.current_right_ring_id and instance.bis_right_ring_id == instance.current_left_ring_id ): needs_swapping = True if ( - instance.bis_left_ring_id != instance.current_left_ring_id + instance.bis_left_ring_id != instance.current_left_ring_id and instance.bis_left_ring_id == instance.current_right_ring_id ): needs_swapping = True