From 2652012d870bb396f36bed1cfb399692515c4bcb Mon Sep 17 00:00:00 2001 From: Bogadisa Date: Mon, 18 May 2026 14:14:00 -0230 Subject: [PATCH 1/8] #812 Small changes to modifier data --- .../data_deposit/modifier/modifier_data/AulsUprising.csv | 4 ++-- .../data_deposit/modifier/modifier_data/BalanceOfTerror.csv | 4 ++-- .../data_deposit/modifier/modifier_data/BoundByDestiny.csv | 4 ++-- .../data_deposit/modifier/modifier_data/BrutalRestraint.csv | 4 ++-- .../data_deposit/modifier/modifier_data/CaneOfKulemak.csv | 2 +- .../modifier/modifier_data/CircleOfAmbition.csv | 4 ++-- .../data_deposit/modifier/modifier_data/CircleOfAnguish.csv | 4 ++-- .../data_deposit/modifier/modifier_data/CircleOfFear.csv | 4 ++-- .../data_deposit/modifier/modifier_data/CircleOfGuilt.csv | 4 ++-- .../modifier/modifier_data/CircleOfNostalgia.csv | 4 ++-- .../data_deposit/modifier/modifier_data/CircleOfRegret.csv | 4 ++-- .../data_deposit/modifier/modifier_data/ElegantHubris.csv | 4 ++-- .../data_deposit/modifier/modifier_data/ForbiddenFlame.csv | 4 ++-- .../data_deposit/modifier/modifier_data/ForbiddenFlesh.csv | 4 ++-- .../data_deposit/modifier/modifier_data/ForbiddenShako.csv | 4 ++-- .../data_deposit/modifier/modifier_data/GloriousVanity.csv | 4 ++-- .../data_deposit/modifier/modifier_data/GrandSpectrum.csv | 4 ++-- .../data_deposit/modifier/modifier_data/HeroicTragedy.csv | 4 ++-- .../modifier/modifier_data/ImpossibleEscape.csv | 4 ++-- .../data_deposit/modifier/modifier_data/LethalPride.csv | 4 ++-- .../data_deposit/modifier/modifier_data/Mageblood.csv | 4 ++-- .../data_deposit/modifier/modifier_data/MilitantFaith.csv | 4 ++-- .../data_deposit/modifier/modifier_data/Paradoxica.csv | 4 ++-- .../modifier/modifier_data/PrecursorsEmblem.csv | 4 ++-- .../modifier/modifier_data/ReplicaDragonfangsFlight.csv | 4 ++-- .../modifier/modifier_data/ScreamsOfTheDesiccated.csv | 6 +++--- .../modifier/modifier_data/ShroudOfTheLightless.csv | 4 ++-- .../data_deposit/modifier/modifier_data/SkinOfTheLords.csv | 4 ++-- .../modifier/modifier_data/SplitPersonality.csv | 4 ++-- .../data_deposit/modifier/modifier_data/SublimeVision.csv | 4 ++-- .../modifier/modifier_data/ThatWhichWasTaken.csv | 4 ++-- .../data_deposit/modifier/modifier_data/TheAdorned.csv | 4 ++-- .../modifier/modifier_data/TheLightOfMeaning.csv | 4 ++-- .../data_deposit/modifier/modifier_data/TheUtmost.csv | 4 ++-- .../data_deposit/modifier/modifier_data/ThreadOfHope.csv | 4 ++-- .../data_deposit/modifier/modifier_data/Voices.csv | 4 ++-- .../data_deposit/modifier/modifier_data/WatchersEye.csv | 2 +- 37 files changed, 73 insertions(+), 73 deletions(-) diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/AulsUprising.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/AulsUprising.csv index 050291230..41921c4ff 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/AulsUprising.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/AulsUprising.csv @@ -1,8 +1,8 @@ # Unique Name: Aul's Uprising # Base Types: Onyx Amulet -# Total modifiers on each item: 5|6 +# Total modifiers: 5|6 # Can have duplicate modifiers: False|False|False|False -# Modifier distrubution: 1:1,5:1,8:1,"rest":2|3 +# Modifier distribution: 1:1,5:1,8:1,"rest":2|3 # Source: https://poedb.tw/Auls_Uprising#AulsUprisingUnique minRoll,maxRoll,textRolls,position,effect,static,unique 50,120,,0,"+# to maximum Life",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/BalanceOfTerror.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/BalanceOfTerror.csv index 157f41004..3ba2aa920 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/BalanceOfTerror.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/BalanceOfTerror.csv @@ -1,8 +1,8 @@ # Unique Name: The Balance of Terror # Base Types: Cobalt Jewel -# Total modifiers on each item: 3 +# Total modifiers: 3 # Can have duplicate modifiers: False|False -# Modifier distrubution: 1:1,"rest":2 +# Modifier distribution: 1:1,"rest":2 # Source: https://poedb.tw/The_Balance_of_Terror minRoll,maxRoll,position,effect,static,unique 10,15,0,"#% to all Elemental Resistances",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/BoundByDestiny.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/BoundByDestiny.csv index 5e6b5c896..45b028289 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/BoundByDestiny.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/BoundByDestiny.csv @@ -1,8 +1,8 @@ # Unique Name: Bound By Destiny # Base Types: Prismatic Jewel -# Total modifiers on each item: 3 +# Total modifiers: 3 # Can have duplicate modifiers: False|False|False -# Modifier distrubution: 18:1,36:1,"rest":1 +# Modifier distribution: 18:1,36:1,"rest":1 # Source: https://poedb.tw/us/Bound_By_Destiny#BoundByDestinyModifiers minRoll,maxRoll,position,effect,static,unique 10,15,0,"#% increased maximum Life if 2 Elder Items are Equipped",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/BrutalRestraint.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/BrutalRestraint.csv index 1d5220889..bddc12f1c 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/BrutalRestraint.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/BrutalRestraint.csv @@ -1,8 +1,8 @@ # Unique Name: Brutal Restraint # Base Types: Timeless Jewel -# Total modifiers on each item: 2 +# Total modifiers: 2 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":2 +# Modifier distribution: "rest":2 # Source: https://poedb.tw/Brutal_Restraint minRoll,maxRoll,textRolls,position,effect,static,unique 500,8000,,0,"Denoted service of # dekhara in the akhara of # Passives in radius are Conquered by the Maraketh",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CaneOfKulemak.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CaneOfKulemak.csv index 49ce755f6..70f3092db 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CaneOfKulemak.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CaneOfKulemak.csv @@ -2,7 +2,7 @@ # Base Types: Serpentine Staff # Total modifiers: 4|5 # Can have duplicate modifiers: False|False|False|False -# Modifier distrubution: 1:1,6:1,25:1|2,"rest":0|1 +# Modifier distribution: 1:1,6:1,25:1|2,"rest":0|1 # Source: https://poedb.tw/us/Cane_of_Kulemak minRoll,maxRoll,textRolls,position,effect,static,unique,veiled 60,90,,0,"#% increased Unveiled Modifier magnitudes",,True,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfAmbition.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfAmbition.csv index a22791fb2..4ac367f29 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfAmbition.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfAmbition.csv @@ -1,8 +1,8 @@ # Unique Name: Circle of Ambition # Base Types: Prismatic Ring -# Total modifiers on each item: 6 +# Total modifiers: 6 # Can have duplicate modifiers: False|False -# Modifier distrubution: 2:2,"rest":4 +# Modifier distribution: 2:2,"rest":4 # Source: https://poedb.tw/Circle_of_Ambition#CircleofAmbitionModifiers minRoll,maxRoll,textRolls,position,effect,static,unique 10,20,,0,"+#% to all Elemental Resistances",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfAnguish.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfAnguish.csv index 2b351ff60..b1584603f 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfAnguish.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfAnguish.csv @@ -1,8 +1,8 @@ # Unique Name: Circle of Anguish # Base Types: Ruby Ring -# Total modifiers on each item: 5 +# Total modifiers: 5 # Can have duplicate modifiers: False|False -# Modifier distrubution: 4:3,"rest":2 +# Modifier distribution: 4:3,"rest":2 # Source: https://www.poewiki.net/wiki/Synthesis_league#Unique_items minRoll,maxRoll,textRolls,position,effect,static,unique 20,30,,0,"+# to Strength",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfFear.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfFear.csv index 59ded8898..2726d11d5 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfFear.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfFear.csv @@ -1,8 +1,8 @@ # Unique Name: Circle of Fear # Base Types: Sapphire Ring -# Total modifiers on each item: 5 +# Total modifiers: 5 # Can have duplicate modifiers: False|False -# Modifier distrubution: 4:3,"rest":2 +# Modifier distribution: 4:3,"rest":2 # Source: https://www.poewiki.net/wiki/Synthesis_league#Unique_items minRoll,maxRoll,textRolls,position,effect,static,unique 20,30,,0,"+# to Dexterity",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfGuilt.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfGuilt.csv index 517788397..b4d5cebb8 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfGuilt.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfGuilt.csv @@ -1,8 +1,8 @@ # Unique Name: Circle of Guilt # Base Types: Iron Ring -# Total modifiers on each item: 5 +# Total modifiers: 5 # Can have duplicate modifiers: False|False -# Modifier distrubution: 4:3,"rest":2 +# Modifier distribution: 4:3,"rest":2 # Source: https://www.poewiki.net/wiki/Synthesis_league#Unique_items minRoll,maxRoll,textRolls,position,effect,static,unique 10,20,,0,"+# to all Attributes",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfNostalgia.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfNostalgia.csv index a7f27af4d..1222452c4 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfNostalgia.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfNostalgia.csv @@ -1,8 +1,8 @@ # Unique Name: Circle of Nostalgia # Base Types: Amethyst Ring -# Total modifiers on each item: 5 +# Total modifiers: 5 # Can have duplicate modifiers: False|False -# Modifier distrubution: 4:3,"rest":2 +# Modifier distribution: 4:3,"rest":2 # Source: https://www.poewiki.net/wiki/Synthesis_league#Unique_items minRoll,maxRoll,textRolls,position,effect,static,unique 10,20,,0,"+# to all Attributes",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfRegret.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfRegret.csv index 5a7c4ca5c..a58278523 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfRegret.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/CircleOfRegret.csv @@ -1,8 +1,8 @@ # Unique Name: Circle of Regret # Base Types: Topaz Ring -# Total modifiers on each item: 5 +# Total modifiers: 5 # Can have duplicate modifiers: False|False -# Modifier distrubution: 4:3,"rest":2 +# Modifier distribution: 4:3,"rest":2 # Source: https://www.poewiki.net/wiki/Synthesis_league#Unique_items minRoll,maxRoll,textRolls,position,effect,static,unique 20,30,,0,"+# to Intelligence",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ElegantHubris.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ElegantHubris.csv index 44c555a99..d9833ebdc 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ElegantHubris.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ElegantHubris.csv @@ -1,8 +1,8 @@ # Unique Name: Elegant Hubris # Base Types: Timeless Jewel -# Total modifiers on each item: 2 +# Total modifiers: 2 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":2 +# Modifier distribution: "rest":2 # Source: https://poedb.tw/Elegant_Hubris minRoll,maxRoll,textRolls,position,effect,static,unique 2000,160000,,0,"Commissioned # coins to commemorate # Passives in radius are Conquered by the Eternal Empire",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ForbiddenFlame.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ForbiddenFlame.csv index 29e800089..551db82a3 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ForbiddenFlame.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ForbiddenFlame.csv @@ -1,8 +1,8 @@ # Unique Name: Forbidden Flame # Base Types: Crimson Jewel -# Total modifiers on each item: 1 +# Total modifiers: 1 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":1 +# Modifier distribution: "rest":1 # Source: https://poedb.tw/Forbidden_Flame textRolls,position,effect,unique "Assassin|Berserker|Champion|Chieftain|Deadeye|Elementalist|Gladiator|Guardian|Hierophant|Inquisitor|Juggernaut|Necromancer|Occultist|Pathfinder|Saboteur|Slayer|Trickster|Unleashed Potential|Warden|Fatal Flourish|Indomitable Resolve|Fury of Nature|Searing Purity|Nine Lives|Ambush and Assassinate|Deadly Infusion|Mistwalker|Noxious Strike|Opportunistic|Toxic Delivery|Unstable Infusion|Aspect of Carnage|Blitz|Gore Dancer|Ancestral Fury|Crave the Slaughter|Defy Pain|Flawless Savagery|Rite of Ruin|War Bringer|Conqueror|Worthy Causes|First to Strike, Last to Fall|Fortitude|Inspirational|Master of Metal|Unstoppable Hero|Worthy Foe|Hinekora, Death's Fury|Ngamahu, Flame's Advance|Ramako, Sun's Light|Sione, Sun's Roar|Tasalio, Cleansing Water|Tawhoa, Forest's Strength|Tukohama, War's Herald|Valako, Storm's Embrace|Avidity|Endless Munitions|Far Shot|Focal Point|Gathering Winds|Occupying Force|Ricochet|Wind Ward|Bastion of Elements|Elemancer|Bringer of Ruin|Heart of Destruction|Liege of the Primordial|Mastermind of Discord|Shaper of Flames|Shaper of Storms|Shaper of Winter|Determined Survivor|Gratuitous Violence|Jagged Technique|Measured Retaliation|More Than Skill|War of Attrition|Weapon Master|Bastion of Hope|Harmony of Purpose|Radiant Crusade|Radiant Faith|Time of Need|Unwavering Crusade|Unwavering Faith|Arcane Blessing|Conviction of Power|Divine Guidance|Illuminated Devotion|Pursuit of Faith|Ritual of Awakening|Sanctuary of Thought|Sign of Purpose|Augury of Penitence|Inevitable Judgement|Instruments of Virtue|Instruments of Zeal|Pious Path|Righteous Providence|Sanctuary|Unbreakable|Undeniable|Unflinching|Unrelenting|Unstoppable|Untiring|Unyielding|Bone Barrier|Commander of Darkness|Corpse Pact|Essence Glutton|Mindless Aggression|Mistress of Sacrifice|Plaguebringer|Unnatural Strength|Forbidden Power|Frigid Wake|Profane Bloom|Unholy Authority|Vile Bastion|Void Beacon|Withering Presence|Master Alchemist|Master Distiller|Master Surgeon|Master Toxicist|Nature's Adrenaline|Nature's Boon|Nature's Reprisal|Bomb Specialist|Born in the Shadows|Chain Reaction|Demolitions Specialist|Explosives Expert|Shrapnel Specialist|Calculated Risk|Like Clockwork|Perfect Crime|Pyromaniac|Harness the Void|Bane of Legends|Brutal Fervour|Endless Hunger|Headsman|Impact|Masterful Form|Overwhelm|Escape Artist|Heartstopper|One Step Ahead|Polymath|Soul Drinker|Spellbreaker|Swift Killer|Avatar of the Wilds|Enduring Suffusion|Experienced Herbalist|Lesson of the Seasons|Mother's Teachings|Oath of Spring|Oath of Summer|Oath of Winter|Seasoned Hunter",0,"Allocates # if you have the matching modifier on Forbidden Flesh",True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ForbiddenFlesh.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ForbiddenFlesh.csv index 5947e54bf..84da411a1 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ForbiddenFlesh.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ForbiddenFlesh.csv @@ -1,8 +1,8 @@ # Unique Name: Forbidden Flesh # Base Types: Cobalt Jewel -# Total modifiers on each item: 1 +# Total modifiers: 1 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":1 +# Modifier distribution: "rest":1 # Source: https://poedb.tw/Forbidden_Flesh textRolls,position,effect,unique "Assassin|Berserker|Champion|Chieftain|Deadeye|Elementalist|Gladiator|Guardian|Hierophant|Inquisitor|Juggernaut|Necromancer|Occultist|Pathfinder|Saboteur|Slayer|Trickster|Unleashed Potential|Warden|Fatal Flourish|Indomitable Resolve|Fury of Nature|Searing Purity|Nine Lives|Ambush and Assassinate|Deadly Infusion|Mistwalker|Noxious Strike|Opportunistic|Toxic Delivery|Unstable Infusion|Aspect of Carnage|Blitz|Gore Dancer|Ancestral Fury|Crave the Slaughter|Defy Pain|Flawless Savagery|Rite of Ruin|War Bringer|Conqueror|Worthy Causes|First to Strike, Last to Fall|Fortitude|Inspirational|Master of Metal|Unstoppable Hero|Worthy Foe|Hinekora, Death's Fury|Ngamahu, Flame's Advance|Ramako, Sun's Light|Sione, Sun's Roar|Tasalio, Cleansing Water|Tawhoa, Forest's Strength|Tukohama, War's Herald|Valako, Storm's Embrace|Avidity|Endless Munitions|Far Shot|Focal Point|Gathering Winds|Occupying Force|Ricochet|Wind Ward|Bastion of Elements|Elemancer|Bringer of Ruin|Heart of Destruction|Liege of the Primordial|Mastermind of Discord|Shaper of Flames|Shaper of Storms|Shaper of Winter|Determined Survivor|Gratuitous Violence|Jagged Technique|Measured Retaliation|More Than Skill|War of Attrition|Weapon Master|Bastion of Hope|Harmony of Purpose|Radiant Crusade|Radiant Faith|Time of Need|Unwavering Crusade|Unwavering Faith|Arcane Blessing|Conviction of Power|Divine Guidance|Illuminated Devotion|Pursuit of Faith|Ritual of Awakening|Sanctuary of Thought|Sign of Purpose|Augury of Penitence|Inevitable Judgement|Instruments of Virtue|Instruments of Zeal|Pious Path|Righteous Providence|Sanctuary|Unbreakable|Undeniable|Unflinching|Unrelenting|Unstoppable|Untiring|Unyielding|Bone Barrier|Commander of Darkness|Corpse Pact|Essence Glutton|Mindless Aggression|Mistress of Sacrifice|Plaguebringer|Unnatural Strength|Forbidden Power|Frigid Wake|Profane Bloom|Unholy Authority|Vile Bastion|Void Beacon|Withering Presence|Master Alchemist|Master Distiller|Master Surgeon|Master Toxicist|Nature's Adrenaline|Nature's Boon|Nature's Reprisal|Bomb Specialist|Born in the Shadows|Chain Reaction|Demolitions Specialist|Explosives Expert|Shrapnel Specialist|Calculated Risk|Like Clockwork|Perfect Crime|Pyromaniac|Harness the Void|Bane of Legends|Brutal Fervour|Endless Hunger|Headsman|Impact|Masterful Form|Overwhelm|Escape Artist|Heartstopper|One Step Ahead|Polymath|Soul Drinker|Spellbreaker|Swift Killer|Avatar of the Wilds|Enduring Suffusion|Experienced Herbalist|Lesson of the Seasons|Mother's Teachings|Oath of Spring|Oath of Summer|Oath of Winter|Seasoned Hunter",0,"Allocates # if you have the matching modifier on Forbidden Flame",True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ForbiddenShako.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ForbiddenShako.csv index c4997ffa0..1f18bef01 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ForbiddenShako.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ForbiddenShako.csv @@ -1,8 +1,8 @@ # Unique Name: Forbidden Shako # Base Types: Great Crown -# Total modifiers on each item: 3 +# Total modifiers: 3 # Can have duplicate modifiers: True -# Modifier distrubution: "rest":3 +# Modifier distribution: "rest":3 # Source: https://poedb.tw/Forbidden_Shako#ForbiddenShakoUnique minRoll,maxRoll,textRolls,position,effect,unique 25,30,,0,"+# to all Attributes",True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/GloriousVanity.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/GloriousVanity.csv index f6e93f90d..ee9bd7a11 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/GloriousVanity.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/GloriousVanity.csv @@ -1,8 +1,8 @@ # Unique Name: Glorious Vanity # Base Types: Timeless Jewel -# Total modifiers on each item: 2 +# Total modifiers: 2 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":2 +# Modifier distribution: "rest":2 # Source: https://poedb.tw/Glorious_Vanity minRoll,maxRoll,textRolls,position,effect,static,unique 100,8000,,0,"Bathed in the blood of # sacrificed in the name of # Passives in radius are Conquered by the Vaal",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/GrandSpectrum.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/GrandSpectrum.csv index f21caa2db..1efd7ae1d 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/GrandSpectrum.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/GrandSpectrum.csv @@ -1,8 +1,8 @@ # Unique Name: Grand Spectrum # Base Types: Viridian Jewel|Cobalt Jewel|Crimson Jewel -# Total modifiers on each item: 1 +# Total modifiers: 1 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":1 +# Modifier distribution: "rest":1 # Source: https://poedb.tw/Grand_Spectrum minRoll,maxRoll,position,effect,static,unique ,,0,"+7% to all Elemental Resistances per Grand Spectrum",True,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/HeroicTragedy.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/HeroicTragedy.csv index 5428766e9..6e2fb806d 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/HeroicTragedy.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/HeroicTragedy.csv @@ -1,8 +1,8 @@ # Unique Name: Heroic Tragedy # Base Types: Timeless Jewel -# Total modifiers on each item: 2 +# Total modifiers: 2 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":2 +# Modifier distribution: "rest":2 # Source: https://poedb.tw/us/Heroic_Tragedy minRoll,maxRoll,textRolls,position,effect,static,unique 100,8000,,0,"Remembrancing # songworthy deeds by the line of # Passives in radius are Conquered by the Kalguur",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ImpossibleEscape.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ImpossibleEscape.csv index f2da540c0..02a5c60a2 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ImpossibleEscape.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ImpossibleEscape.csv @@ -1,8 +1,8 @@ # Unique Name: Impossible Escape # Base Types: Viridian Jewel -# Total modifiers on each item: 1 +# Total modifiers: 1 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":1 +# Modifier distribution: "rest":1 # Source: https://poedb.tw/Impossible_Escape textRolls,position,effect,unique "Divine Shield|The Agnostic|Resolute Technique|Ancestral Bond|Blood Magic|Zealot's Oath|Avatar of Fire|Glancing Blows|Runebinder|Call to Arms|Arsenal of Vengeance|Eternal Youth|Imbalanced Guard|Elemental Overload|Mind Over Matter|The Impaler|Unwavering Stance|Crimson Dance|Iron Will|Versatile Combatant|Iron Grip|Necromantic Aegis|Worship the Blightheart|Solipsism|Magebane|Iron Reflexes|Hex Master|Minion Instability|Conduit|Vaal Pact|Wicked Ward|Elemental Equilibrium|Pain Attunement|Bloodsoaked Blade|Eldritch Battery|Supreme Ego|Precise Technique|Point Blank|Wind Dancer|Chaos Inoculation|Lethe Shade|Arrow Dancing|Ghost Dance|Ghost Reaver|Acrobatics|Perfect Agony",0,"Passives in Radius of # can be Allocated without being connected to your tree",True \ No newline at end of file diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/LethalPride.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/LethalPride.csv index fd243d7d1..399de7c3c 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/LethalPride.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/LethalPride.csv @@ -1,8 +1,8 @@ # Unique Name: Lethal Pride # Base Types: Timeless Jewel -# Total modifiers on each item: 2 +# Total modifiers: 2 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":2 +# Modifier distribution: "rest":2 # Source: https://poedb.tw/Lethal_Pride minRoll,maxRoll,textRolls,position,effect,static,unique 10000,18000,,0,"Commanded leadership over # warriors under # Passives in radius are Conquered by the Karui",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/Mageblood.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/Mageblood.csv index 3988bb6ec..57482eedc 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/Mageblood.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/Mageblood.csv @@ -1,8 +1,8 @@ # Unique Name: Mageblood # Base Types: Heavy Belt -# Total modifiers on each item: 6 +# Total modifiers: 5 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":6 +# Modifier distribution: "rest":5 # Source: https://poedb.tw/us/Mageblood minRoll,maxRoll,textRolls,position,effect,static,unique 30,50,,0,"+# to Dexterity",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/MilitantFaith.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/MilitantFaith.csv index fb4de1f60..0570ee4c5 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/MilitantFaith.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/MilitantFaith.csv @@ -1,8 +1,8 @@ # Unique Name: Militant Faith # Base Types: Timeless Jewel -# Total modifiers on each item: 4 +# Total modifiers: 4 # Can have duplicate modifiers: False|False -# Modifier distrubution: 3:2,"rest":2 +# Modifier distribution: 3:2,"rest":2 # Source: https://poedb.tw/Militant_Faith minRoll,maxRoll,textRolls,position,effect,static,unique 2000,10000,,0,"Carved to glorify # new faithful converted by High Templar # Passives in radius are Conquered by the Templars",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/Paradoxica.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/Paradoxica.csv index 492d4c1f5..ce78d4d2c 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/Paradoxica.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/Paradoxica.csv @@ -1,8 +1,8 @@ # Unique Name: Paradoxica # Base Types: Vaal Rapier -# Total modifiers on each item: 3 +# Total modifiers: 3 # Can have duplicate modifiers: False|False -# Modifier distrubution: 1:1,"rest":2 +# Modifier distribution: 1:1,"rest":2 # Source: https://www.poewiki.net/wiki/Paradoxica minRoll,maxRoll,textRolls,position,effect,static,unique,veiled ,,,0,"Attacks with this Weapon deal Double Damage",True,True,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/PrecursorsEmblem.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/PrecursorsEmblem.csv index fc704e797..22b6dc997 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/PrecursorsEmblem.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/PrecursorsEmblem.csv @@ -1,8 +1,8 @@ # Unique Name: Precursor's Emblem # Base Types: Ruby Ring|Sapphire Ring|Topaz Ring|Prismatic Ring|Two-Stone Ring -# Total modifiers on each item: 6 +# Total modifiers: 6 # Can have duplicate modifiers: False|True -# Modifier distrubution: 9:3,"rest":3 +# Modifier distribution: 9:3,"rest":3 # Source: https://poedb.tw/Precursors_Emblem#PrecursorsEmblemUnique minRoll,maxRoll,textRolls,position,effect,static,unique 20,20,,0,"+# to Strength",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ReplicaDragonfangsFlight.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ReplicaDragonfangsFlight.csv index 9304d5b45..1914b72b0 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ReplicaDragonfangsFlight.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ReplicaDragonfangsFlight.csv @@ -1,8 +1,8 @@ # Unique Name: Replica Dragonfang's Flight # Base Types: Onyx Amulet -# Total modifiers on each item: 4 +# Total modifiers: 4 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":4 +# Modifier distribution: "rest":4 # Source: https://poedb.tw/Replica_Dragonfangs_Flight minRoll,maxRoll,textRolls,position,effect,unique ,,"Fireball|Ice Nova|Leap Slam|Sweep|Ground Slam|Cleave|Shield Charge|Enduring Cry|Double Strike|Elemental Hit|Immortal Call|Dual Strike|Whirling Blades|Frenzy|Cold Snap|Raise Zombie|Detonate Dead|Caustic Arrow|Creeping Frost|Ice Shot|Static Strike|Split Arrow|Blood Rage|Righteous Fire|Discharge|Flicker Strike|Spark|Ice Spear|Raise Spectre|Infernal Blow|Glacial Hammer|Frost Wall|Freezing Pulse|Shock Nova|Viper Strike|Phase Run|Explosive Arrow|Temporal Chains|Elemental Weakness|Warlord's Mark|Punishment|Enfeeble|Assassin's Mark|Sniper's Mark|Despair|Lightning Warp|Summon Skeletons|Glacial Shield Swipe|Crushing Fist|Swordstorm|Heavy Strike|Dominating Blow|Rain of Arrows|Firestorm|Lightning Strike|Tempest Shield|Molten Shell|Power Siphon|Puncture|Lightning Arrow|Arc|Haste|Purity of Elements|Vitality|Discipline|Grace|Determination|Anger|Hatred|Wrath|Burning Arrow|Clarity|Shockwave Totem|Rejuvenation Totem|Conversion Trap|Bear Trap|Fire Trap|Decoy Totem|Devouring Totem|Ethereal Knives|Arctic Armour|Holy Flame Totem|Flammability|Frostbite|Conductivity|Incinerate|Cyclone|Searing Bond|Reave|Lightning Trap|Pyroclast Mine|Smoke Mine|Icicle Mine|Stormblast Mine|Animate Guardian|Spectral Throw|Animate Weapon|Purity of Fire|Purity of Ice|Purity of Lightning|Storm Call|Flameblast|Barrage|Ball Lightning|Summon Raging Spirit|Flame Surge|Desecrate|Flesh Offering|Bone Offering|Glacial Cascade|Convocation|Molten Strike|Tornado Shot|Herald of Ash|Herald of Ice|Herald of Thunder|Poacher's Mark|Lightning Tendrils|Mirror Arrow|Blink Arrow|Kinetic Blast|Summon Chaos Golem|Summon Ice Golem|Summon Flame Golem|Summon Lightning Golem|Ice Crash|Rallying Cry|Infernal Cry|Vigilant Strike|Rolling Magma|Flame Dash|Frost Blades|Wild Strike|Galvanic Arrow|Blast Rain|Bladefall|Siege Ballista|Blade Vortex|Contagion|Wither|Essence Drain|Ice Trap|Orb of Storms|Frost Bomb|Summon Stone Golem|Eviscerate|Snipe|Vengeful Cry|Earthquake|Sunder|Lacerate|Spirit Offering|Frostbolt|Vortex|Blight|Scorching Ray|Blade Flurry|Charged Dash|Dark Pact|Storm Burst|Cremation|Bodyswap|Volatile Dead|Unearth|Explosive Trap|Siphoning Trap|Flamethrower Trap|Lightning Spire Trap|Seismic Trap|Vulnerability|Tectonic Slam|Spectral Shield Throw|Herald of Purity|Herald of Agony|Consecrated Path|Smite|Scourge Arrow|Toxic Rain|Summon Holy Relic|Winter Orb|Storm Brand|Armageddon Brand|Brand Recall|War Banner|Dread Banner|Shattering Steel|Lancing Steel|Purifying Flame|Soulrend|Bane|Divine Ire|Wave of Conviction|Zealotry|Malevolence|Precision|Steelskin|Dash|Bladestorm|Blood and Sand|Berserk|Perforate|Chain Hook|Frostblink|Flesh and Stone|Pride|Cobra Lash|Withering Step|Venom Gyre|Summon Skitterbots|Plague Bearer|Pestilent Strike|Summon Carrion Golem|Artillery Ballista|Shrapnel Ballista|Ensnaring Arrow|Stormbind|Blade Blast|Spellslinger|Kinetic Bolt|Arcane Cloak|Intimidating Cry|Ancestral Cry|Seismic Cry|General's Cry|Arcanist Brand|Penance Brand|Wintertide Brand|Earthshatter|Sigil of Power|Splitting Steel|Flame Wall|Blazing Salvo|Crackling Lance|Void Sphere|Frost Shield|Hydrosphere|Hexblast|Exsanguinate|Corrupting Fever|Petrified Blood|Reap|Defiance Banner|Storm Rain|Rage Vortex|Shield Crush|Summon Reaper|Boneshatter|Ambush|Voltaxic Burst|Battlemage's Cry|Absolution|Eye of Winter|Spectral Helix|Forbidden Rite|Blade Trap|Manabond|Explosive Concoction|Poisonous Concoction|Temporal Rift|Energy Blade|Tornado|Soul Link|Flame Link|Intuitive Link|Protective Link|Vampiric Link|Destructive Link|Galvanic Field|Lightning Conduit|Alchemist's Mark|Frozen Legion|Volcanic Fissure|Automation|Autoexertion|Divine Retribution",0,"+3 to Level of all # Gems",True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ScreamsOfTheDesiccated.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ScreamsOfTheDesiccated.csv index d55d2fc26..8c3358db7 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ScreamsOfTheDesiccated.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ScreamsOfTheDesiccated.csv @@ -1,8 +1,8 @@ # Unique Name: Screams of the Desiccated # Base Types: Leather Belt -# Total modifiers on each item: 3|4|5 -# Can have duplicate modifiers: False -# Modifier distrubution: 2:2,"rest":1|2|3 +# Total modifiers: 3|4 +# Can have duplicate modifiers: False|True +# Modifier distribution: 2:2,"rest":1|2 # Source: https://poedb.tw/us/Screams_of_the_Desiccated minRoll,maxRoll,textRolls,position,effect,unique 23,32,,0,"+# to Intelligence",True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ShroudOfTheLightless.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ShroudOfTheLightless.csv index e2de7b33f..cec88d487 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ShroudOfTheLightless.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ShroudOfTheLightless.csv @@ -1,8 +1,8 @@ # Unique Name: Shroud of the Lightless # Base Types: Carnal Armour -# Total modifiers on each item: 6 +# Total modifiers: 6 # Can have duplicate modifiers: True -# Modifier distrubution: "rest":6 +# Modifier distribution: "rest":6 # Source: https://poedb.tw/Shroud_of_the_Lightless minRoll,maxRoll,textRolls,position,effect,static,unique 1,3,,0,"Has # Abyssal Sockets",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/SkinOfTheLords.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/SkinOfTheLords.csv index 18e490d0c..386fce70f 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/SkinOfTheLords.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/SkinOfTheLords.csv @@ -1,8 +1,8 @@ # Unique Name: Skin of the Lords # Base Types: Simple Robe -# Total modifiers on each item: 5 +# Total modifiers: 5 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":5 +# Modifier distribution: "rest":5 # Source: https://poedb.tw/Skin_of_the_Lords#SkinoftheLordsUnique # Note: Potential source of error due to very simple effect string minRoll,maxRoll,textRolls,position,effect,static,unique diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/SplitPersonality.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/SplitPersonality.csv index ffc56372b..5e26914e1 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/SplitPersonality.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/SplitPersonality.csv @@ -1,8 +1,8 @@ # Unique Name: Split Personality # Base Types: Crimson Jewel -# Total modifiers on each item: 2 +# Total modifiers: 2 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":2 +# Modifier distribution: "rest":2 # Source: https://poedb.tw/Split_Personality#SplitPersonalityUnique minRoll,maxRoll,textRolls,position,effect,static,unique 5,5,,0,"+# to Strength",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/SublimeVision.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/SublimeVision.csv index 9d29533db..644fac577 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/SublimeVision.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/SublimeVision.csv @@ -1,8 +1,8 @@ # Unique Name: Sublime Vision # Base Types: Prismatic Jewel -# Total modifiers on each item: 3 +# Total modifiers: 3 # Can have duplicate modifiers: False|False|False -# Modifier distrubution: 2:1,3:1,"rest":1 +# Modifier distribution: 2:1,3:1,"rest":1 # Source: https://poedb.tw/Sublime_Vision minRoll,maxRoll,textRolls,position,effect,static,unique 20,40,,0,"Auras from your Skills have #% increased Effect on you",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ThatWhichWasTaken.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ThatWhichWasTaken.csv index 2f70c1a88..4e4dc9ff3 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ThatWhichWasTaken.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ThatWhichWasTaken.csv @@ -1,8 +1,8 @@ # Unique Name: That Which Was Taken # Base Types: Crimson Jewel -# Total modifiers on each item: 4 +# Total modifiers: 4 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":4 +# Modifier distribution: "rest":4 # Source: https://poedb.tw/That_Which_Was_Taken minRoll,maxRoll,position,effect,static,unique ,,0,"Movement Speed cannot be modified to below Base Value",True,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/TheAdorned.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/TheAdorned.csv index 26693eaed..cd37957f6 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/TheAdorned.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/TheAdorned.csv @@ -1,8 +1,8 @@ # Unique Name: The Adorned # Base Types: Crimson Jewel -# Total modifiers on each item: 1 +# Total modifiers: 1 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":1 +# Modifier distribution: "rest":1 # Source: https://poedb.tw/us/The_Adorned minRoll,maxRoll,position,effect,unique 0,100,0,"#% increased Effect of Jewel Socket Passive Skills containing Corrupted Magic Jewels",True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/TheLightOfMeaning.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/TheLightOfMeaning.csv index 19fca0dfa..e464f4603 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/TheLightOfMeaning.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/TheLightOfMeaning.csv @@ -1,8 +1,8 @@ # Unique Name: The Light of Meaning # Base Types: Prismatic Jewel -# Total modifiers on each item: 1 +# Total modifiers: 1 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":1 +# Modifier distribution: "rest":1 # Source: https://poedb.tw/The_Light_of_Meaning position,effect,static,unique 0,"Passive Skills in Radius also grant +5 to maximum Life",True,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/TheUtmost.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/TheUtmost.csv index 42bea2d87..0d385289b 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/TheUtmost.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/TheUtmost.csv @@ -1,8 +1,8 @@ # Unique Name: The Utmost # Base Types: Gold Amulet -# Total modifiers on each item: 4 +# Total modifiers: 4 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":4 +# Modifier distribution: "rest":4 # Source: https://poedb.tw/The_Utmost minRoll,maxRoll,position,effect,unique 0,30,0,"+#% chance to Suppress Spell Damage",True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ThreadOfHope.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ThreadOfHope.csv index bdfce79c0..246550e80 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ThreadOfHope.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/ThreadOfHope.csv @@ -1,8 +1,8 @@ # Unique Name: Thread of Hope # Base Types: Crimson Jewel -# Total modifiers on each item: 3 +# Total modifiers: 3 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":3 +# Modifier distribution: "rest":3 # Source: https://poedb.tw/Thread_of_Hope minRoll,maxRoll,textRolls,position,effect,static,unique ,,"Small|Medium|Large|Very Large|Massive",0,"Only affects Passives in # Ring",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/Voices.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/Voices.csv index 969381ebc..a47c0c5f6 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/Voices.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/Voices.csv @@ -1,8 +1,8 @@ # Unique Name: Voices # Base Types: Large Cluster Jewel -# Total modifiers on each item: 2 +# Total modifiers: 2 # Can have duplicate modifiers: False -# Modifier distrubution: "rest":2 +# Modifier distribution: "rest":2 # Source: https://poedb.tw/Voices textRolls,position,effect,static,unique "1|3|5|7",0,"Adds # Small Passive Skills? which grants? nothing",,True diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/WatchersEye.csv b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/WatchersEye.csv index 28dca4b0a..cc3f4d6da 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/WatchersEye.csv +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data/WatchersEye.csv @@ -2,7 +2,7 @@ # Base Types: Prismatic Jewel # Total modifiers: 5|6 # Can have duplicate modifiers: False|False -# Modifier distrubution: 3:3,"rest":2|3 +# Modifier distribution: 3:3,"rest":2|3 # Source: https://poedb.tw/Watchers_Eye minRoll,maxRoll,position,effect,static,unique 4,6,0,"#% increased maximum Energy Shield",,True From ff8bb9a92d5cc5666df426f38a4dab4819bfb512 Mon Sep 17 00:00:00 2001 From: Bogadisa Date: Mon, 18 May 2026 14:30:21 -0230 Subject: [PATCH 2/8] #812 Updated backend data retrieval --- .../app/alembic/replaceable_objects/main.py | 18 +- ...c96438_removing_modifier_auto_increment.py | 185 ++++++++++++++++++ ...9f3f_added_unidentified_aggregation_job.py | 9 +- src/backend_api/app/api/routes/modifier.py | 6 +- src/backend_api/app/core/models/models.py | 26 +-- .../app/core/schemas/item_modifier.py | 1 + src/backend_api/app/core/schemas/modifier.py | 3 +- .../app/crud/extensions/crud_modifier.py | 65 ++---- .../data_retrieval_app/data_deposit/README.md | 10 +- .../modifier/modifier_data_depositor.py | 4 +- .../detectors/unique_detector.py | 1 + .../transform_poe_api_data.py | 1 + .../utils/data_deposit_test_data_creator.py | 115 +++++------ 13 files changed, 298 insertions(+), 146 deletions(-) create mode 100644 src/backend_api/app/alembic/versions/17daa1c96438_removing_modifier_auto_increment.py diff --git a/src/backend_api/app/alembic/replaceable_objects/main.py b/src/backend_api/app/alembic/replaceable_objects/main.py index 856cd3688..d8d941ad6 100644 --- a/src/backend_api/app/alembic/replaceable_objects/main.py +++ b/src/backend_api/app/alembic/replaceable_objects/main.py @@ -12,11 +12,11 @@ def __init__(self, name, sqltext): class ReplaceableTrigger(ReplaceableObject): - def __init__(self, name, table, function, trigger): + def __init__(self, name: str, table: str, function: str, trigger: str): self.name = name self.table = table - self.function = function - self.trigger = trigger + self.function = function.format(name=name) + self.trigger = trigger.format(name=name, table=table) ObjectType = TypeVar("ObjectType", bound=ReplaceableObject) @@ -89,22 +89,24 @@ def reverse(self): @Operations.implementation_for(CreateViewOp) def create_view(operations: Operations, operation: CreateViewOp): operations.execute( - "CREATE VIEW %s AS %s" % (operation.target.name, operation.target.sqltext) + "CREATE VIEW {} AS {}".format(operation.target.name, operation.target.sqltext) ) @Operations.implementation_for(DropViewOp) def drop_view(operations: Operations, operation: DropViewOp): - operations.execute("DROP VIEW %s" % operation.target.name) + operations.execute("DROP VIEW {}".format(operation.target.name)) @Operations.implementation_for(CreateTriggerOp) def create_trigger(operations: Operations, operation: CreateTriggerOp): operations.execute( - "CREATE FUNCTION %s() %s" % (operation.target.name, operation.target.function) + "CREATE FUNCTION {}() {}".format( + operation.target.name, operation.target.function + ) ) operations.execute( - "CREATE TRIGGER %s %s" % (operation.target.name, operation.target.trigger) + "CREATE TRIGGER {} {}".format(operation.target.name, operation.target.trigger) ) @@ -113,4 +115,4 @@ def drop_trigger(operations: Operations, operation: DropTriggerOp): operations.execute( "DROP TRIGGER {} ON {};".format(operation.target.name, operation.target.table) ) - operations.execute("DROP FUNCTION %s();" % operation.target.name) + operations.execute("DROP FUNCTION {}();".format(operation.target.name)) diff --git a/src/backend_api/app/alembic/versions/17daa1c96438_removing_modifier_auto_increment.py b/src/backend_api/app/alembic/versions/17daa1c96438_removing_modifier_auto_increment.py new file mode 100644 index 000000000..8fc6246db --- /dev/null +++ b/src/backend_api/app/alembic/versions/17daa1c96438_removing_modifier_auto_increment.py @@ -0,0 +1,185 @@ +"""Removing modifier auto increment + +Revision ID: 17daa1c96438 +Revises: cc39d4eb113b +Create Date: 2026-05-09 16:05:14.813497 + +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + +from app.alembic.replaceable_objects.main import ReplaceableTrigger + +# revision identifiers, used by Alembic. +revision: str = "17daa1c96438" +down_revision: Union[str, None] = "cc39d4eb113b" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + +modifier_id_trigger = ReplaceableTrigger( + "increment_modifier_id", + "modifier", + """ + RETURNS trigger AS ${name}$ + DECLARE + exists boolean; + BEGIN + exists := EXISTS(SELECT 1 FROM modifier WHERE "effect" = NEW.effect); + IF NOT exists THEN + NEW."modifierId" := nextval('modifier_id_seq'); + + ELSIF exists THEN + NEW."modifierId" := (SELECT "modifierId" FROM modifier WHERE "effect" = NEW.effect LIMIT 1); + END IF; + + RETURN NEW; + END; + ${name}$ LANGUAGE plpgsql; + """, + """ + BEFORE INSERT ON {table} + FOR EACH ROW + EXECUTE FUNCTION {name}(); + """, +) + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + + # droping old item_modifier key + op.drop_constraint( + op.f("item_modifier_modifierId_fkey"), "item_modifier", type_="foreignkey" + ) + op.drop_constraint( + op.f("item_modifier_modifierId_fkey1"), "item_modifier", type_="foreignkey" + ) + + # Dropping old modifier keys + op.f("""ALTER TABLE modifier ALTER COLUMN "modifierId" DROP IDENTITY;""") + op.drop_constraint(op.f("modifier_pkey"), "modifier", type_="primary") + op.drop_constraint( + op.f("modifier_modifierId_position_key"), "modifier", type_="unique" + ) + + # creating new keys + op.create_primary_key("modifier_pkey", "modifier", ["modifierId", "position"]) + op.add_column( + "item_modifier", sa.Column("position", sa.SmallInteger(), nullable=False) + ) + op.create_foreign_key( + "fk_item_modifier_modfierId_position", + "item_modifier", + "modifier", + ["modifierId", "position"], + ["modifierId", "position"], + ondelete="CASCADE", + onupdate="CASCADE", + ) + + op.execute(""" + UPDATE modifier AS m + SET "modifierId" = "modifierId" + 10000; + """) + + op.execute(""" + WITH subquery as (SELECT + DENSE_RANK() OVER ( + ORDER BY m.effect + ) AS newModifierId, + m."position", + m."modifierId" AS modifierId + FROM modifier AS m + ORDER BY newModifierId, m.position) + + UPDATE modifier AS m + SET "modifierId" = sq.newModifierId + FROM subquery AS sq + WHERE "modifierId" = sq.modifierId; + """) + + op.execute("CREATE SEQUENCE modifier_id_seq;") + op.execute( + """SELECT setval('modifier_id_seq', (SELECT MAX("modifierId") FROM modifier));""" + ) + + op.create_trigger(modifier_id_trigger) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_trigger(modifier_id_trigger) + + # dropping new modifier keys + op.drop_constraint( + op.f("fk_item_modifier_modfierId_position"), "item_modifier", type_="foreignkey" + ) + op.drop_column("item_modifier", "position") + op.drop_constraint(op.f("modifier_pkey"), "modifier", type_="primary") + + op.execute(""" + WITH subquery as (SELECT + ROW_NUMBER() OVER(ORDER BY "modifierId", position ASC) AS newModifierId, + m."position", + m."modifierId" AS modifierId + FROM modifier AS m + ORDER BY newModifierId, m.position) + + UPDATE modifier AS m + SET "modifierId" = sq.newModifierId + FROM subquery AS sq + WHERE "modifierId" = sq.modifierId + AND m.position = sq.position; + """) + + op.execute(""" + DROP SEQUENCE modifier_id_seq; + """) + + # adding old keys + op.create_primary_key("modifier_pkey", "modifier", ["modifierId"]) + op.create_unique_constraint( + op.f("modifier_modifierId_position_key"), + "modifier", + ["modifierId", "position"], + postgresql_nulls_not_distinct=False, + ) + + op.create_foreign_key( + "item_modifier_modifierId_fkey", + "item_modifier", + "modifier", + ["modifierId"], + ["modifierId"], + ) + op.create_foreign_key( + "item_modifier_modifierId_fkey1", + "item_modifier", + "modifier", + ["modifierId"], + ["modifierId"], + ) + + op.alter_column( + "modifier", + "modifierId", + existing_type=sa.SMALLINT(), + server_default=sa.Identity( + always=False, + start=1, + increment=1, + minvalue=1, + maxvalue=32767, + cycle=True, + cache=1, + ), + existing_nullable=False, + ) + op.execute( + """SELECT setval(pg_get_serial_sequence('modifier', 'modifierId'), (SELECT MAX("modifierId") FROM modifier));""" + ) + # ### end Alembic commands ### diff --git a/src/backend_api/app/alembic/versions/e38727349f3f_added_unidentified_aggregation_job.py b/src/backend_api/app/alembic/versions/e38727349f3f_added_unidentified_aggregation_job.py index 30bef2fe1..47942eee0 100644 --- a/src/backend_api/app/alembic/versions/e38727349f3f_added_unidentified_aggregation_job.py +++ b/src/backend_api/app/alembic/versions/e38727349f3f_added_unidentified_aggregation_job.py @@ -13,7 +13,6 @@ from app.alembic.replaceable_objects.main import ReplaceableTrigger - # revision identifiers, used by Alembic. revision: str = "e38727349f3f" down_revision: Union[str, None] = "0f3f15f56b7d" @@ -25,7 +24,7 @@ "aggregate_unidentified", "unidentified_item", """ - RETURNS TRIGGER AS $aggregate_unidentified$ + RETURNS TRIGGER AS ${name}$ DECLARE current_hour INT; divine_id INT; @@ -65,12 +64,12 @@ RETURN NEW; END; - $aggregate_unidentified$ LANGUAGE plpgsql; + ${name}$ LANGUAGE plpgsql; """, """ - BEFORE INSERT ON unidentified_item + BEFORE INSERT ON {table} FOR EACH ROW - EXECUTE FUNCTION aggregate_unidentified(); + EXECUTE FUNCTION {name}(); """, ) diff --git a/src/backend_api/app/api/routes/modifier.py b/src/backend_api/app/api/routes/modifier.py index 2a55852e0..8621463d0 100644 --- a/src/backend_api/app/api/routes/modifier.py +++ b/src/backend_api/app/api/routes/modifier.py @@ -144,6 +144,7 @@ async def create_modifier( ) async def update_modifier( modifierId: int, + position: int, modifier_update: schemas.ModifierUpdate, db: Session = Depends(get_db), ): @@ -155,7 +156,7 @@ async def update_modifier( Returns the updated modifier. """ - modifier_map = {"modifierId": modifierId} + modifier_map = {"modifierId": modifierId, "position": position} modifier = await CRUD_modifier.get( db=db, @@ -172,6 +173,7 @@ async def update_modifier( ) async def delete_modifier( modifierId: int, + position: int, db: Session = Depends(get_db), ): """ @@ -181,7 +183,7 @@ async def delete_modifier( Always deletes one modifier. """ - modifier_map = {"modifierId": modifierId} + modifier_map = {"modifierId": modifierId, "position": position} await CRUD_modifier.remove(db=db, filter=modifier_map) return get_delete_return_msg( diff --git a/src/backend_api/app/core/models/models.py b/src/backend_api/app/core/models/models.py index 0a2bd2700..0f5951bf4 100644 --- a/src/backend_api/app/core/models/models.py +++ b/src/backend_api/app/core/models/models.py @@ -8,13 +8,14 @@ DateTime, Float, ForeignKey, + ForeignKeyConstraint, Identity, Index, Integer, + PrimaryKeyConstraint, SmallInteger, String, Text, - UniqueConstraint, func, ) from sqlalchemy.dialects.postgresql import JSONB, UUID @@ -144,11 +145,7 @@ class UnidentifiedItem(_ItemBase, Base): class Modifier(Base): __tablename__ = "modifier" - modifierId: Mapped[int] = mapped_column( - SmallInteger, - Identity(start=1, increment=1, cycle=True), - primary_key=True, - ) + modifierId: Mapped[int] = mapped_column(SmallInteger, nullable=False) position: Mapped[int] = mapped_column(SmallInteger, nullable=False) minRoll: Mapped[float | None] = mapped_column(Float(4)) maxRoll: Mapped[float | None] = mapped_column(Float(4)) @@ -175,6 +172,7 @@ class Modifier(Base): ) __table_args__ = ( + PrimaryKeyConstraint("modifierId", "position"), CheckConstraint( """ CASE @@ -220,7 +218,6 @@ class Modifier(Base): """ modifier."maxRoll" >= modifier."minRoll" """, name="check_modifier_maxRoll_greaterThan_minRoll", ), - UniqueConstraint(modifierId, position), ) @@ -232,11 +229,10 @@ class ItemModifier(Base): modifierId: Mapped[int] = mapped_column( SmallInteger, - ForeignKey( - "modifier.modifierId", - ondelete="CASCADE", - onupdate="CASCADE", - ), + nullable=False, + ) + position: Mapped[int] = mapped_column( + SmallInteger, nullable=False, ) createdHoursSinceLaunch: Mapped[int] = mapped_column(SmallInteger, nullable=False) @@ -249,6 +245,12 @@ class ItemModifier(Base): Float(4), ) __table_args__ = ( + ForeignKeyConstraint( + ["modifierId", "position"], + ["modifier.modifierId", "modifier.position"], + ondelete="CASCADE", + onupdate="CASCADE", + ), Index( "ix_item_modifierId_createdHoursSinceLaunch_roll_itemId", "modifierId", diff --git a/src/backend_api/app/core/schemas/item_modifier.py b/src/backend_api/app/core/schemas/item_modifier.py index ca2d8630e..953f9a1c5 100644 --- a/src/backend_api/app/core/schemas/item_modifier.py +++ b/src/backend_api/app/core/schemas/item_modifier.py @@ -7,6 +7,7 @@ class _BaseItemModifier(_pydantic.BaseModel): itemId: int modifierId: int + position: int roll: float | None = None diff --git a/src/backend_api/app/core/schemas/modifier.py b/src/backend_api/app/core/schemas/modifier.py index 999764436..75d2d6212 100644 --- a/src/backend_api/app/core/schemas/modifier.py +++ b/src/backend_api/app/core/schemas/modifier.py @@ -27,11 +27,12 @@ class _BaseModifier(_pydantic.BaseModel): class GroupedModifierProperties(_pydantic.BaseModel): - modifierId: list[int] + position: list[int] textRolls: list[str | None] class GroupedModifierByEffect(_pydantic.BaseModel): + modifierId: int effect: str regex: str static: bool | None diff --git a/src/backend_api/app/crud/extensions/crud_modifier.py b/src/backend_api/app/crud/extensions/crud_modifier.py index 864e6bf61..6b67b86fc 100644 --- a/src/backend_api/app/crud/extensions/crud_modifier.py +++ b/src/backend_api/app/crud/extensions/crud_modifier.py @@ -1,7 +1,5 @@ -import pandas as pd -from fastapi import HTTPException from pydantic import TypeAdapter -from sqlalchemy import select +from sqlalchemy import func, select from sqlalchemy.orm import Session from app.core.models.models import Modifier as model_Modifier @@ -25,51 +23,22 @@ class CRUDModifier( async def get_grouped_modifier_by_effect(self, db: Session): stmt = select( model_Modifier.modifierId, - model_Modifier.effect, - model_Modifier.regex, - model_Modifier.textRolls, - model_Modifier.relatedUniques, - model_Modifier.static, - ) - db_modifier_rows = db.execute(stmt).mappings().all() - - if not db_modifier_rows: - raise HTTPException( - status_code=404, - detail=f"No objects found in the table {self.model.__tablename__}.", - ) - - modifiers_df = pd.DataFrame(db_modifier_rows).sort_values(by="modifierId") - - grouped_modifier_df = modifiers_df.groupby( - ["effect", "regex", "static", "relatedUniques"], - as_index=False, - dropna=False, - sort=False, - ).agg(lambda x: list(x)) - - not_static_mask = grouped_modifier_df["static"].isna() - grouped_modifier_df.loc[not_static_mask, "static"] = None - grouped_modifier_df.loc[~not_static_mask, "regex"] = grouped_modifier_df.loc[ - ~not_static_mask, "effect" - ] - - # Stores the listed fields in a list of dicts - grouped_modifier_properties_record = grouped_modifier_df[ - ["modifierId", "textRolls"] - ].to_dict("records") - - # Removes the listed fields - grouped_modifier_df = grouped_modifier_df.drop( - ["modifierId", "textRolls"], axis=1 - ) - - # Adds the fields back in, but as a field with dicts - grouped_modifier_df[ - "groupedModifierProperties" - ] = grouped_modifier_properties_record - - grouped_modifier_by_effect_record = grouped_modifier_df.to_dict("records") + func.min(model_Modifier.effect).label("effect"), + func.coalesce( + func.min(model_Modifier.regex), + func.min(model_Modifier.effect), + ).label("regex"), + func.min(model_Modifier.relatedUniques).label("relatedUniques"), + func.bool_or(model_Modifier.static).label("static"), + func.json_build_object( + "position", + func.json_agg(model_Modifier.position), + "textRolls", + func.json_agg(model_Modifier.textRolls), + ).label("groupedModifierProperties"), + ).group_by(model_Modifier.modifierId) + + grouped_modifier_by_effect_record = db.execute(stmt).mappings().all() validate = TypeAdapter( GroupedModifierByEffect | list[GroupedModifierByEffect] diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/README.md b/src/backend_data_retrieval/data_retrieval_app/data_deposit/README.md index 363a672dc..5a7d5d5bb 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/README.md +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/README.md @@ -1,8 +1,8 @@ # How to deposit new data? 1. Ready file in csv-format - - Add comments that are recorded by relevant logger by starting initial lines with `#` - - Add the comments needed for data processing (See [Modifier Types](#modifier-types)) + - Add comments that are recorded by relevant logger by starting initial lines with `#` + - Add the comments needed for data processing (See [Modifier Types](#modifier-types)) 2. Paste file into `./{date type}/{data type}_data` 3. Run `main.py` @@ -17,9 +17,9 @@ Additional information is also needed, in order to make realistic test data. The | Field | Type | How to parse | Notes | | ---------------------------- | ------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Base Types | list\[str\] | Made into a list by splitting by `\|` | | -| Total modifiers on each item | list\[int\] | Made into a list by splitting by `\|` | | -| Can have duplicate modifiers | list\[bool\] | Made into a list by splitting by `\|` | Must have same length as number of keys in `Modifier distrubution`. If `True`, the same modifier can be chosen multiple times from the pool specified by `Modifier distrubution` | -| Modifier distrubution | dict\[int \| str, list\[int\]\] | Each key-value-pair is seperated by `,`, and the pair is then split by `:`. Values are made into lists by splitting by `\|` | The value tells you how many modifiers to choose from the pool specified by the key. If the length of the value list is greater than 1, a random elemnt must be chosen. The pool of modifiers to choose from is specified by the interval `[prev_key, key)` where `prev_key=0` if no key has previously been used and `key="rest"` == `key=len(dict)` | +| Total modifiers | list\[int\] | Made into a list by splitting by `\|` | | +| Can have duplicate modifiers | list\[bool\] | Made into a list by splitting by `\|` | Must have same length as number of keys in `Modifier distribution`. If `True`, the same modifier can be chosen multiple times from the pool specified by `Modifier distribution` | +| Modifier distribution | dict\[int \| str, list\[int\]\] | Each key-value-pair is seperated by `,`, and the pair is then split by `:`. Values are made into lists by splitting by `\|` | The value tells you how many modifiers to choose from the pool specified by the key. If the length of the value list is greater than 1, a random elemnt must be chosen. The pool of modifiers to choose from is specified by the interval `[prev_key, key)` where `prev_key=0` if no key has previously been used and `key="rest"` == `key=len(dict)` | ## Item Base types diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data_depositor.py b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data_depositor.py index 1728c95cd..7d16ee9f2 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data_depositor.py +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/modifier/modifier_data_depositor.py @@ -76,7 +76,7 @@ def _update_duplicates( by=["effect", "position"], ascending=False, inplace=True ) - update_url = self.data_url + "?modifierId={}" + update_url = self.data_url + "?modifierId={}&position={}" rolls = None update_regex = False @@ -151,7 +151,7 @@ def _update_duplicates( headers.update(self.pom_auth_headers) try: response = requests.put( - update_url.format(row_cur["modifierId"]), + update_url.format(row_cur["modifierId"], row_cur["position"]), json=data, headers=headers, # add HTTP Basic Auth diff --git a/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/detectors/unique_detector.py b/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/detectors/unique_detector.py index ae55b3910..118203542 100644 --- a/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/detectors/unique_detector.py +++ b/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/detectors/unique_detector.py @@ -58,6 +58,7 @@ class UniqueUnidentifiedDetector(UniqueDetector): "Great Crown", "Simple Robe", "Leather Belt", + "Heavy Belt", ] wanted_item_icons = { diff --git a/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/transforming_data/transform_poe_api_data.py b/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/transforming_data/transform_poe_api_data.py index df4c2157d..70a0da357 100644 --- a/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/transforming_data/transform_poe_api_data.py +++ b/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/transforming_data/transform_poe_api_data.py @@ -463,6 +463,7 @@ def item_modifier_table_columns_to_not_drop(self) -> set[str]: dont_drop_columns = { "itemId", "modifierId", + "position", "roll", "createdHoursSinceLaunch", } diff --git a/src/backend_data_retrieval/data_retrieval_app/tests/scripts/create_public_stashes_test_data/utils/data_deposit_test_data_creator.py b/src/backend_data_retrieval/data_retrieval_app/tests/scripts/create_public_stashes_test_data/utils/data_deposit_test_data_creator.py index 11b415d2f..90b32099d 100644 --- a/src/backend_data_retrieval/data_retrieval_app/tests/scripts/create_public_stashes_test_data/utils/data_deposit_test_data_creator.py +++ b/src/backend_data_retrieval/data_retrieval_app/tests/scripts/create_public_stashes_test_data/utils/data_deposit_test_data_creator.py @@ -111,35 +111,32 @@ def _parse_comment( return output - def _find_connected_modifier_ids_and_rolls( + def _find_modifier_and_rolls( self, modifier_df: pd.DataFrame, - modifier_distrubution: dict[int | str, list[int]], + modifier_distribution: dict[int | str, list[int]], ) -> tuple[ dict[int | str, list[list[int]]], dict[int | str, list[tuple[int, int] | str]] ]: """ - Example input modifier_distrubution dict: + Example input modifier_distribution dict: { - 3: [1], + 3: [2], "rest": [1] } Example output connected_modifier_ids_dict: { 3: [ - [1], - [5, 6] + 1, + 5 ], "rest": [ - [1] + 2 ] } Example output connected_rolls_dict: { - 3: [ - [1, 20], - "Balbala|Xibaqua" - ], + 3: {1: {"rolls": [[1,20], "Balbala|Xibaqua"]}, 5: {"rolls": [[1,4]]}}, "rest": [ [] ] @@ -150,14 +147,13 @@ def _find_connected_modifier_ids_and_rolls( roll is seperated by `|` """ - def get_modifier_ids_rolls_effect( + def get_roll_ranges( row: pd.Series, ) -> pd.Series: - choosable_modifier_ids = row["groupedModifierProperties"]["modifierId"] - effect = row["effect"] + modifier_id = row["modifierId"] df: pd.DataFrame = self.db_modifier_df.loc[ - self.db_modifier_df["effect"] == effect + self.db_modifier_df["modifierId"] == modifier_id ] not_static = all(df["static"].isna()) rolls = [] @@ -178,19 +174,15 @@ def get_modifier_ids_rolls_effect( .to_list() ) - return pd.Series( - { - "modifier_ids": choosable_modifier_ids, - "rolls": rolls, - "effect": effect, - } - ) + # return pd.Series({"modifier_id": modifier_id, "rolls": rolls}, index=) + # return {modifier_id: rolls} + return pd.Series({"modifier_id": modifier_id, "rolls": rolls}) grouped_modifier_df = self.grouped_modifier_df prev_key = 0 connected_modifier_ids_dict = {} connected_rolls_dict = {} - for key in modifier_distrubution: + for key in modifier_distribution: upper_bound = key if key == "rest": upper_bound = len(modifier_df) @@ -213,24 +205,18 @@ def get_modifier_ids_rolls_effect( choosable_effects_mask ] - modifier_ids_rolls_effect_df = choosable_grouped_modifier_df.apply( - get_modifier_ids_rolls_effect, axis=1 - ) + modifier_ids = choosable_grouped_modifier_df["modifierId"].to_list() + effects = choosable_grouped_modifier_df["effect"].to_list() - modifier_ids: list[list[int]] = modifier_ids_rolls_effect_df[ - "modifier_ids" - ].to_list() connected_modifier_ids_dict[key] = modifier_ids - - rolls = modifier_ids_rolls_effect_df["rolls"].to_list() + rolls = choosable_grouped_modifier_df.apply(get_roll_ranges, axis=1) + rolls.set_index("modifier_id", inplace=True) + rolls = rolls.to_dict()["rolls"] connected_rolls_dict[key] = rolls - effects: list[list[str]] = modifier_ids_rolls_effect_df["effect"].to_list() for modifier_id, effect in zip(modifier_ids, effects, strict=True): - if tuple(modifier_id) not in self.modifier_ids_to_effect_map: - self.modifier_ids_to_effect_map[tuple(modifier_id)] = effect - - prev_key = upper_bound + if modifier_id not in self.modifier_ids_to_effect_map: + self.modifier_ids_to_effect_map[modifier_id] = effect return connected_modifier_ids_dict, connected_rolls_dict @@ -263,25 +249,26 @@ def create_templates(self) -> None: modifier_comments["Can have duplicate modifiers"] ) - if "Modifier distrubution" in modifier_comments: - modifier_template["distrubution"] = self._parse_comment( - modifier_comments["Modifier distrubution"] + if "Modifier distribution" in modifier_comments: + modifier_template["distribution"] = self._parse_comment( + modifier_comments["Modifier distribution"] ) else: - modifier_template["distrubution"] = {"rest": 6} + modifier_template["distribution"] = {"rest": 6} ( modifier_template["modifier_ids_to_choose"], modifier_template["roll_ranges"], - ) = self._find_connected_modifier_ids_and_rolls( - modifier_df, modifier_template["distrubution"] + ) = self._find_modifier_and_rolls( + modifier_df, modifier_template["distribution"] ) + # Check for equal lengths taken from: # https://stackoverflow.com/questions/35791051/better-way-to-check-if-all-lists-in-a-list-are-the-same-length it = iter( [ modifier_template["can_duplicate"], - modifier_template["distrubution"].keys(), + modifier_template["distribution"].keys(), modifier_template["modifier_ids_to_choose"].keys(), modifier_template["roll_ranges"].keys(), ] @@ -293,7 +280,7 @@ def create_templates(self) -> None: + str( [ len(modifier_template["can_duplicate"]), - len(modifier_template["distrubution"].keys()), + len(modifier_template["distribution"].keys()), len(modifier_template["modifier_ids_to_choose"].keys()), len(modifier_template["roll_ranges"].keys()), ] @@ -345,35 +332,34 @@ def _choose_and_make_modifiers( Note: Only works when len(can_duplicate) == - len(distrubution.keys()) + len(distribution.keys()) == len(modifier_ids_to_choose.keys()) == len(roll_ranges.keys()) """ can_duplicate = template["can_duplicate"] - distrubution = template["distrubution"] + distribution = template["distribution"] modifier_ids_to_choose = template["modifier_ids_to_choose"] roll_ranges = template["roll_ranges"] modifiers = [] - for i, key in enumerate(distrubution): - n_modifiers_to_create: int = random.choice(distrubution[key]) + for i, key in enumerate(distribution): + n_modifiers_to_create: int = random.choice(distribution[key]) remove_chosen_modifiers_from_pool: bool = not can_duplicate[i] - modifier_ids_to_choose_from: list[list[int]] = modifier_ids_to_choose[ + modifier_ids_to_choose_from: list[int] = modifier_ids_to_choose[key].copy() + complementing_roll_ranges: dict[int, list[str | list[float]]] = roll_ranges[ key ].copy() - complementing_roll_ranges: list[str | list[float]] = roll_ranges[key].copy() for _ in range(n_modifiers_to_create): - choice_made = random.choice(range(len(modifier_ids_to_choose_from))) + modifier_id = random.choice(modifier_ids_to_choose_from) - chosen_modifier_ids = modifier_ids_to_choose_from[choice_made] - chosen_rolls = complementing_roll_ranges[choice_made] + chosen_rolls = complementing_roll_ranges[modifier_id] - effect = self.modifier_ids_to_effect_map[tuple(chosen_modifier_ids)] + effect = self.modifier_ids_to_effect_map[modifier_id] if chosen_rolls: # equivalent to modifier not being static @@ -383,9 +369,8 @@ def _choose_and_make_modifiers( if remove_chosen_modifiers_from_pool or not chosen_rolls: # not chosen_rolls -> static, can't have multiple static modifiers - modifier_ids_to_choose_from.pop(choice_made) - complementing_roll_ranges.pop(choice_made) - + modifier_ids_to_choose_from.remove(modifier_id) + complementing_roll_ranges.pop(modifier_id) return modifiers def _create_item_dict_from_template( @@ -427,12 +412,16 @@ def _create_item_dict_from_template( def create_test_data( self, ) -> Iterator[tuple[str, list[dict[str, Any]]]]: - for filename, template in self.templates.items(): - stash = [] - for _ in range(self.n_of_items): - item_dict = self._create_item_dict_from_template(template) - stash.append(item_dict) - yield filename, stash + try: + for filename, template in self.templates.items(): + stash = [] + for _ in range(self.n_of_items): + item_dict = self._create_item_dict_from_template(template) + stash.append(item_dict) + yield filename, stash + except Exception as e: + test_logger.exception(f"An error occurred while creating test data: {e}") + raise e def main() -> int: From 61e76c66cabdc3b7ea634b6e8bd6cfd077082b26 Mon Sep 17 00:00:00 2001 From: Bogadisa Date: Mon, 18 May 2026 15:22:10 -0230 Subject: [PATCH 3/8] #812 Fixed plotting backend --- .../app/core/schemas/plot/__init__.py | 2 +- .../app/core/schemas/plot/input.py | 9 ++++--- src/backend_api/app/plotting/plotter.py | 25 ++++++++----------- .../external_data_retrieval/config.py | 4 ++- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/backend_api/app/core/schemas/plot/__init__.py b/src/backend_api/app/core/schemas/plot/__init__.py index 6a66bd346..0de392497 100644 --- a/src/backend_api/app/core/schemas/plot/__init__.py +++ b/src/backend_api/app/core/schemas/plot/__init__.py @@ -6,6 +6,6 @@ BaseSpecs, ItemSpecs, WantedModifier, - ModifierLimitations, + ModifierLimitation, ) from .output import PlotData diff --git a/src/backend_api/app/core/schemas/plot/input.py b/src/backend_api/app/core/schemas/plot/input.py index f5052ab58..a7f333eb2 100644 --- a/src/backend_api/app/core/schemas/plot/input.py +++ b/src/backend_api/app/core/schemas/plot/input.py @@ -26,7 +26,8 @@ class BaseSpecs(_pydantic.BaseModel): subCategory: str | None = None -class ModifierLimitations(_pydantic.BaseModel): +class ModifierLimitation(_pydantic.BaseModel): + position: int maxRoll: float | None = None minRoll: float | None = None textRoll: int | None = None @@ -34,7 +35,7 @@ class ModifierLimitations(_pydantic.BaseModel): class WantedModifier(_pydantic.BaseModel): modifierId: int - modifierLimitations: ModifierLimitations | None = None + modifierLimitations: list[ModifierLimitation] | None = None class BasePlotQuery(_pydantic.BaseModel): @@ -48,13 +49,13 @@ class BasePlotQuery(_pydantic.BaseModel): class PlotQuery(BasePlotQuery): "Plots for items with or without modifiers" - wantedModifiers: list[list[WantedModifier]] | None = None + wantedModifiers: list[WantedModifier] | None = None class IdentifiedPlotQuery(BasePlotQuery): "Plots for items with modifiers" - wantedModifiers: list[list[WantedModifier]] + wantedModifiers: list[WantedModifier] class UnidentifiedPlotQuery(BasePlotQuery): diff --git a/src/backend_api/app/plotting/plotter.py b/src/backend_api/app/plotting/plotter.py index 29b101d53..bbdb033eb 100644 --- a/src/backend_api/app/plotting/plotter.py +++ b/src/backend_api/app/plotting/plotter.py @@ -43,7 +43,7 @@ BasePlotQuery, IdentifiedPlotQuery, ItemSpecs, - ModifierLimitations, + ModifierLimitation, PlotData, PlotQuery, UnidentifiedPlotQuery, @@ -277,7 +277,7 @@ def _raise_invalid_query(self, query: PlotQuery) -> None: ) def _check_rolls( - self, modifier_limitations: ModifierLimitations + self, modifier_limitations: ModifierLimitation ) -> ColumnElement[bool] | BinaryExpression[bool]: if modifier_limitations.textRoll is not None: return model_ItemModifier.roll == modifier_limitations.textRoll @@ -297,7 +297,7 @@ def _add_wanted_modifiers( self, statement: Select, *, - wanted_modifier_query: list[list[WantedModifier]], + wanted_modifier_query: list[WantedModifier], start: int | None, end: int | None, ) -> Select: @@ -308,20 +308,17 @@ def _add_wanted_modifiers( one is chosen. """ exists_conditions = [] - wanted_modifier = None # Gets set to the last mod after loop below - for grouped_wanted_modifier in wanted_modifier_query: - modifier_roll_limitation_found = False - for wanted_modifier in grouped_wanted_modifier: - if wanted_modifier.modifierLimitations is not None: - modifier_roll_limitation_found = True - roll_condition = self._check_rolls( - wanted_modifier.modifierLimitations - ) + for wanted_modifier in wanted_modifier_query: + if wanted_modifier.modifierLimitations is not None: + for modifier_limitation in wanted_modifier.modifierLimitations: + roll_condition = self._check_rolls(modifier_limitation) + and_conditions = [ model_Item.itemId == model_ItemModifier.itemId, model_ItemModifier.modifierId == wanted_modifier.modifierId, roll_condition, ] + if start is not None: and_conditions.append( model_ItemModifier.createdHoursSinceLaunch >= start @@ -331,7 +328,6 @@ def _add_wanted_modifiers( and_conditions.append( model_ItemModifier.createdHoursSinceLaunch <= end ) - exists_conditions.append( select(1) .where( @@ -342,8 +338,7 @@ def _add_wanted_modifiers( ) .exists() ) - - if wanted_modifier and not modifier_roll_limitation_found: + else: and_conditions = [ model_Item.itemId == model_ItemModifier.itemId, model_ItemModifier.modifierId == wanted_modifier.modifierId, diff --git a/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/config.py b/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/config.py index 3f47e30ce..1375ea524 100644 --- a/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/config.py +++ b/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/config.py @@ -28,16 +28,18 @@ def BACKEND_BASE_URL(self) -> HttpUrl: FIRST_SUPERUSER: str FIRST_SUPERUSER_PASSWORD: str CURRENT_SOFTCORE_LEAGUE: str + @computed_field # type: ignore[prop-decorator] @property def CURRENT_HARDCORE_LEAGUE(self) -> str: return f"Hardcore {self.CURRENT_SOFTCORE_LEAGUE}" + POE_PUBLIC_STASHES_AUTH_TOKEN: str OAUTH_CLIENT_ID: str OAUTH_CLIENT_SECRET: str MINI_BATCH_SIZE: int = 30 - N_CHECKPOINTS_PER_TRANSFORMATION: int = 10 + N_CHECKPOINTS_PER_TRANSFORMATION: int = 1 TIME_BETWEEN_RESTART: int = 3600 MAX_TIME_PER_MINI_BATCH: int = 3 * 60 From 87e6396c39a57f7d3403183e7537b333d49c09f0 Mon Sep 17 00:00:00 2001 From: Bogadisa Date: Mon, 18 May 2026 19:21:24 -0230 Subject: [PATCH 4/8] #812 Fixed frontend modifier handling --- src/frontend/openapi.json | 87 ++++++-- src/frontend/src/client/index.ts | 4 +- .../client/models/GroupedModifierByEffect.ts | 1 + .../models/GroupedModifierProperties.ts | 2 +- src/frontend/src/client/models/Item.ts | 1 + src/frontend/src/client/models/ItemCreate.ts | 1 + .../src/client/models/ItemModifier.ts | 1 + .../src/client/models/ItemModifierCreate.ts | 1 + ...erLimitations.ts => ModifierLimitation.ts} | 3 +- src/frontend/src/client/models/PlotQuery.ts | 2 +- .../src/client/models/WantedModifier.ts | 4 +- .../schemas/$GroupedModifierByEffect.ts | 4 + .../schemas/$GroupedModifierProperties.ts | 2 +- src/frontend/src/client/schemas/$Item.ts | 8 + .../src/client/schemas/$ItemCreate.ts | 8 + .../src/client/schemas/$ItemModifier.ts | 4 + .../src/client/schemas/$ItemModifierCreate.ts | 4 + ...rLimitations.ts => $ModifierLimitation.ts} | 6 +- src/frontend/src/client/schemas/$PlotQuery.ts | 5 +- .../src/client/schemas/$WantedModifier.ts | 5 +- .../src/client/services/ModifiersService.ts | 8 + .../ModifierInputComp/FancyModifierInput.tsx | 91 +++++--- .../FancySelectedModifier.tsx | 187 ++++++++--------- .../Input/ModifierInputComp/ModifierInput.tsx | 36 ++-- .../StandardLayoutInput/ErrorMessage.tsx | 2 +- src/frontend/src/hooks/graphing/utils.tsx | 45 +--- src/frontend/src/routeTree.gen.ts | 195 +++++++----------- src/frontend/src/store/GraphInputStore.tsx | 138 ++++++++++--- src/frontend/src/store/StateInterface.tsx | 13 +- 29 files changed, 497 insertions(+), 371 deletions(-) rename src/frontend/src/client/models/{ModifierLimitations.ts => ModifierLimitation.ts} (80%) rename src/frontend/src/client/schemas/{$ModifierLimitations.ts => $ModifierLimitation.ts} (84%) diff --git a/src/frontend/openapi.json b/src/frontend/openapi.json index 89ff6f5d3..fdf3cf208 100644 --- a/src/frontend/openapi.json +++ b/src/frontend/openapi.json @@ -1538,6 +1538,15 @@ "type": "integer", "title": "Modifierid" } + }, + { + "name": "position", + "in": "query", + "required": true, + "schema": { + "type": "integer", + "title": "Position" + } } ], "responses": { @@ -1793,6 +1802,15 @@ "type": "integer", "title": "Modifierid" } + }, + { + "name": "position", + "in": "query", + "required": true, + "schema": { + "type": "integer", + "title": "Position" + } } ], "requestBody": { @@ -2295,6 +2313,10 @@ }, "GroupedModifierByEffect": { "properties": { + "modifierId": { + "type": "integer", + "title": "Modifierid" + }, "effect": { "type": "string", "title": "Effect" @@ -2331,6 +2353,7 @@ }, "type": "object", "required": [ + "modifierId", "effect", "regex", "static", @@ -2341,12 +2364,12 @@ }, "GroupedModifierProperties": { "properties": { - "modifierId": { + "position": { "items": { "type": "integer" }, "type": "array", - "title": "Modifierid" + "title": "Position" }, "textRolls": { "items": { @@ -2365,7 +2388,7 @@ }, "type": "object", "required": [ - "modifierId", + "position", "textRolls" ], "title": "GroupedModifierProperties" @@ -2484,6 +2507,17 @@ "type": "string", "title": "Rarity" }, + "gameItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Gameitemid" + }, "identified": { "type": "boolean", "title": "Identified", @@ -2805,6 +2839,17 @@ "type": "string", "title": "Rarity" }, + "gameItemId": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "title": "Gameitemid" + }, "identified": { "type": "boolean", "title": "Identified", @@ -2977,6 +3022,10 @@ "type": "integer", "title": "Modifierid" }, + "position": { + "type": "integer", + "title": "Position" + }, "roll": { "anyOf": [ { @@ -2997,6 +3046,7 @@ "required": [ "itemId", "modifierId", + "position", "createdHoursSinceLaunch" ], "title": "ItemModifier" @@ -3011,6 +3061,10 @@ "type": "integer", "title": "Modifierid" }, + "position": { + "type": "integer", + "title": "Position" + }, "roll": { "anyOf": [ { @@ -3031,6 +3085,7 @@ "required": [ "itemId", "modifierId", + "position", "createdHoursSinceLaunch" ], "title": "ItemModifierCreate" @@ -3600,8 +3655,12 @@ ], "title": "ModifierCreate" }, - "ModifierLimitations": { + "ModifierLimitation": { "properties": { + "position": { + "type": "integer", + "title": "Position" + }, "maxRoll": { "anyOf": [ { @@ -3637,7 +3696,10 @@ } }, "type": "object", - "title": "ModifierLimitations" + "required": [ + "position" + ], + "title": "ModifierLimitation" }, "ModifierUpdate": { "properties": { @@ -3905,10 +3967,7 @@ "anyOf": [ { "items": { - "items": { - "$ref": "#/components/schemas/WantedModifier" - }, - "type": "array" + "$ref": "#/components/schemas/WantedModifier" }, "type": "array" }, @@ -4268,12 +4327,16 @@ "modifierLimitations": { "anyOf": [ { - "$ref": "#/components/schemas/ModifierLimitations" + "items": { + "$ref": "#/components/schemas/ModifierLimitation" + }, + "type": "array" }, { "type": "null" } - ] + ], + "title": "Modifierlimitations" } }, "type": "object", @@ -4295,4 +4358,4 @@ } } } -} \ No newline at end of file +} diff --git a/src/frontend/src/client/index.ts b/src/frontend/src/client/index.ts index f3b32a728..8ac0e3140 100644 --- a/src/frontend/src/client/index.ts +++ b/src/frontend/src/client/index.ts @@ -28,7 +28,7 @@ export type { ItemSpecs } from './models/ItemSpecs'; export type { MetadataObject } from './models/MetadataObject'; export type { Modifier } from './models/Modifier'; export type { ModifierCreate } from './models/ModifierCreate'; -export type { ModifierLimitations } from './models/ModifierLimitations'; +export type { ModifierLimitation } from './models/ModifierLimitation'; export type { ModifierUpdate } from './models/ModifierUpdate'; export type { PlotData } from './models/PlotData'; export type { PlotQuery } from './models/PlotQuery'; @@ -62,7 +62,7 @@ export { $ItemSpecs } from './schemas/$ItemSpecs'; export { $MetadataObject } from './schemas/$MetadataObject'; export { $Modifier } from './schemas/$Modifier'; export { $ModifierCreate } from './schemas/$ModifierCreate'; -export { $ModifierLimitations } from './schemas/$ModifierLimitations'; +export { $ModifierLimitation } from './schemas/$ModifierLimitation'; export { $ModifierUpdate } from './schemas/$ModifierUpdate'; export { $PlotData } from './schemas/$PlotData'; export { $PlotQuery } from './schemas/$PlotQuery'; diff --git a/src/frontend/src/client/models/GroupedModifierByEffect.ts b/src/frontend/src/client/models/GroupedModifierByEffect.ts index 83426c20f..6bbc0ce95 100644 --- a/src/frontend/src/client/models/GroupedModifierByEffect.ts +++ b/src/frontend/src/client/models/GroupedModifierByEffect.ts @@ -4,6 +4,7 @@ /* eslint-disable */ import type { GroupedModifierProperties } from './GroupedModifierProperties'; export type GroupedModifierByEffect = { + modifierId: number; effect: string; regex: string; static: (boolean | null); diff --git a/src/frontend/src/client/models/GroupedModifierProperties.ts b/src/frontend/src/client/models/GroupedModifierProperties.ts index 08775f3eb..79b70ac18 100644 --- a/src/frontend/src/client/models/GroupedModifierProperties.ts +++ b/src/frontend/src/client/models/GroupedModifierProperties.ts @@ -3,7 +3,7 @@ /* tslint:disable */ /* eslint-disable */ export type GroupedModifierProperties = { - modifierId: Array; + position: Array; textRolls: Array<(string | null)>; }; diff --git a/src/frontend/src/client/models/Item.ts b/src/frontend/src/client/models/Item.ts index 04007f7ac..f82c264ec 100644 --- a/src/frontend/src/client/models/Item.ts +++ b/src/frontend/src/client/models/Item.ts @@ -9,6 +9,7 @@ export type Item = { itemBaseTypeId: number; ilvl: number; rarity: string; + gameItemId?: (string | null); identified?: boolean; currencyAmount?: (number | null); currencyId?: (number | null); diff --git a/src/frontend/src/client/models/ItemCreate.ts b/src/frontend/src/client/models/ItemCreate.ts index 00a32c350..e371bdcb5 100644 --- a/src/frontend/src/client/models/ItemCreate.ts +++ b/src/frontend/src/client/models/ItemCreate.ts @@ -9,6 +9,7 @@ export type ItemCreate = { itemBaseTypeId: number; ilvl: number; rarity: string; + gameItemId?: (string | null); identified?: boolean; currencyAmount?: (number | null); currencyId?: (number | null); diff --git a/src/frontend/src/client/models/ItemModifier.ts b/src/frontend/src/client/models/ItemModifier.ts index 54283e291..c47396310 100644 --- a/src/frontend/src/client/models/ItemModifier.ts +++ b/src/frontend/src/client/models/ItemModifier.ts @@ -5,6 +5,7 @@ export type ItemModifier = { itemId: number; modifierId: number; + position: number; roll?: (number | null); createdHoursSinceLaunch: number; }; diff --git a/src/frontend/src/client/models/ItemModifierCreate.ts b/src/frontend/src/client/models/ItemModifierCreate.ts index b58dbba58..a4d74b9ec 100644 --- a/src/frontend/src/client/models/ItemModifierCreate.ts +++ b/src/frontend/src/client/models/ItemModifierCreate.ts @@ -5,6 +5,7 @@ export type ItemModifierCreate = { itemId: number; modifierId: number; + position: number; roll?: (number | null); createdHoursSinceLaunch: number; }; diff --git a/src/frontend/src/client/models/ModifierLimitations.ts b/src/frontend/src/client/models/ModifierLimitation.ts similarity index 80% rename from src/frontend/src/client/models/ModifierLimitations.ts rename to src/frontend/src/client/models/ModifierLimitation.ts index e67c6b45e..1fe9ed89f 100644 --- a/src/frontend/src/client/models/ModifierLimitations.ts +++ b/src/frontend/src/client/models/ModifierLimitation.ts @@ -2,7 +2,8 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export type ModifierLimitations = { +export type ModifierLimitation = { + position: number; maxRoll?: (number | null); minRoll?: (number | null); textRoll?: (number | null); diff --git a/src/frontend/src/client/models/PlotQuery.ts b/src/frontend/src/client/models/PlotQuery.ts index 759b413aa..d00b12cff 100644 --- a/src/frontend/src/client/models/PlotQuery.ts +++ b/src/frontend/src/client/models/PlotQuery.ts @@ -14,6 +14,6 @@ export type PlotQuery = { baseSpecifications?: (BaseSpecs | null); end?: (number | null); start?: (number | null); - wantedModifiers?: (Array> | null); + wantedModifiers?: (Array | null); }; diff --git a/src/frontend/src/client/models/WantedModifier.ts b/src/frontend/src/client/models/WantedModifier.ts index 06ab2d513..9dc1ad28c 100644 --- a/src/frontend/src/client/models/WantedModifier.ts +++ b/src/frontend/src/client/models/WantedModifier.ts @@ -2,9 +2,9 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -import type { ModifierLimitations } from './ModifierLimitations'; +import type { ModifierLimitation } from './ModifierLimitation'; export type WantedModifier = { modifierId: number; - modifierLimitations?: (ModifierLimitations | null); + modifierLimitations?: (Array | null); }; diff --git a/src/frontend/src/client/schemas/$GroupedModifierByEffect.ts b/src/frontend/src/client/schemas/$GroupedModifierByEffect.ts index 92fa02115..47241a172 100644 --- a/src/frontend/src/client/schemas/$GroupedModifierByEffect.ts +++ b/src/frontend/src/client/schemas/$GroupedModifierByEffect.ts @@ -4,6 +4,10 @@ /* eslint-disable */ export const $GroupedModifierByEffect = { properties: { + modifierId: { + type: 'number', + isRequired: true, + }, effect: { type: 'string', isRequired: true, diff --git a/src/frontend/src/client/schemas/$GroupedModifierProperties.ts b/src/frontend/src/client/schemas/$GroupedModifierProperties.ts index 3b5785610..4f9e454b0 100644 --- a/src/frontend/src/client/schemas/$GroupedModifierProperties.ts +++ b/src/frontend/src/client/schemas/$GroupedModifierProperties.ts @@ -4,7 +4,7 @@ /* eslint-disable */ export const $GroupedModifierProperties = { properties: { - modifierId: { + position: { type: 'array', contains: { type: 'number', diff --git a/src/frontend/src/client/schemas/$Item.ts b/src/frontend/src/client/schemas/$Item.ts index ffd5e86b3..6beee37e6 100644 --- a/src/frontend/src/client/schemas/$Item.ts +++ b/src/frontend/src/client/schemas/$Item.ts @@ -28,6 +28,14 @@ export const $Item = { type: 'string', isRequired: true, }, + gameItemId: { + type: 'any-of', + contains: [{ + type: 'string', + }, { + type: 'null', + }], + }, identified: { type: 'boolean', }, diff --git a/src/frontend/src/client/schemas/$ItemCreate.ts b/src/frontend/src/client/schemas/$ItemCreate.ts index b8b441801..1995e1f36 100644 --- a/src/frontend/src/client/schemas/$ItemCreate.ts +++ b/src/frontend/src/client/schemas/$ItemCreate.ts @@ -28,6 +28,14 @@ export const $ItemCreate = { type: 'string', isRequired: true, }, + gameItemId: { + type: 'any-of', + contains: [{ + type: 'string', + }, { + type: 'null', + }], + }, identified: { type: 'boolean', }, diff --git a/src/frontend/src/client/schemas/$ItemModifier.ts b/src/frontend/src/client/schemas/$ItemModifier.ts index 88958f943..4a505a920 100644 --- a/src/frontend/src/client/schemas/$ItemModifier.ts +++ b/src/frontend/src/client/schemas/$ItemModifier.ts @@ -12,6 +12,10 @@ export const $ItemModifier = { type: 'number', isRequired: true, }, + position: { + type: 'number', + isRequired: true, + }, roll: { type: 'any-of', contains: [{ diff --git a/src/frontend/src/client/schemas/$ItemModifierCreate.ts b/src/frontend/src/client/schemas/$ItemModifierCreate.ts index e1e26f7a4..cf1aeb4bb 100644 --- a/src/frontend/src/client/schemas/$ItemModifierCreate.ts +++ b/src/frontend/src/client/schemas/$ItemModifierCreate.ts @@ -12,6 +12,10 @@ export const $ItemModifierCreate = { type: 'number', isRequired: true, }, + position: { + type: 'number', + isRequired: true, + }, roll: { type: 'any-of', contains: [{ diff --git a/src/frontend/src/client/schemas/$ModifierLimitations.ts b/src/frontend/src/client/schemas/$ModifierLimitation.ts similarity index 84% rename from src/frontend/src/client/schemas/$ModifierLimitations.ts rename to src/frontend/src/client/schemas/$ModifierLimitation.ts index e11223711..92d9460d3 100644 --- a/src/frontend/src/client/schemas/$ModifierLimitations.ts +++ b/src/frontend/src/client/schemas/$ModifierLimitation.ts @@ -2,8 +2,12 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export const $ModifierLimitations = { +export const $ModifierLimitation = { properties: { + position: { + type: 'number', + isRequired: true, + }, maxRoll: { type: 'any-of', contains: [{ diff --git a/src/frontend/src/client/schemas/$PlotQuery.ts b/src/frontend/src/client/schemas/$PlotQuery.ts index b930568e5..502d1d031 100644 --- a/src/frontend/src/client/schemas/$PlotQuery.ts +++ b/src/frontend/src/client/schemas/$PlotQuery.ts @@ -54,10 +54,7 @@ export const $PlotQuery = { contains: [{ type: 'array', contains: { - type: 'array', - contains: { - type: 'WantedModifier', - }, + type: 'WantedModifier', }, }, { type: 'null', diff --git a/src/frontend/src/client/schemas/$WantedModifier.ts b/src/frontend/src/client/schemas/$WantedModifier.ts index c14035855..f4ece94ec 100644 --- a/src/frontend/src/client/schemas/$WantedModifier.ts +++ b/src/frontend/src/client/schemas/$WantedModifier.ts @@ -11,7 +11,10 @@ export const $WantedModifier = { modifierLimitations: { type: 'any-of', contains: [{ - type: 'ModifierLimitations', + type: 'array', + contains: { + type: 'ModifierLimitation', + }, }, { type: 'null', }], diff --git a/src/frontend/src/client/services/ModifiersService.ts b/src/frontend/src/client/services/ModifiersService.ts index d63b9ccfe..840e2ada3 100644 --- a/src/frontend/src/client/services/ModifiersService.ts +++ b/src/frontend/src/client/services/ModifiersService.ts @@ -48,8 +48,10 @@ export class ModifiersService { */ public static deleteModifier({ modifierId, + position, }: { modifierId: number, + position: number, }): CancelablePromise { return __request(OpenAPI, { method: 'DELETE', @@ -57,6 +59,9 @@ export class ModifiersService { path: { 'modifierId': modifierId, }, + query: { + 'position': position, + }, errors: { 422: `Validation Error`, }, @@ -135,9 +140,11 @@ export class ModifiersService { */ public static updateModifier({ modifierId, + position, requestBody, }: { modifierId: number, + position: number, requestBody: ModifierUpdate, }): CancelablePromise { return __request(OpenAPI, { @@ -145,6 +152,7 @@ export class ModifiersService { url: '/api/api_v1/modifier/', query: { 'modifierId': modifierId, + 'position': position, }, body: requestBody, mediaType: 'application/json', diff --git a/src/frontend/src/components/Input/ModifierInputComp/FancyModifierInput.tsx b/src/frontend/src/components/Input/ModifierInputComp/FancyModifierInput.tsx index 255079891..aee131314 100644 --- a/src/frontend/src/components/Input/ModifierInputComp/FancyModifierInput.tsx +++ b/src/frontend/src/components/Input/ModifierInputComp/FancyModifierInput.tsx @@ -5,10 +5,11 @@ import { DefaultMinMaxValues, } from "../StandardLayoutInput/MinMaxNumberInput"; import { useGraphInputStore } from "../../../store/GraphInputStore"; -import { WantedModifierExtended } from "../../../store/StateInterface"; +import { ModifierLimitationState } from "../../../store/StateInterface"; type HandleChangeEventFunction = ( modifierId: number, + position: number, value: string | undefined, selectedModifierIndex: number, numericalType?: "min" | "max", @@ -19,8 +20,9 @@ export type TakingInputEventFunction = (orderIndex: number) => void; interface InputChangeHandler { modifierId: number; + position: number; selectedModifierIndex: number; - currentRelevantModifierSpec: WantedModifierExtended; + modifierLimitation: ModifierLimitationState | null | undefined; orderIndex: number; isNumerical: boolean; textRolls: string | null | undefined; @@ -29,19 +31,18 @@ interface InputChangeHandler { } const InputChangeHandler = (props: InputChangeHandler) => { - const modifierLimitations = - props.currentRelevantModifierSpec.modifierLimitations; const textRolls = props.textRolls; if (props.isNumerical) { const defaultMinMaxValues: DefaultMinMaxValues = { - max: modifierLimitations?.maxRoll ?? undefined, - min: modifierLimitations?.minRoll ?? undefined, + max: props.modifierLimitation?.maxRoll ?? undefined, + min: props.modifierLimitation?.minRoll ?? undefined, }; return ( props.handleAnyChange( props.modifierId, + props.position, value, props.selectedModifierIndex, numericalType, @@ -59,7 +60,7 @@ const InputChangeHandler = (props: InputChangeHandler) => { } else if (textRolls == null) { throw "'textRolls' cannot be undefined at the same time as 'isNumerical===false'"; } else { - const defaultTextIndex = modifierLimitations?.textRoll ?? undefined; + const defaultTextIndex = props.modifierLimitation?.textRoll ?? undefined; let defaultTextValue: string | undefined = undefined; if (defaultTextIndex !== undefined) { defaultTextValue = textRolls.split("|")[defaultTextIndex]; @@ -71,6 +72,7 @@ const InputChangeHandler = (props: InputChangeHandler) => { handleTextChange={(value) => props.handleAnyChange( props.modifierId, + props.position, value, props.selectedModifierIndex, undefined, @@ -88,32 +90,34 @@ const InputChangeHandler = (props: InputChangeHandler) => { }; interface DefaultOutputHandlerProps { - currentRelevantModifierSpec: WantedModifierExtended; + modifierLimitation: ModifierLimitationState | null | undefined; isNumerical: boolean; textRolls: string | null | undefined; } const DefaultOutputHandler = (props: DefaultOutputHandlerProps) => { - const modifierLimitations = - props.currentRelevantModifierSpec.modifierLimitations; - if (modifierLimitations == null) { + if (props.modifierLimitation == null) { return #; } if (props.isNumerical) { if ( - modifierLimitations.minRoll == null && - modifierLimitations.maxRoll == null + props.modifierLimitation.minRoll == null && + props.modifierLimitation.maxRoll == null ) { return #; } return ( - {modifierLimitations.minRoll ? modifierLimitations.minRoll : "Min"} + {props.modifierLimitation.minRoll + ? props.modifierLimitation.minRoll + : "Min"} - {modifierLimitations.maxRoll ? modifierLimitations.maxRoll : "Max"} + {props.modifierLimitation.maxRoll + ? props.modifierLimitation.maxRoll + : "Max"} ); @@ -122,8 +126,8 @@ const DefaultOutputHandler = (props: DefaultOutputHandlerProps) => { } else { return ( - {modifierLimitations.textRoll != null - ? props.textRolls.split("|")[modifierLimitations.textRoll] + {props.modifierLimitation.textRoll != null + ? props.textRolls.split("|")[props.modifierLimitation.textRoll] : "#"} ); @@ -133,6 +137,7 @@ const DefaultOutputHandler = (props: DefaultOutputHandlerProps) => { interface FancyModifierInputProps { currentlyTakingInput: boolean; modifierId: number; + position: number; selectedModifierIndex: number; textRolls: string | null | undefined; orderIndex: number; @@ -151,6 +156,7 @@ export const FancyModifierInput = (props: FancyModifierInputProps) => { // A generic handle function that handles mixed input const handleAnyChange: HandleChangeEventFunction = ( modifierId: number, + position: number, value: string | undefined, selectedModifierIndex: number, numericalType?: "min" | "max", @@ -162,9 +168,19 @@ export const FancyModifierInput = (props: FancyModifierInputProps) => { if (numericalType !== undefined) { const numValue = value ? Number(value) : undefined; if (numericalType === "min") { - setWantedModifierMinRoll(modifierId, numValue, selectedModifierIndex); + setWantedModifierMinRoll( + modifierId, + position, + numValue, + selectedModifierIndex, + ); } else { - setWantedModifierMaxRoll(modifierId, numValue, selectedModifierIndex); + setWantedModifierMaxRoll( + modifierId, + position, + numValue, + selectedModifierIndex, + ); } } else if (textRolls) { if (value === "Any") { @@ -172,7 +188,12 @@ export const FancyModifierInput = (props: FancyModifierInputProps) => { } const textValue = value !== undefined ? textRolls.split("|").indexOf(value) : undefined; - setWantedModifierTextRoll(modifierId, textValue, selectedModifierIndex); + setWantedModifierTextRoll( + modifierId, + position, + textValue, + selectedModifierIndex, + ); } else { throw "Modifier must have text rolls if the roll is not numerical."; } @@ -184,21 +205,35 @@ export const FancyModifierInput = (props: FancyModifierInputProps) => { (spec) => spec.index == props.selectedModifierIndex, ); - const currentRelevantModifierSpec = - currentWantedModifierExtended[props.orderIndex]; + const currentModifierLimitations = currentWantedModifierExtended.find( + (wantedModifierExtended) => + wantedModifierExtended.modifierId === props.modifierId && + wantedModifierExtended.index === props.selectedModifierIndex, + )?.modifierLimitations; + + let currentModifierPositionLimitation: + | ModifierLimitationState + | null + | undefined = null; + if (currentModifierLimitations != null) { + currentModifierPositionLimitation = currentModifierLimitations?.find( + (limitation) => limitation.position === props.position, + ); + } // This happens when 'Clear Query' is pressed: // For a split second this element is rerendered, but there are no selected modifiers. - // which makes 'currentRelevantModifierSpec' null | undefined - if (currentRelevantModifierSpec == null) { - return; - } + // which makes 'currentModifierLimitations' null | undefined + // if (currentModifierLimitations == null) { + // return; + // } if (props.currentlyTakingInput) { return ( { key={`fancyInput-${props.selectedModifierIndex}-click-${props.orderIndex}`} > diff --git a/src/frontend/src/components/Input/ModifierInputComp/FancySelectedModifier.tsx b/src/frontend/src/components/Input/ModifierInputComp/FancySelectedModifier.tsx index d99e11392..950f57fa9 100644 --- a/src/frontend/src/components/Input/ModifierInputComp/FancySelectedModifier.tsx +++ b/src/frontend/src/components/Input/ModifierInputComp/FancySelectedModifier.tsx @@ -4,109 +4,104 @@ import { useState } from "react"; import { FancyModifierInput } from "./FancyModifierInput"; interface FancyModifierInputProps { - selectedModifier: ModifierOption; - index: number; - isDimmed?: boolean; + selectedModifier: ModifierOption; + index: number; + isDimmed?: boolean; } export type HandleChangeEventFunction = ( - isNumerical: boolean, - modifierId: number, - value: string | undefined, - index_to_handle: number, - numericalType?: string, - textRolls?: string + isNumerical: boolean, + modifierId: number, + position: number, + value: string | undefined, + index_to_handle: number, + numericalType?: string, + textRolls?: string, ) => void; export const FancySelectedModifier = (props: FancyModifierInputProps) => { - const selectedModifier = props.selectedModifier; + const selectedModifier = props.selectedModifier; - const splitSelectedModiferLabel = selectedModifier.label.split("#"); + const splitSelectedModiferLabel = selectedModifier.label.split("#"); - const [currentlyTakingInput, setCurrentlyTakingInput] = useState( - Array(splitSelectedModiferLabel.length).fill(false) - ); + const [currentlyTakingInput, setCurrentlyTakingInput] = useState( + Array(splitSelectedModiferLabel.length).fill(false), + ); - const changeTakingInput = (labelIndex: number) => { - if (props.isDimmed) { - return; - } - setCurrentlyTakingInput([ - ...currentlyTakingInput.slice(0, labelIndex), - !currentlyTakingInput[labelIndex], - ...currentlyTakingInput.slice(labelIndex + 1), - ]); - }; + const changeTakingInput = (labelIndex: number) => { + if (props.isDimmed) { + return; + } + setCurrentlyTakingInput([ + ...currentlyTakingInput.slice(0, labelIndex), + !currentlyTakingInput[labelIndex], + ...currentlyTakingInput.slice(labelIndex + 1), + ]); + }; - return ( - - - {splitSelectedModiferLabel.map( - (labelPart, labelIndex, splitSelectedModifer) => { - const labelPartSplit = labelPart.split(" "); - const isNotEnd = - splitSelectedModifer.length !== labelIndex + 1; - return labelPartSplit.map((word, wordIndex) => { - if ( - wordIndex < labelPartSplit.length - 1 || - !isNotEnd - ) { - return ( - - {word + " "} - - ); - } else if (isNotEnd) { - const textRolls = - selectedModifier.groupedModifierProperties - .textRolls[labelIndex] ?? undefined; - const modifierId = - selectedModifier.groupedModifierProperties - .modifierId[labelIndex]; - return ( - - - {word} - - - - ); - } - }); - } - )} - - - ); + return ( + + + {splitSelectedModiferLabel.map( + (labelPart, labelIndex, splitSelectedModifer) => { + const labelPartSplit = labelPart.split(" "); + const isNotEnd = splitSelectedModifer.length !== labelIndex + 1; + return labelPartSplit.map((word, wordIndex) => { + if (wordIndex < labelPartSplit.length - 1 || !isNotEnd) { + return ( + + {word + " "} + + ); + } else if (isNotEnd) { + const textRolls = + selectedModifier.groupedModifierProperties.textRolls[ + labelIndex + ] ?? undefined; + const modifierId = selectedModifier.modifierId; + const position = + selectedModifier.groupedModifierProperties.position[ + labelIndex + ]; + return ( + + {word} + + + ); + } + }); + }, + )} + + + ); }; diff --git a/src/frontend/src/components/Input/ModifierInputComp/ModifierInput.tsx b/src/frontend/src/components/Input/ModifierInputComp/ModifierInput.tsx index 5ce714906..d0f24dfae 100644 --- a/src/frontend/src/components/Input/ModifierInputComp/ModifierInput.tsx +++ b/src/frontend/src/components/Input/ModifierInputComp/ModifierInput.tsx @@ -20,6 +20,7 @@ export interface ModifierOption extends SelectBoxOptionValue { index?: number; static?: boolean; relatedUniques?: string; + modifierId: number; groupedModifierProperties: GroupedModifierProperties; } @@ -41,6 +42,7 @@ export const ModifierInput = () => { regex: prefetchedModifier.regex, static: prefetchedModifier.static ?? undefined, relatedUniques: prefetchedModifier.relatedUniques ?? undefined, + modifierId: prefetchedModifier.modifierId, groupedModifierProperties: prefetchedModifier.groupedModifierProperties, })); const [choosableModifierOptions, setChoosableModifierOptions] = useState< @@ -59,10 +61,8 @@ export const ModifierInput = () => { if (wantedModifierExtended.length > 0) { prevSelectedModifiers = wantedModifierExtended.reduce( (selectedModifiers, wantedModifier) => { - const prevSelectedModifier = choosableModifierOptions.find((modifier) => - modifier.groupedModifierProperties.modifierId.includes( - wantedModifier.modifierId, - ), + const prevSelectedModifier = choosableModifierOptions.find( + (modifier) => modifier.modifierId === wantedModifier.modifierId, ); if (prevSelectedModifier === undefined) { return selectedModifiers; @@ -137,28 +137,20 @@ export const ModifierInput = () => { ]); removeWantedModifierExtended(overrideIndex); - newlySelectedModifier.groupedModifierProperties.modifierId.map( - (modifierId) => { - addWantedModifierExtended( - { modifierId: modifierId }, - overrideIndex, - newlySelectedModifier.relatedUniques, - ); - }, + addWantedModifierExtended( + { modifierId: newlySelectedModifier.modifierId }, + overrideIndex, + newlySelectedModifier.relatedUniques, ); } else { setSelectedModifiers((currentSelectedModifiers) => [ ...currentSelectedModifiers, { ...newlySelectedModifier, index: selectedModifiers.length }, ]); - newlySelectedModifier.groupedModifierProperties.modifierId.map( - (modifierId) => { - addWantedModifierExtended( - { modifierId: modifierId }, - selectedModifiers.length, - newlySelectedModifier.relatedUniques, - ); - }, + addWantedModifierExtended( + { modifierId: newlySelectedModifier.modifierId }, + selectedModifiers.length, + newlySelectedModifier.relatedUniques, ); } }; @@ -224,9 +216,7 @@ export const ModifierInput = () => { isChecked={selectedModifier.isSelected} key={selectedIndex} onChange={() => { - if ( - selectedModifier.groupedModifierProperties.modifierId[0] !== null - ) { + if (selectedModifier.modifierId !== null) { handleCheckboxChange(selectedModifier, selectedIndex); } }} diff --git a/src/frontend/src/components/Input/StandardLayoutInput/ErrorMessage.tsx b/src/frontend/src/components/Input/StandardLayoutInput/ErrorMessage.tsx index f8317d5b9..8b26c3ff8 100644 --- a/src/frontend/src/components/Input/StandardLayoutInput/ErrorMessage.tsx +++ b/src/frontend/src/components/Input/StandardLayoutInput/ErrorMessage.tsx @@ -4,7 +4,7 @@ import { AlertIcon, AlertTitle, AlertProps, -} from "@chakra-ui/alert"; +} from "@chakra-ui/react/alert"; import { IconBaseProps } from "react-icons/lib"; interface ErrorMessageProps { diff --git a/src/frontend/src/hooks/graphing/utils.tsx b/src/frontend/src/hooks/graphing/utils.tsx index 503475f62..cab534c1b 100644 --- a/src/frontend/src/hooks/graphing/utils.tsx +++ b/src/frontend/src/hooks/graphing/utils.tsx @@ -1,10 +1,7 @@ -import { PlotQuery, WantedModifier } from "../../client"; +import { PlotQuery } from "../../client"; import { useErrorStore } from "../../store/ErrorStore"; import { useGraphInputStore } from "../../store/GraphInputStore"; -import { - BaseSpecState, - WantedModifierExtended, -} from "../../store/StateInterface"; +import { BaseSpecState } from "../../store/StateInterface"; import { LEAGUE_LAUNCH_TIME, PLOTTING_WINDOW_HOURS } from "../../config"; export const LEAGUE_LAUNCH_DATETIME = new Date(LEAGUE_LAUNCH_TIME); @@ -17,7 +14,7 @@ const calcMean = (values: number[]) => { const calcSTD = (values: number[], mean: number) => { return Math.sqrt( values.reduce((prev, cur) => prev + (cur - mean) * (cur - mean), 0) / - values.length + values.length, ); }; @@ -40,7 +37,7 @@ export function formatHoursSinceLaunch(hoursSinceLaunch: number): string { export const getHoursSinceLaunch = (currentTime: Date): number => { const getCurrentTimeDate = currentTime.getTime(); const hoursSinceLaunch = Math.floor( - (getCurrentTimeDate - LEAGUE_LAUNCH_DATETIME.getTime()) / (1000 * 3600) + (getCurrentTimeDate - LEAGUE_LAUNCH_DATETIME.getTime()) / (1000 * 3600), ); return hoursSinceLaunch; }; @@ -63,10 +60,10 @@ export const getOptimizedPlotQuery = (): PlotQuery | undefined => { return newUniqueCandidates; } return prev.filter((prevCandidate) => - newUniqueCandidates.includes(prevCandidate) + newUniqueCandidates.includes(prevCandidate), ); }, - [] as string[] + [] as string[], ); if (possibleUniques.length === 0 && state.itemSpec?.identified !== false) { useErrorStore.getState().setNoRelatedUniqueError(true); @@ -125,32 +122,12 @@ export const getOptimizedPlotQuery = (): PlotQuery | undefined => { } itemSpec = { ...itemSpec, name: possibleUniques.join("|") }; - const wantedModifier: WantedModifier[][] = state.wantedModifierExtended + const wantedModifier = state.wantedModifierExtended .filter((wantedModifier) => wantedModifier.isSelected) - .reduce((prev, cur, index) => { - // Very over complicated way to group modifier ids - const prevLength = prev.length; - if (prevLength === 0) { - return [[cur]]; - } - const wantedModifierIndex = cur.index; - const prevWantedModifierIndex = - state.wantedModifierExtended[index - 1].index; - - if (wantedModifierIndex === prevWantedModifierIndex) { - return [ - ...prev.slice(0, prevLength - 1), - [...prev[prevLength - 1], cur], - ]; - } - return [...prev, [cur]]; - }, [] as WantedModifierExtended[][]) - .map((groupedWantedModifierExtended) => - groupedWantedModifierExtended.map((wantedModifierExtended) => ({ - modifierId: wantedModifierExtended.modifierId, - modifierLimitations: wantedModifierExtended.modifierLimitations, - })) - ); + .map((wantedMoidifierExtended) => ({ + modifierId: wantedMoidifierExtended.modifierId, + modifierLimitations: wantedMoidifierExtended.modifierLimitations, + })); const currentTime = new Date(); const end = getHoursSinceLaunch(currentTime); const window = PLOTTING_WINDOW_HOURS; diff --git a/src/frontend/src/routeTree.gen.ts b/src/frontend/src/routeTree.gen.ts index 56825c097..640582595 100644 --- a/src/frontend/src/routeTree.gen.ts +++ b/src/frontend/src/routeTree.gen.ts @@ -8,128 +8,61 @@ // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. -// Import Routes - -import { Route as rootRoute } from './routes/__root' -import { Route as PrivacyPolicyImport } from './routes/privacy-policy' -import { Route as CaptchaImport } from './routes/captcha' -import { Route as AboutImport } from './routes/about' -import { Route as LayoutImport } from './routes/_layout' -import { Route as LayoutIndexImport } from './routes/_layout/index' - -// Create/Update Routes - -const PrivacyPolicyRoute = PrivacyPolicyImport.update({ +import { Route as rootRouteImport } from './routes/__root' +import { Route as PrivacyPolicyRouteImport } from './routes/privacy-policy' +import { Route as CaptchaRouteImport } from './routes/captcha' +import { Route as AboutRouteImport } from './routes/about' +import { Route as LayoutRouteImport } from './routes/_layout' +import { Route as LayoutIndexRouteImport } from './routes/_layout/index' + +const PrivacyPolicyRoute = PrivacyPolicyRouteImport.update({ id: '/privacy-policy', path: '/privacy-policy', - getParentRoute: () => rootRoute, + getParentRoute: () => rootRouteImport, } as any) - -const CaptchaRoute = CaptchaImport.update({ +const CaptchaRoute = CaptchaRouteImport.update({ id: '/captcha', path: '/captcha', - getParentRoute: () => rootRoute, + getParentRoute: () => rootRouteImport, } as any) - -const AboutRoute = AboutImport.update({ +const AboutRoute = AboutRouteImport.update({ id: '/about', path: '/about', - getParentRoute: () => rootRoute, + getParentRoute: () => rootRouteImport, } as any) - -const LayoutRoute = LayoutImport.update({ +const LayoutRoute = LayoutRouteImport.update({ id: '/_layout', - getParentRoute: () => rootRoute, + getParentRoute: () => rootRouteImport, } as any) - -const LayoutIndexRoute = LayoutIndexImport.update({ +const LayoutIndexRoute = LayoutIndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => LayoutRoute, } as any) -// Populate the FileRoutesByPath interface - -declare module '@tanstack/react-router' { - interface FileRoutesByPath { - '/_layout': { - id: '/_layout' - path: '' - fullPath: '' - preLoaderRoute: typeof LayoutImport - parentRoute: typeof rootRoute - } - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutImport - parentRoute: typeof rootRoute - } - '/captcha': { - id: '/captcha' - path: '/captcha' - fullPath: '/captcha' - preLoaderRoute: typeof CaptchaImport - parentRoute: typeof rootRoute - } - '/privacy-policy': { - id: '/privacy-policy' - path: '/privacy-policy' - fullPath: '/privacy-policy' - preLoaderRoute: typeof PrivacyPolicyImport - parentRoute: typeof rootRoute - } - '/_layout/': { - id: '/_layout/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof LayoutIndexImport - parentRoute: typeof LayoutImport - } - } -} - -// Create and export the route tree - -interface LayoutRouteChildren { - LayoutIndexRoute: typeof LayoutIndexRoute -} - -const LayoutRouteChildren: LayoutRouteChildren = { - LayoutIndexRoute: LayoutIndexRoute, -} - -const LayoutRouteWithChildren = - LayoutRoute._addFileChildren(LayoutRouteChildren) - export interface FileRoutesByFullPath { - '': typeof LayoutRouteWithChildren + '/': typeof LayoutIndexRoute '/about': typeof AboutRoute '/captcha': typeof CaptchaRoute '/privacy-policy': typeof PrivacyPolicyRoute - '/': typeof LayoutIndexRoute } - export interface FileRoutesByTo { '/about': typeof AboutRoute '/captcha': typeof CaptchaRoute '/privacy-policy': typeof PrivacyPolicyRoute '/': typeof LayoutIndexRoute } - export interface FileRoutesById { - __root__: typeof rootRoute + __root__: typeof rootRouteImport '/_layout': typeof LayoutRouteWithChildren '/about': typeof AboutRoute '/captcha': typeof CaptchaRoute '/privacy-policy': typeof PrivacyPolicyRoute '/_layout/': typeof LayoutIndexRoute } - export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '' | '/about' | '/captcha' | '/privacy-policy' | '/' + fullPaths: '/' | '/about' | '/captcha' | '/privacy-policy' fileRoutesByTo: FileRoutesByTo to: '/about' | '/captcha' | '/privacy-policy' | '/' id: @@ -141,7 +74,6 @@ export interface FileRouteTypes { | '/_layout/' fileRoutesById: FileRoutesById } - export interface RootRouteChildren { LayoutRoute: typeof LayoutRouteWithChildren AboutRoute: typeof AboutRoute @@ -149,48 +81,63 @@ export interface RootRouteChildren { PrivacyPolicyRoute: typeof PrivacyPolicyRoute } +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/privacy-policy': { + id: '/privacy-policy' + path: '/privacy-policy' + fullPath: '/privacy-policy' + preLoaderRoute: typeof PrivacyPolicyRouteImport + parentRoute: typeof rootRouteImport + } + '/captcha': { + id: '/captcha' + path: '/captcha' + fullPath: '/captcha' + preLoaderRoute: typeof CaptchaRouteImport + parentRoute: typeof rootRouteImport + } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutRouteImport + parentRoute: typeof rootRouteImport + } + '/_layout': { + id: '/_layout' + path: '' + fullPath: '/' + preLoaderRoute: typeof LayoutRouteImport + parentRoute: typeof rootRouteImport + } + '/_layout/': { + id: '/_layout/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof LayoutIndexRouteImport + parentRoute: typeof LayoutRoute + } + } +} + +interface LayoutRouteChildren { + LayoutIndexRoute: typeof LayoutIndexRoute +} + +const LayoutRouteChildren: LayoutRouteChildren = { + LayoutIndexRoute: LayoutIndexRoute, +} + +const LayoutRouteWithChildren = + LayoutRoute._addFileChildren(LayoutRouteChildren) + const rootRouteChildren: RootRouteChildren = { LayoutRoute: LayoutRouteWithChildren, AboutRoute: AboutRoute, CaptchaRoute: CaptchaRoute, PrivacyPolicyRoute: PrivacyPolicyRoute, } - -export const routeTree = rootRoute +export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) ._addFileTypes() - -/* ROUTE_MANIFEST_START -{ - "routes": { - "__root__": { - "filePath": "__root.tsx", - "children": [ - "/_layout", - "/about", - "/captcha", - "/privacy-policy" - ] - }, - "/_layout": { - "filePath": "_layout.tsx", - "children": [ - "/_layout/" - ] - }, - "/about": { - "filePath": "about.tsx" - }, - "/captcha": { - "filePath": "captcha.tsx" - }, - "/privacy-policy": { - "filePath": "privacy-policy.tsx" - }, - "/_layout/": { - "filePath": "_layout/index.tsx", - "parent": "/_layout" - } - } -} -ROUTE_MANIFEST_END */ diff --git a/src/frontend/src/store/GraphInputStore.tsx b/src/frontend/src/store/GraphInputStore.tsx index 649a65849..6cc1ae85e 100644 --- a/src/frontend/src/store/GraphInputStore.tsx +++ b/src/frontend/src/store/GraphInputStore.tsx @@ -176,19 +176,20 @@ export const useGraphInputStore = create((set) => ({ baseSpec: undefined, })), - addLeague: (league: string) => set((state) => ({ leagues: [...state.leagues, league] })), + addLeague: (league: string) => + set((state) => ({ leagues: [...state.leagues, league] })), removeLeague: (league: string) => set((state) => ({ leagues: [ ...state.leagues.reduce( - (prev, cur) => - cur === league ? [...prev] : [...prev, cur], - [] as string[]) - ] + (prev, cur) => (cur === league ? [...prev] : [...prev, cur]), + [] as string[], + ), + ], })), removeAllLeagues: () => set(() => ({ - leagues: [] + leagues: [], })), setItemSpec: (itemSpec: ItemSpecState) => set(() => ({ itemSpec: itemSpec })), @@ -415,6 +416,7 @@ export const useGraphInputStore = create((set) => ({ setWantedModifierMinRoll: ( modifierId: number, + position: number, minRoll: number | undefined, index: number, ) => @@ -425,20 +427,41 @@ export const useGraphInputStore = create((set) => ({ wantedModifierExtended.modifierId === modifierId && wantedModifierExtended.index === index ) { + let updatedModifierLimitations = + wantedModifierExtended.modifierLimitations; + if (updatedModifierLimitations == null) { + updatedModifierLimitations = []; + } if (minRoll === undefined) { - if (!wantedModifierExtended.modifierLimitations?.maxRoll) { - delete wantedModifierExtended["modifierLimitations"]; + for (let i = 0; i < updatedModifierLimitations.length; i++) { + const limitation = updatedModifierLimitations[i]; + if (limitation.position === position) { + if (limitation.minRoll == null) { + delete updatedModifierLimitations[i]; + } else { + delete limitation["minRoll"]; + } + break; + } + } + } else { + let updatedModifierLimitation = updatedModifierLimitations.find( + (modifierLimitation) => + modifierLimitation.position === position, + ); + if (updatedModifierLimitation == null) { + updatedModifierLimitation = { + position: position, + minRoll: minRoll, + }; + updatedModifierLimitations.push(updatedModifierLimitation); } else { - delete wantedModifierExtended.modifierLimitations["minRoll"]; + updatedModifierLimitation.minRoll = minRoll; } - return wantedModifierExtended; } return { ...wantedModifierExtended, - modifierLimitations: { - ...wantedModifierExtended.modifierLimitations, - minRoll: minRoll, - }, + modifierLimitations: updatedModifierLimitations, }; } else { return wantedModifierExtended; @@ -450,6 +473,7 @@ export const useGraphInputStore = create((set) => ({ setWantedModifierMaxRoll: ( modifierId: number, + position: number, maxRoll: number | undefined, index: number, ) => @@ -460,20 +484,41 @@ export const useGraphInputStore = create((set) => ({ wantedModifierExtended.modifierId === modifierId && wantedModifierExtended.index === index ) { + let updatedModifierLimitations = + wantedModifierExtended.modifierLimitations; + if (updatedModifierLimitations == null) { + updatedModifierLimitations = []; + } if (maxRoll === undefined) { - if (!wantedModifierExtended.modifierLimitations?.minRoll) { - delete wantedModifierExtended["modifierLimitations"]; + for (let i = 0; i < updatedModifierLimitations.length; i++) { + const limitation = updatedModifierLimitations[i]; + if (limitation.position === position) { + if (limitation.minRoll == null) { + delete updatedModifierLimitations[i]; + } else { + delete limitation["maxRoll"]; + } + break; + } + } + } else { + let updatedModifierLimitation = updatedModifierLimitations.find( + (modifierLimitation) => + modifierLimitation.position === position, + ); + if (updatedModifierLimitation == null) { + updatedModifierLimitation = { + position: position, + maxRoll: maxRoll, + }; + updatedModifierLimitations.push(updatedModifierLimitation); } else { - delete wantedModifierExtended.modifierLimitations["maxRoll"]; + updatedModifierLimitation.maxRoll = maxRoll; } - return wantedModifierExtended; } return { ...wantedModifierExtended, - modifierLimitations: { - ...wantedModifierExtended.modifierLimitations, - maxRoll: maxRoll, - }, + modifierLimitations: updatedModifierLimitations, }; } else { return wantedModifierExtended; @@ -485,25 +530,50 @@ export const useGraphInputStore = create((set) => ({ setWantedModifierTextRoll: ( modifierId: number, + position: number, textRoll: number | undefined, index: number, ) => set((state) => { const updatedModifiersExtended = state.wantedModifierExtended.map( - (wantedModifierExtended) => - wantedModifierExtended.modifierId === modifierId && + (wantedModifierExtended) => { + if ( + wantedModifierExtended.modifierId === modifierId && wantedModifierExtended.index === index - ? { - ...wantedModifierExtended, - modifierLimitations: - textRoll !== undefined - ? { - ...wantedModifierExtended.modifierLimitations, - textRoll: textRoll, - } - : undefined, + ) { + let updatedModifierLimitations = + wantedModifierExtended.modifierLimitations; + if (updatedModifierLimitations == null) { + updatedModifierLimitations = []; + } + if (textRoll === undefined) { + updatedModifierLimitations = updatedModifierLimitations.filter( + (modifierLimitation) => + modifierLimitation.position !== position, + ); + } else { + let updatedModifierLimitation = updatedModifierLimitations.find( + (modifierLimitation) => + modifierLimitation.position === position, + ); + if (updatedModifierLimitation == null) { + updatedModifierLimitation = { + position: position, + textRoll: textRoll, + }; + updatedModifierLimitations.push(updatedModifierLimitation); + } else { + updatedModifierLimitation.textRoll = textRoll; + } } - : wantedModifierExtended, + return { + ...wantedModifierExtended, + modifierLimitations: updatedModifierLimitations, + }; + } else { + return wantedModifierExtended; + } + }, ); return { wantedModifierExtended: updatedModifiersExtended }; }), diff --git a/src/frontend/src/store/StateInterface.tsx b/src/frontend/src/store/StateInterface.tsx index 45d4195ea..2d6c2543d 100644 --- a/src/frontend/src/store/StateInterface.tsx +++ b/src/frontend/src/store/StateInterface.tsx @@ -45,6 +45,7 @@ export interface BaseSpecState { } export interface ModifierLimitationState { + position: number; minRoll?: number | null; maxRoll?: number | null; textRoll?: number | null; @@ -52,7 +53,7 @@ export interface ModifierLimitationState { export interface WantedModifier { modifierId: number; - modifierLimitations?: ModifierLimitationState | null; + modifierLimitations?: ModifierLimitationState[] | null; } // Used to keep track of the different wanted modifiers for frontend export interface WantedModifierExtended extends WantedModifier { @@ -119,7 +120,6 @@ export interface GraphInputState { removeLeague: (league: string) => void; removeAllLeagues: () => void; - setItemName: (name: string | undefined) => void; setItemSpec: (itemSpec: ItemSpecState) => void; @@ -165,16 +165,19 @@ export interface GraphInputState { ) => void; setWantedModifierMinRoll: ( modifierId: number, + position: number, minRoll: number | undefined, index: number, ) => void; setWantedModifierMaxRoll: ( modifierId: number, + position: number, maxRoll: number | undefined, index: number, ) => void; setWantedModifierTextRoll: ( modifierId: number, + position: number, textRoll: number | undefined, index: number, ) => void; @@ -195,14 +198,14 @@ export interface ExpandedComponentState { export interface ErrorState { leagueError: boolean; noSelectedModifiersError: boolean; - modifiersUnidentifiedError: boolean, - currentlySelectedModifiersError: boolean, + modifiersUnidentifiedError: boolean; + currentlySelectedModifiersError: boolean; noRelatedUniqueError: boolean; baseSpecDoesNotMatchError: boolean; setLeagueError: (leagueError: boolean) => void; setNoSelectedModifiersError: (modifiersError: boolean) => void; setModifiersUnidentifiedError: (modifiersError: boolean) => void; - setCurrentlySelectedModifiersError: (modifiersError: boolean) => void, + setCurrentlySelectedModifiersError: (modifiersError: boolean) => void; setNoRelatedUniqueError: (noRelatedUniqueError: boolean) => void; setBaseSpecDoesNotMatchError: (baseSpecDoesNotMatchError: boolean) => void; } From 8eed4426a04515e7e366465659b422f01b47af89 Mon Sep 17 00:00:00 2001 From: Bogadisa Date: Sun, 24 May 2026 14:13:48 -0230 Subject: [PATCH 5/8] #812 Response to PR comments --- src/backend_api/app/crud/extensions/crud_modifier.py | 7 +++++++ .../data_retrieval_app/external_data_retrieval/config.py | 2 +- .../utils/data_deposit_test_data_creator.py | 4 ++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/backend_api/app/crud/extensions/crud_modifier.py b/src/backend_api/app/crud/extensions/crud_modifier.py index 6b67b86fc..1c8f0652f 100644 --- a/src/backend_api/app/crud/extensions/crud_modifier.py +++ b/src/backend_api/app/crud/extensions/crud_modifier.py @@ -1,3 +1,4 @@ +from fastapi import HTTPException from pydantic import TypeAdapter from sqlalchemy import func, select from sqlalchemy.orm import Session @@ -40,6 +41,12 @@ async def get_grouped_modifier_by_effect(self, db: Session): grouped_modifier_by_effect_record = db.execute(stmt).mappings().all() + if not grouped_modifier_by_effect_record: + raise HTTPException( + status_code=404, + detail=f"No objects found in the table {self.model.__tablename__}.", + ) + validate = TypeAdapter( GroupedModifierByEffect | list[GroupedModifierByEffect] ).validate_python diff --git a/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/config.py b/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/config.py index 1375ea524..edeed8ab6 100644 --- a/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/config.py +++ b/src/backend_data_retrieval/data_retrieval_app/external_data_retrieval/config.py @@ -39,7 +39,7 @@ def CURRENT_HARDCORE_LEAGUE(self) -> str: OAUTH_CLIENT_SECRET: str MINI_BATCH_SIZE: int = 30 - N_CHECKPOINTS_PER_TRANSFORMATION: int = 1 + N_CHECKPOINTS_PER_TRANSFORMATION: int = 10 TIME_BETWEEN_RESTART: int = 3600 MAX_TIME_PER_MINI_BATCH: int = 3 * 60 diff --git a/src/backend_data_retrieval/data_retrieval_app/tests/scripts/create_public_stashes_test_data/utils/data_deposit_test_data_creator.py b/src/backend_data_retrieval/data_retrieval_app/tests/scripts/create_public_stashes_test_data/utils/data_deposit_test_data_creator.py index 90b32099d..de8694d4b 100644 --- a/src/backend_data_retrieval/data_retrieval_app/tests/scripts/create_public_stashes_test_data/utils/data_deposit_test_data_creator.py +++ b/src/backend_data_retrieval/data_retrieval_app/tests/scripts/create_public_stashes_test_data/utils/data_deposit_test_data_creator.py @@ -174,8 +174,6 @@ def get_roll_ranges( .to_list() ) - # return pd.Series({"modifier_id": modifier_id, "rolls": rolls}, index=) - # return {modifier_id: rolls} return pd.Series({"modifier_id": modifier_id, "rolls": rolls}) grouped_modifier_df = self.grouped_modifier_df @@ -218,6 +216,8 @@ def get_roll_ranges( if modifier_id not in self.modifier_ids_to_effect_map: self.modifier_ids_to_effect_map[modifier_id] = effect + prev_key = upper_bound + return connected_modifier_ids_dict, connected_rolls_dict def create_templates(self) -> None: From 5948779ac0343ce3ec4602d16c160ae3c5a745a1 Mon Sep 17 00:00:00 2001 From: Bogadisa Date: Sun, 24 May 2026 15:34:36 -0230 Subject: [PATCH 6/8] #812 Reverted error message change --- .../src/components/Input/StandardLayoutInput/ErrorMessage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/components/Input/StandardLayoutInput/ErrorMessage.tsx b/src/frontend/src/components/Input/StandardLayoutInput/ErrorMessage.tsx index 8b26c3ff8..f8317d5b9 100644 --- a/src/frontend/src/components/Input/StandardLayoutInput/ErrorMessage.tsx +++ b/src/frontend/src/components/Input/StandardLayoutInput/ErrorMessage.tsx @@ -4,7 +4,7 @@ import { AlertIcon, AlertTitle, AlertProps, -} from "@chakra-ui/react/alert"; +} from "@chakra-ui/alert"; import { IconBaseProps } from "react-icons/lib"; interface ErrorMessageProps { From 150bafdcd9757a0ee7b5d93ade2dada5a0065592 Mon Sep 17 00:00:00 2001 From: Bogadisa Date: Sun, 24 May 2026 18:14:20 -0230 Subject: [PATCH 7/8] #812 Abstracted min and max roll --- src/frontend/src/hooks/graphing/utils.tsx | 76 +++++++- src/frontend/src/routeTree.gen.ts | 195 +++++++++++++-------- src/frontend/src/store/GraphInputStore.tsx | 111 ++---------- 3 files changed, 210 insertions(+), 172 deletions(-) diff --git a/src/frontend/src/hooks/graphing/utils.tsx b/src/frontend/src/hooks/graphing/utils.tsx index cab534c1b..b8a40326d 100644 --- a/src/frontend/src/hooks/graphing/utils.tsx +++ b/src/frontend/src/hooks/graphing/utils.tsx @@ -1,7 +1,7 @@ import { PlotQuery } from "../../client"; import { useErrorStore } from "../../store/ErrorStore"; import { useGraphInputStore } from "../../store/GraphInputStore"; -import { BaseSpecState } from "../../store/StateInterface"; +import { BaseSpecState, GraphInputState } from "../../store/StateInterface"; import { LEAGUE_LAUNCH_TIME, PLOTTING_WINDOW_HOURS } from "../../config"; export const LEAGUE_LAUNCH_DATETIME = new Date(LEAGUE_LAUNCH_TIME); @@ -42,6 +42,80 @@ export const getHoursSinceLaunch = (currentTime: Date): number => { return hoursSinceLaunch; }; +export const updateNumericalRoll = ( + state: GraphInputState, + modifierId: number, + position: number, + roll: number | undefined, + rollType: "min" | "max", + index: number, +) => { + const updatedModifiersExtended = state.wantedModifierExtended.map( + (wantedModifierExtended) => { + if ( + wantedModifierExtended.modifierId === modifierId && + wantedModifierExtended.index === index + ) { + let updatedModifierLimitations = + wantedModifierExtended.modifierLimitations; + // roll === undefined => remove rollType + if (updatedModifierLimitations == null) { + if (roll === undefined) { + return wantedModifierExtended; + } + updatedModifierLimitations = []; + } + + if (roll === undefined) { + for (let i = 0; i < updatedModifierLimitations.length; i++) { + const limitation = updatedModifierLimitations[i]; + if (limitation.position === position) { + if (limitation.minRoll == null) { + delete updatedModifierLimitations[i]; + } else { + if (rollType === "min") { + delete limitation["minRoll"]; + } else { + delete limitation["maxRoll"]; + } + } + break; + } + } + } else { + let updatedModifierLimitation = updatedModifierLimitations.find( + (modifierLimitation) => modifierLimitation.position === position, + ); + if (updatedModifierLimitation == null) { + updatedModifierLimitation = { + position: position, + }; + if (rollType === "min") { + updatedModifierLimitation["minRoll"] = roll; + } else { + updatedModifierLimitation["maxRoll"] = roll; + } + updatedModifierLimitations.push(updatedModifierLimitation); + } else { + if (rollType === "min") { + updatedModifierLimitation["minRoll"] = roll; + } else { + updatedModifierLimitation["maxRoll"] = roll; + } + } + } + return { + ...wantedModifierExtended, + modifierLimitations: updatedModifierLimitations, + }; + } else { + return wantedModifierExtended; + } + }, + ); + return { wantedModifierExtended: updatedModifiersExtended }; +}; + export const getOptimizedPlotQuery = (): PlotQuery | undefined => { // currently always runs, needs to be in if check // when Non-unique rarity is possible diff --git a/src/frontend/src/routeTree.gen.ts b/src/frontend/src/routeTree.gen.ts index 640582595..56825c097 100644 --- a/src/frontend/src/routeTree.gen.ts +++ b/src/frontend/src/routeTree.gen.ts @@ -8,61 +8,128 @@ // You should NOT make any changes in this file as it will be overwritten. // Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified. -import { Route as rootRouteImport } from './routes/__root' -import { Route as PrivacyPolicyRouteImport } from './routes/privacy-policy' -import { Route as CaptchaRouteImport } from './routes/captcha' -import { Route as AboutRouteImport } from './routes/about' -import { Route as LayoutRouteImport } from './routes/_layout' -import { Route as LayoutIndexRouteImport } from './routes/_layout/index' - -const PrivacyPolicyRoute = PrivacyPolicyRouteImport.update({ +// Import Routes + +import { Route as rootRoute } from './routes/__root' +import { Route as PrivacyPolicyImport } from './routes/privacy-policy' +import { Route as CaptchaImport } from './routes/captcha' +import { Route as AboutImport } from './routes/about' +import { Route as LayoutImport } from './routes/_layout' +import { Route as LayoutIndexImport } from './routes/_layout/index' + +// Create/Update Routes + +const PrivacyPolicyRoute = PrivacyPolicyImport.update({ id: '/privacy-policy', path: '/privacy-policy', - getParentRoute: () => rootRouteImport, + getParentRoute: () => rootRoute, } as any) -const CaptchaRoute = CaptchaRouteImport.update({ + +const CaptchaRoute = CaptchaImport.update({ id: '/captcha', path: '/captcha', - getParentRoute: () => rootRouteImport, + getParentRoute: () => rootRoute, } as any) -const AboutRoute = AboutRouteImport.update({ + +const AboutRoute = AboutImport.update({ id: '/about', path: '/about', - getParentRoute: () => rootRouteImport, + getParentRoute: () => rootRoute, } as any) -const LayoutRoute = LayoutRouteImport.update({ + +const LayoutRoute = LayoutImport.update({ id: '/_layout', - getParentRoute: () => rootRouteImport, + getParentRoute: () => rootRoute, } as any) -const LayoutIndexRoute = LayoutIndexRouteImport.update({ + +const LayoutIndexRoute = LayoutIndexImport.update({ id: '/', path: '/', getParentRoute: () => LayoutRoute, } as any) +// Populate the FileRoutesByPath interface + +declare module '@tanstack/react-router' { + interface FileRoutesByPath { + '/_layout': { + id: '/_layout' + path: '' + fullPath: '' + preLoaderRoute: typeof LayoutImport + parentRoute: typeof rootRoute + } + '/about': { + id: '/about' + path: '/about' + fullPath: '/about' + preLoaderRoute: typeof AboutImport + parentRoute: typeof rootRoute + } + '/captcha': { + id: '/captcha' + path: '/captcha' + fullPath: '/captcha' + preLoaderRoute: typeof CaptchaImport + parentRoute: typeof rootRoute + } + '/privacy-policy': { + id: '/privacy-policy' + path: '/privacy-policy' + fullPath: '/privacy-policy' + preLoaderRoute: typeof PrivacyPolicyImport + parentRoute: typeof rootRoute + } + '/_layout/': { + id: '/_layout/' + path: '/' + fullPath: '/' + preLoaderRoute: typeof LayoutIndexImport + parentRoute: typeof LayoutImport + } + } +} + +// Create and export the route tree + +interface LayoutRouteChildren { + LayoutIndexRoute: typeof LayoutIndexRoute +} + +const LayoutRouteChildren: LayoutRouteChildren = { + LayoutIndexRoute: LayoutIndexRoute, +} + +const LayoutRouteWithChildren = + LayoutRoute._addFileChildren(LayoutRouteChildren) + export interface FileRoutesByFullPath { - '/': typeof LayoutIndexRoute + '': typeof LayoutRouteWithChildren '/about': typeof AboutRoute '/captcha': typeof CaptchaRoute '/privacy-policy': typeof PrivacyPolicyRoute + '/': typeof LayoutIndexRoute } + export interface FileRoutesByTo { '/about': typeof AboutRoute '/captcha': typeof CaptchaRoute '/privacy-policy': typeof PrivacyPolicyRoute '/': typeof LayoutIndexRoute } + export interface FileRoutesById { - __root__: typeof rootRouteImport + __root__: typeof rootRoute '/_layout': typeof LayoutRouteWithChildren '/about': typeof AboutRoute '/captcha': typeof CaptchaRoute '/privacy-policy': typeof PrivacyPolicyRoute '/_layout/': typeof LayoutIndexRoute } + export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/about' | '/captcha' | '/privacy-policy' + fullPaths: '' | '/about' | '/captcha' | '/privacy-policy' | '/' fileRoutesByTo: FileRoutesByTo to: '/about' | '/captcha' | '/privacy-policy' | '/' id: @@ -74,6 +141,7 @@ export interface FileRouteTypes { | '/_layout/' fileRoutesById: FileRoutesById } + export interface RootRouteChildren { LayoutRoute: typeof LayoutRouteWithChildren AboutRoute: typeof AboutRoute @@ -81,63 +149,48 @@ export interface RootRouteChildren { PrivacyPolicyRoute: typeof PrivacyPolicyRoute } -declare module '@tanstack/react-router' { - interface FileRoutesByPath { - '/privacy-policy': { - id: '/privacy-policy' - path: '/privacy-policy' - fullPath: '/privacy-policy' - preLoaderRoute: typeof PrivacyPolicyRouteImport - parentRoute: typeof rootRouteImport - } - '/captcha': { - id: '/captcha' - path: '/captcha' - fullPath: '/captcha' - preLoaderRoute: typeof CaptchaRouteImport - parentRoute: typeof rootRouteImport - } - '/about': { - id: '/about' - path: '/about' - fullPath: '/about' - preLoaderRoute: typeof AboutRouteImport - parentRoute: typeof rootRouteImport - } - '/_layout': { - id: '/_layout' - path: '' - fullPath: '/' - preLoaderRoute: typeof LayoutRouteImport - parentRoute: typeof rootRouteImport - } - '/_layout/': { - id: '/_layout/' - path: '/' - fullPath: '/' - preLoaderRoute: typeof LayoutIndexRouteImport - parentRoute: typeof LayoutRoute - } - } -} - -interface LayoutRouteChildren { - LayoutIndexRoute: typeof LayoutIndexRoute -} - -const LayoutRouteChildren: LayoutRouteChildren = { - LayoutIndexRoute: LayoutIndexRoute, -} - -const LayoutRouteWithChildren = - LayoutRoute._addFileChildren(LayoutRouteChildren) - const rootRouteChildren: RootRouteChildren = { LayoutRoute: LayoutRouteWithChildren, AboutRoute: AboutRoute, CaptchaRoute: CaptchaRoute, PrivacyPolicyRoute: PrivacyPolicyRoute, } -export const routeTree = rootRouteImport + +export const routeTree = rootRoute ._addFileChildren(rootRouteChildren) ._addFileTypes() + +/* ROUTE_MANIFEST_START +{ + "routes": { + "__root__": { + "filePath": "__root.tsx", + "children": [ + "/_layout", + "/about", + "/captcha", + "/privacy-policy" + ] + }, + "/_layout": { + "filePath": "_layout.tsx", + "children": [ + "/_layout/" + ] + }, + "/about": { + "filePath": "about.tsx" + }, + "/captcha": { + "filePath": "captcha.tsx" + }, + "/privacy-policy": { + "filePath": "privacy-policy.tsx" + }, + "/_layout/": { + "filePath": "_layout/index.tsx", + "parent": "/_layout" + } + } +} +ROUTE_MANIFEST_END */ diff --git a/src/frontend/src/store/GraphInputStore.tsx b/src/frontend/src/store/GraphInputStore.tsx index 6cc1ae85e..fad2acf9c 100644 --- a/src/frontend/src/store/GraphInputStore.tsx +++ b/src/frontend/src/store/GraphInputStore.tsx @@ -12,6 +12,7 @@ import { import { GroupedModifierByEffect, ItemBaseType, PlotQuery } from "../client"; import { encodeHash, decodeHash } from "./utils"; import { DEFAULT_LEAGUES } from "../config"; +import { updateNumericalRoll } from "../hooks/graphing/utils"; // Graph Input Store - This store is used to store graph input data. export const useGraphInputStore = create((set) => ({ @@ -420,56 +421,9 @@ export const useGraphInputStore = create((set) => ({ minRoll: number | undefined, index: number, ) => - set((state) => { - const updatedModifiersExtended = state.wantedModifierExtended.map( - (wantedModifierExtended) => { - if ( - wantedModifierExtended.modifierId === modifierId && - wantedModifierExtended.index === index - ) { - let updatedModifierLimitations = - wantedModifierExtended.modifierLimitations; - if (updatedModifierLimitations == null) { - updatedModifierLimitations = []; - } - if (minRoll === undefined) { - for (let i = 0; i < updatedModifierLimitations.length; i++) { - const limitation = updatedModifierLimitations[i]; - if (limitation.position === position) { - if (limitation.minRoll == null) { - delete updatedModifierLimitations[i]; - } else { - delete limitation["minRoll"]; - } - break; - } - } - } else { - let updatedModifierLimitation = updatedModifierLimitations.find( - (modifierLimitation) => - modifierLimitation.position === position, - ); - if (updatedModifierLimitation == null) { - updatedModifierLimitation = { - position: position, - minRoll: minRoll, - }; - updatedModifierLimitations.push(updatedModifierLimitation); - } else { - updatedModifierLimitation.minRoll = minRoll; - } - } - return { - ...wantedModifierExtended, - modifierLimitations: updatedModifierLimitations, - }; - } else { - return wantedModifierExtended; - } - }, - ); - return { wantedModifierExtended: updatedModifiersExtended }; - }), + set((state) => + updateNumericalRoll(state, modifierId, position, minRoll, "min", index), + ), setWantedModifierMaxRoll: ( modifierId: number, @@ -477,56 +431,9 @@ export const useGraphInputStore = create((set) => ({ maxRoll: number | undefined, index: number, ) => - set((state) => { - const updatedModifiersExtended = state.wantedModifierExtended.map( - (wantedModifierExtended) => { - if ( - wantedModifierExtended.modifierId === modifierId && - wantedModifierExtended.index === index - ) { - let updatedModifierLimitations = - wantedModifierExtended.modifierLimitations; - if (updatedModifierLimitations == null) { - updatedModifierLimitations = []; - } - if (maxRoll === undefined) { - for (let i = 0; i < updatedModifierLimitations.length; i++) { - const limitation = updatedModifierLimitations[i]; - if (limitation.position === position) { - if (limitation.minRoll == null) { - delete updatedModifierLimitations[i]; - } else { - delete limitation["maxRoll"]; - } - break; - } - } - } else { - let updatedModifierLimitation = updatedModifierLimitations.find( - (modifierLimitation) => - modifierLimitation.position === position, - ); - if (updatedModifierLimitation == null) { - updatedModifierLimitation = { - position: position, - maxRoll: maxRoll, - }; - updatedModifierLimitations.push(updatedModifierLimitation); - } else { - updatedModifierLimitation.maxRoll = maxRoll; - } - } - return { - ...wantedModifierExtended, - modifierLimitations: updatedModifierLimitations, - }; - } else { - return wantedModifierExtended; - } - }, - ); - return { wantedModifierExtended: updatedModifiersExtended }; - }), + set((state) => + updateNumericalRoll(state, modifierId, position, maxRoll, "max", index), + ), setWantedModifierTextRoll: ( modifierId: number, @@ -543,7 +450,11 @@ export const useGraphInputStore = create((set) => ({ ) { let updatedModifierLimitations = wantedModifierExtended.modifierLimitations; + // textRoll === undefined => remove textRoll if (updatedModifierLimitations == null) { + if (textRoll === undefined) { + return wantedModifierExtended; + } updatedModifierLimitations = []; } if (textRoll === undefined) { From 61a491e2f7f2f6273a7fab2212ecfa8f05cf5e8f Mon Sep 17 00:00:00 2001 From: Bogadisa Date: Sun, 24 May 2026 18:14:57 -0230 Subject: [PATCH 8/8] #812 Fixed set to copy warning pandas --- .../item_base_type/item_base_type_data_depositor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend_data_retrieval/data_retrieval_app/data_deposit/item_base_type/item_base_type_data_depositor.py b/src/backend_data_retrieval/data_retrieval_app/data_deposit/item_base_type/item_base_type_data_depositor.py index 82c5ceedd..bad7d88bb 100644 --- a/src/backend_data_retrieval/data_retrieval_app/data_deposit/item_base_type/item_base_type_data_depositor.py +++ b/src/backend_data_retrieval/data_retrieval_app/data_deposit/item_base_type/item_base_type_data_depositor.py @@ -49,7 +49,7 @@ def _update_duplicates(self, duplicate_df: pd.DataFrame): changed_rows = duplicate_df[ (duplicate_df["relatedUniques"] != duplicate_df["relatedUniques_y"]) & (~duplicate_df["relatedUniques"].isna()) - ] + ].copy() if not changed_rows.empty: logger.info(