Skip to content

Commit

Permalink
Bug fixes (#83)
Browse files Browse the repository at this point in the history
* second fight only requires 3 books for an accessory augment

* allow importing lodestone when there's slots missing

* trying to fix some weird proxy bugs

* update changelog

* bump

* fix loot solver tests
  • Loading branch information
freyamade authored Aug 27, 2024
1 parent 318786c commit 3e8c70b
Show file tree
Hide file tree
Showing 11 changed files with 123 additions and 61 deletions.
26 changes: 15 additions & 11 deletions backend/api/lodestone_scraper.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,17 +186,21 @@ def get_current_gear(self, character_id: str, expected_job: str):
if (slot_name == 'OFFHAND' and expected_job != 'PLD') or slot_name in IGNORED_SLOTS:
continue

class_list = soup.select_one(selectors['CLASS_LIST']['selector']).getText()
if expected_job not in class_list and class_list not in SPECIAL_ALLOWED_CLASSLISTS:
raise MismatchedJobError(class_list)

gear_name = soup.select_one(selectors['NAME']['selector']).getText()
item_level = int(soup.select_one(selectors['ITEM_LEVEL']['selector']).getText().split(' ')[-1])

if item_level > max_il:
max_il = item_level
if item_level < min_il:
min_il = item_level
# Do a test to see if the gear slot is available
class_list_el = soup.select_one(selectors['CLASS_LIST']['selector'])
gear_name = None
if class_list_el is not None:
class_list = soup.select_one(selectors['CLASS_LIST']['selector']).getText()
if expected_job not in class_list and class_list not in SPECIAL_ALLOWED_CLASSLISTS:
raise MismatchedJobError(class_list)

gear_name = soup.select_one(selectors['NAME']['selector']).getText()
item_level = int(soup.select_one(selectors['ITEM_LEVEL']['selector']).getText().split(' ')[-1])

if item_level > max_il:
max_il = item_level
if item_level < min_il:
min_il = item_level

slot_map[LODESTONE_TO_SA_NAME_MAP[slot_name]] = gear_name

Expand Down
84 changes: 70 additions & 14 deletions backend/api/tests/test_lodestone_gear_import.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,76 @@ def test_import(self):
# Build an expected data packet
expected = {
'job_id': 'GNB',
'mainhand': Gear.objects.get(name='Neo Kingdom').pk,
'offhand': Gear.objects.get(name='Neo Kingdom').pk,
'head': Gear.objects.get(name='Neo Kingdom', has_armour=True).pk,
'body': Gear.objects.get(name='Neo Kingdom', has_armour=True).pk,
'hands': Gear.objects.get(name='Neo Kingdom', has_armour=True).pk,
'legs': Gear.objects.get(name='Neo Kingdom', has_armour=True).pk,
'feet': Gear.objects.get(name='Neo Kingdom', has_armour=True).pk,
'earrings': Gear.objects.get(name='Neo Kingdom', has_accessories=True).pk,
'necklace': Gear.objects.get(name='Neo Kingdom', has_accessories=True).pk,
'bracelet': Gear.objects.get(name='Neo Kingdom', has_accessories=True).pk,
'right_ring': Gear.objects.get(name='Neo Kingdom', has_accessories=True).pk,
'left_ring': Gear.objects.get(name='Epochal', has_accessories=True).pk,
'min_il': 690,
'max_il': 700,
'mainhand': Gear.objects.get(name='Archeo Kingdom').pk,
'offhand': Gear.objects.get(name='Archeo Kingdom').pk,
'head': Gear.objects.get(name='Dark Horse Champion', has_armour=True).pk,
'body': Gear.objects.get(name='Augmented Quetzalli', has_armour=True).pk,
'hands': Gear.objects.get(name='Quetzalli', has_armour=True).pk,
'legs': Gear.objects.get(name='Archeo Kingdom', has_armour=True).pk,
'feet': Gear.objects.get(name='Archeo Kingdom', has_armour=True).pk,
'earrings': Gear.objects.get(name='Dark Horse Champion', has_accessories=True).pk,
'necklace': Gear.objects.get(name='Dark Horse Champion', has_accessories=True).pk,
'bracelet': Gear.objects.get(name='Augmented Quetzalli', has_accessories=True).pk,
'right_ring': Gear.objects.get(name='Archeo Kingdom', has_accessories=True).pk,
'left_ring': Gear.objects.get(name='Dark Horse Champion', has_accessories=True).pk,
'min_il': 710,
'max_il': 730,
}
self.maxDiff = None
self.assertDictEqual(response.json(), expected)

def test_import_with_missing_gear(self):
"""
Test Plan;
- Create a Job and Gear for starting gear so that the import works out properly.
- Import a character with missing gear slots, ensure that the API doesn't break.
"""
Job.objects.create(name='Pugilist', id='PGL', role='dps', ordering=24)
lalafellin = Gear.objects.create(
name='Lalafellin',
item_level=5,
has_armour=True,
has_accessories=False,
has_weapon=False,
).pk
weathered_acc = Gear.objects.create(
name='Weathered',
item_level=5,
has_armour=False,
has_accessories=True,
has_weapon=False,
).pk
weathered = Gear.objects.create(
name='Weathered',
item_level=1,
has_armour=False,
has_accessories=False,
has_weapon=True,
).pk

url = reverse('api:lodestone_gear_import', kwargs={'character_id': '47800977', 'expected_job': 'PGL'})
user = self._get_user()
self.client.force_authenticate(user)
response = self.client.get(url)
self.assertEqual(response.status_code, status.HTTP_200_OK, response.json())

# Build an expected data packet
expected = {
'job_id': 'PGL',
'mainhand': weathered,
'offhand': weathered,
'head': -1,
'body': lalafellin,
'hands': lalafellin,
'legs': lalafellin,
'feet': lalafellin,
'earrings': weathered_acc,
'necklace': weathered_acc,
'bracelet': weathered_acc,
'right_ring': -1,
'left_ring': weathered_acc,
'min_il': 1,
'max_il': 5,
}
self.maxDiff = None
self.assertDictEqual(response.json(), expected)
Expand Down
42 changes: 16 additions & 26 deletions backend/api/tests/test_loot_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,12 +625,10 @@ def test_whole_view(self):
self.assertDictEqual(first_floor_expected[i], first_floor_received[i], f'{i+1}/{len(first_floor_received)}')

second_floor_expected = [
{'token': False, 'Head': self.tm5.id, 'Hands': self.tm1.id, 'Feet': self.tm3.id, 'Tome Accessory Augment': self.tm8.id},
{'token': True, 'Head': self.tm1.id, 'Hands': None, 'Feet': self.tm5.id, 'Tome Accessory Augment': self.tm2.id},
{'token': True, 'Head': self.tm5.id, 'Hands': self.tm1.id, 'Feet': self.tm3.id, 'Tome Accessory Augment': self.tm8.id},
{'token': False, 'Head': self.tm1.id, 'Hands': None, 'Feet': self.tm5.id, 'Tome Accessory Augment': self.tm2.id},
{'token': False, 'Head': self.tm7.id, 'Hands': None, 'Feet': self.tm6.id, 'Tome Accessory Augment': self.tm1.id},
{'token': False, 'Head': self.tm8.id, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm4.id},
{'token': False, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm3.id},
{'token': True, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm2.id},
{'token': True, 'Head': self.tm8.id, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm4.id},
]
second_floor_received = content['second_floor']
self.assertEqual(len(second_floor_expected), len(second_floor_received), second_floor_received)
Expand Down Expand Up @@ -781,12 +779,10 @@ def test_whole_view_split_loot(self):

second_floor_expected = [
{'token': False, 'Head': self.tm5.id, 'Hands': self.tm1.id, 'Feet': self.tm3.id, 'Tome Accessory Augment': self.tm8.id},
{'token': False, 'Head': self.tm1.id, 'Hands': None, 'Feet': self.tm5.id, 'Tome Accessory Augment': self.tm2.id},
{'token': True, 'Head': self.tm7.id, 'Hands': None, 'Feet': self.tm6.id, 'Tome Accessory Augment': self.tm1.id},
{'token': True, 'Head': self.tm1.id, 'Hands': None, 'Feet': self.tm5.id, 'Tome Accessory Augment': self.tm2.id},
{'token': False, 'Head': self.tm7.id, 'Hands': None, 'Feet': self.tm6.id, 'Tome Accessory Augment': self.tm1.id},
{'token': False, 'Head': self.tm8.id, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm4.id},
{'token': False, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm3.id},
{'token': False, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm2.id},
{'token': True, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm5.id},
{'token': True, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm3.id},
]
second_floor_received = content['second_floor']
self.assertEqual(len(second_floor_expected), len(second_floor_received), second_floor_received)
Expand Down Expand Up @@ -853,12 +849,10 @@ def test_solver_sort_overrides(self):
second_floor_expected = [
{'token': False, 'Head': self.tm2.id, 'Hands': self.tm7.id, 'Feet': self.tm8.id, 'Tome Accessory Augment': self.tm1.id},
{'token': False, 'Head': self.tm3.id, 'Hands': self.tm2.id, 'Feet': self.tm5.id, 'Tome Accessory Augment': self.tm8.id},
{'token': False, 'Head': self.tm7.id, 'Hands': self.tm1.id, 'Feet': self.tm6.id, 'Tome Accessory Augment': self.tm4.id},
{'token': True, 'Head': self.tm8.id, 'Hands': None, 'Feet': self.tm3.id, 'Tome Accessory Augment': self.tm2.id},
{'token': True, 'Head': self.tm7.id, 'Hands': self.tm1.id, 'Feet': self.tm6.id, 'Tome Accessory Augment': self.tm4.id},
{'token': False, 'Head': self.tm8.id, 'Hands': None, 'Feet': self.tm3.id, 'Tome Accessory Augment': self.tm2.id},
{'token': False, 'Head': self.tm5.id, 'Hands': None, 'Feet': self.tm4.id, 'Tome Accessory Augment': self.tm7.id},
{'token': False, 'Head': self.tm1.id, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm6.id},
{'token': False, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm2.id},
{'token': True, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm3.id},
{'token': True, 'Head': self.tm1.id, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm6.id},
]

second_floor_received = content['second_floor']
Expand Down Expand Up @@ -1027,12 +1021,10 @@ def test_handling_non_drop_gear(self):

# Also run the second floor function and check that everything is returned in the order we expect
second_floor_expected = [
{'token': False, 'Head': self.tm8.id, 'Hands': self.tm1.id, 'Feet': self.tm5.id, 'Tome Accessory Augment': self.tm2.id},
{'token': True, 'Head': self.tm1.id, 'Hands': None, 'Feet': self.tm3.id, 'Tome Accessory Augment': self.tm7.id},
{'token': True, 'Head': self.tm8.id, 'Hands': self.tm1.id, 'Feet': self.tm5.id, 'Tome Accessory Augment': self.tm2.id},
{'token': False, 'Head': self.tm1.id, 'Hands': None, 'Feet': self.tm3.id, 'Tome Accessory Augment': self.tm7.id},
{'token': False, 'Head': self.tm5.id, 'Hands': None, 'Feet': self.tm6.id, 'Tome Accessory Augment': self.tm8.id},
{'token': False, 'Head': self.tm7.id, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm1.id},
{'token': False, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm4.id},
{'token': True, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm2.id},
{'token': True, 'Head': self.tm7.id, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm1.id},
]
second_floor_received = LootSolver._get_second_floor_data(LootSolver._get_requirements_map(self.team), Loot.objects.all(), id_order, received)
self.assertEqual(len(second_floor_expected), len(second_floor_received), second_floor_received)
Expand Down Expand Up @@ -1438,12 +1430,10 @@ def test_whole_view(self):
second_floor_expected = [
{'token': False, 'Head': self.tm6.id, 'Hands': self.tm1.id, 'Feet': self.tm7.id, 'Tome Accessory Augment': self.tm5.id},
{'token': False, 'Head': self.tm1.id, 'Hands': None, 'Feet': self.tm6.id, 'Tome Accessory Augment': self.tm7.id},
{'token': False, 'Head': self.tm2.id, 'Hands': None, 'Feet': self.tm8.id, 'Tome Accessory Augment': self.tm3.id},
{'token': True, 'Head': self.tm7.id, 'Hands': None, 'Feet': self.tm4.id, 'Tome Accessory Augment': self.tm6.id},
{'token': True, 'Head': self.tm2.id, 'Hands': None, 'Feet': self.tm8.id, 'Tome Accessory Augment': self.tm3.id},
{'token': False, 'Head': self.tm7.id, 'Hands': None, 'Feet': self.tm4.id, 'Tome Accessory Augment': self.tm6.id},
{'token': False, 'Head': None, 'Hands': None, 'Feet': self.tm5.id, 'Tome Accessory Augment': self.tm1.id},
{'token': False, 'Head': None, 'Hands': None, 'Feet': self.tm3.id, 'Tome Accessory Augment': self.tm2.id},
{'token': False, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm8.id},
{'token': True, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm6.id},
{'token': True, 'Head': None, 'Hands': None, 'Feet': self.tm3.id, 'Tome Accessory Augment': self.tm2.id},
]
second_floor_received = content['second_floor']
self.assertEqual(len(second_floor_expected), len(second_floor_received), second_floor_received)
Expand Down Expand Up @@ -1519,7 +1509,7 @@ def test_for_single_person_requiring_loot(self):
second_floor_expected = [
{'token': False, 'Head': self.tm1.id, 'Hands': self.tm1.id, 'Feet': None, 'Tome Accessory Augment': self.tm1.id},
{'token': False, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm1.id},
{'token': False, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm1.id},
{'token': True, 'Head': None, 'Hands': None, 'Feet': None, 'Tome Accessory Augment': self.tm1.id},
]
second_floor_received = content['second_floor']
self.assertEqual(len(second_floor_expected), len(second_floor_received), second_floor_received)
Expand Down
8 changes: 7 additions & 1 deletion backend/api/views/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,18 @@ class ImportAPIView(APIView):
ACCESSORY_SLOTS = {'earrings', 'necklace', 'bracelet', 'left_ring', 'right_ring'}

@staticmethod
def _get_gear_id(gear_selection: Dict[str, str], item_name: str) -> str:
def _get_gear_id(gear_selection: Dict[str, str], item_name: Optional[str]) -> str:
"""
Find the id of the gear piece that matches the name closest.
If item_name is None, return -1 for the dropdown option.
Check the extra_import_classes for distance also
However, if item_name is present in extra_import_names, immediately return the id
"""
if item_name is None:
return -1

diff = float('inf')
gear_id = None
for details in gear_selection:
Expand Down
2 changes: 1 addition & 1 deletion backend/api/views/loot_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class LootSolver(APIView):
SECOND_FLOOR_SLOTS = ['head', 'hands', 'feet', 'tome-accessory-augment']
THIRD_FLOOR_SLOTS = ['body', 'legs', 'tome-armour-augment', ]
FIRST_FLOOR_TOKENS = 3
SECOND_FLOOR_TOKENS = 4
SECOND_FLOOR_TOKENS = 3
THIRD_FLOOR_TOKENS = 4

@staticmethod
Expand Down
2 changes: 1 addition & 1 deletion backend/backend/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

from .celery import app as celery_app

VERSION = '20240815'
VERSION = '20240827'
2 changes: 1 addition & 1 deletion frontend/.env
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VUE_APP_VERSION="20240815"
VUE_APP_VERSION="20240827"
2 changes: 1 addition & 1 deletion frontend/src/components/bis_list/actions.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export default class Actions extends Vue {
}
get syncableLists(): BISList[] {
if (this.character.bis_lists == null) return []
if (this.character.bis_lists == null || this.bisList == null) return []
return this.character.bis_lists.filter((list: BISList) => list.id !== this.bisList.id && list.job.id === this.bisList.job_id)
}
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/components/bis_list/form.vue
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,7 @@ export default class BISListForm extends Vue {
}
get syncableLists(): BISList[] {
if (this.character.bis_lists == null || !this.renderDesktop) return []
console.log(this.character.bis_lists)
if (!this.renderDesktop || this.character == null || this.character.bis_lists == null) return []
return this.character.bis_lists.filter((list: BISList) => list.id !== this.bisList.id && list.job.id === this.bisList.job_id)
}
Expand Down
11 changes: 9 additions & 2 deletions frontend/src/components/modals/changelog.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,15 @@
</div>
<div class="card-content content">
<h2 class="has-text-primary subtitle">{{ version }}</h2>
<div class="divider"><i class="material-icons icon">expand_more</i> Fixes and Improvements <i class="material-icons icon">expand_more</i></div>
<p>Fixed an issue that was happening when trying to Auto-Assign Loot from the Solver when there were Greed slots for the week.</p>
<div class="divider"><i class="material-icons icon">expand_more</i> Bugfixes <i class="material-icons icon">expand_more</i></div>
<p>
Set the weeks-per-token-purchase for the second fight of a Tier to <code>3</code>, as accessory augments are only 3 tokens instead of 4.
<ul>
<li>This shouldn't affect the loot distrib rolls, only it now requires less weeks to get there!</li>
</ul>
</p>
<p>Fixed some issues with proxy character pages not rendering properly.</p>
<p>Fixed errors occurring when trying to import from Lodestone when the Character is missing equipment.</p>
</div>
</div>
</template>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Sentry.init({
Vue,
dsn: 'https://[email protected]/6180221',
logErrors: true,
release: 'savageaim@20240815',
release: 'savageaim@20240827',
integrations: [
new Sentry.BrowserTracing({
routingInstrumentation: Sentry.vueRouterInstrumentation(router),
Expand Down

0 comments on commit 3e8c70b

Please sign in to comment.