diff --git a/src/.env b/src/.env index 3018f8397..3038cfdca 100644 --- a/src/.env +++ b/src/.env @@ -37,7 +37,6 @@ TURNSTILE_SECRET_KEY=1x0000000000000000000000000000000AA # League CURRENT_SOFTCORE_LEAGUE="Mirage" -ALL_SOFTCORE_LEAGUES="Mirage|Keepers|Mercenaries|Phrecia" LEAGUE_LAUNCH_TIME=2026-03-06T19:00:00Z # ISO 8601 format. Round backwards to whole hour number diff --git a/src/backend_api/app/alembic/replaceable_objects/main.py b/src/backend_api/app/alembic/replaceable_objects/main.py index d8d941ad6..856cd3688 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: str, table: str, function: str, trigger: str): + def __init__(self, name, table, function, trigger): self.name = name self.table = table - self.function = function.format(name=name) - self.trigger = trigger.format(name=name, table=table) + self.function = function + self.trigger = trigger ObjectType = TypeVar("ObjectType", bound=ReplaceableObject) @@ -89,24 +89,22 @@ def reverse(self): @Operations.implementation_for(CreateViewOp) def create_view(operations: Operations, operation: CreateViewOp): operations.execute( - "CREATE VIEW {} AS {}".format(operation.target.name, operation.target.sqltext) + "CREATE VIEW %s AS %s" % (operation.target.name, operation.target.sqltext) ) @Operations.implementation_for(DropViewOp) def drop_view(operations: Operations, operation: DropViewOp): - operations.execute("DROP VIEW {}".format(operation.target.name)) + operations.execute("DROP VIEW %s" % operation.target.name) @Operations.implementation_for(CreateTriggerOp) def create_trigger(operations: Operations, operation: CreateTriggerOp): operations.execute( - "CREATE FUNCTION {}() {}".format( - operation.target.name, operation.target.function - ) + "CREATE FUNCTION %s() %s" % (operation.target.name, operation.target.function) ) operations.execute( - "CREATE TRIGGER {} {}".format(operation.target.name, operation.target.trigger) + "CREATE TRIGGER %s %s" % (operation.target.name, operation.target.trigger) ) @@ -115,4 +113,4 @@ def drop_trigger(operations: Operations, operation: DropTriggerOp): operations.execute( "DROP TRIGGER {} ON {};".format(operation.target.name, operation.target.table) ) - operations.execute("DROP FUNCTION {}();".format(operation.target.name)) + operations.execute("DROP FUNCTION %s();" % 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 deleted file mode 100644 index 8fc6246db..000000000 --- a/src/backend_api/app/alembic/versions/17daa1c96438_removing_modifier_auto_increment.py +++ /dev/null @@ -1,185 +0,0 @@ -"""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 47942eee0..30bef2fe1 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,6 +13,7 @@ from app.alembic.replaceable_objects.main import ReplaceableTrigger + # revision identifiers, used by Alembic. revision: str = "e38727349f3f" down_revision: Union[str, None] = "0f3f15f56b7d" @@ -24,7 +25,7 @@ "aggregate_unidentified", "unidentified_item", """ - RETURNS TRIGGER AS ${name}$ + RETURNS TRIGGER AS $aggregate_unidentified$ DECLARE current_hour INT; divine_id INT; @@ -64,12 +65,12 @@ RETURN NEW; END; - ${name}$ LANGUAGE plpgsql; + $aggregate_unidentified$ LANGUAGE plpgsql; """, """ - BEFORE INSERT ON {table} + BEFORE INSERT ON unidentified_item FOR EACH ROW - EXECUTE FUNCTION {name}(); + EXECUTE FUNCTION aggregate_unidentified(); """, ) diff --git a/src/backend_api/app/api/routes/currency.py b/src/backend_api/app/api/routes/currency.py index 30f1c69ba..89a009310 100644 --- a/src/backend_api/app/api/routes/currency.py +++ b/src/backend_api/app/api/routes/currency.py @@ -135,7 +135,7 @@ async def create_currency( @router.put( - "/", + "/{currencyId}", response_model=schemas.Currency, dependencies=[ Depends(get_current_active_superuser), @@ -162,7 +162,7 @@ async def update_currency( @router.delete( - "/", + "/{currencyId}", response_model=str, dependencies=[ Depends(get_current_active_superuser), diff --git a/src/backend_api/app/api/routes/item_base_type.py b/src/backend_api/app/api/routes/item_base_type.py index 1b7a64597..cdd2c31af 100644 --- a/src/backend_api/app/api/routes/item_base_type.py +++ b/src/backend_api/app/api/routes/item_base_type.py @@ -113,7 +113,7 @@ async def create_item_base_type( @router.put( - "/", + "/{itemBaseTypeId}", response_model=schemas.ItemBaseType, dependencies=[ Depends(get_current_active_superuser), @@ -141,7 +141,7 @@ async def update_item_base_type( @router.delete( - "/", + "/{itemBaseTypeId}", response_model=str, dependencies=[Depends(get_current_active_superuser)], ) diff --git a/src/backend_api/app/api/routes/modifier.py b/src/backend_api/app/api/routes/modifier.py index 8621463d0..2a55852e0 100644 --- a/src/backend_api/app/api/routes/modifier.py +++ b/src/backend_api/app/api/routes/modifier.py @@ -144,7 +144,6 @@ async def create_modifier( ) async def update_modifier( modifierId: int, - position: int, modifier_update: schemas.ModifierUpdate, db: Session = Depends(get_db), ): @@ -156,7 +155,7 @@ async def update_modifier( Returns the updated modifier. """ - modifier_map = {"modifierId": modifierId, "position": position} + modifier_map = {"modifierId": modifierId} modifier = await CRUD_modifier.get( db=db, @@ -173,7 +172,6 @@ async def update_modifier( ) async def delete_modifier( modifierId: int, - position: int, db: Session = Depends(get_db), ): """ @@ -183,7 +181,7 @@ async def delete_modifier( Always deletes one modifier. """ - modifier_map = {"modifierId": modifierId, "position": position} + modifier_map = {"modifierId": modifierId} 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 0f5951bf4..0a2bd2700 100644 --- a/src/backend_api/app/core/models/models.py +++ b/src/backend_api/app/core/models/models.py @@ -8,14 +8,13 @@ DateTime, Float, ForeignKey, - ForeignKeyConstraint, Identity, Index, Integer, - PrimaryKeyConstraint, SmallInteger, String, Text, + UniqueConstraint, func, ) from sqlalchemy.dialects.postgresql import JSONB, UUID @@ -145,7 +144,11 @@ class UnidentifiedItem(_ItemBase, Base): class Modifier(Base): __tablename__ = "modifier" - modifierId: Mapped[int] = mapped_column(SmallInteger, nullable=False) + modifierId: Mapped[int] = mapped_column( + SmallInteger, + Identity(start=1, increment=1, cycle=True), + primary_key=True, + ) position: Mapped[int] = mapped_column(SmallInteger, nullable=False) minRoll: Mapped[float | None] = mapped_column(Float(4)) maxRoll: Mapped[float | None] = mapped_column(Float(4)) @@ -172,7 +175,6 @@ class Modifier(Base): ) __table_args__ = ( - PrimaryKeyConstraint("modifierId", "position"), CheckConstraint( """ CASE @@ -218,6 +220,7 @@ class Modifier(Base): """ modifier."maxRoll" >= modifier."minRoll" """, name="check_modifier_maxRoll_greaterThan_minRoll", ), + UniqueConstraint(modifierId, position), ) @@ -229,10 +232,11 @@ class ItemModifier(Base): modifierId: Mapped[int] = mapped_column( SmallInteger, - nullable=False, - ) - position: Mapped[int] = mapped_column( - SmallInteger, + ForeignKey( + "modifier.modifierId", + ondelete="CASCADE", + onupdate="CASCADE", + ), nullable=False, ) createdHoursSinceLaunch: Mapped[int] = mapped_column(SmallInteger, nullable=False) @@ -245,12 +249,6 @@ 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 953f9a1c5..ca2d8630e 100644 --- a/src/backend_api/app/core/schemas/item_modifier.py +++ b/src/backend_api/app/core/schemas/item_modifier.py @@ -7,7 +7,6 @@ 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 75d2d6212..999764436 100644 --- a/src/backend_api/app/core/schemas/modifier.py +++ b/src/backend_api/app/core/schemas/modifier.py @@ -27,12 +27,11 @@ class _BaseModifier(_pydantic.BaseModel): class GroupedModifierProperties(_pydantic.BaseModel): - position: list[int] + modifierId: 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/core/schemas/plot/__init__.py b/src/backend_api/app/core/schemas/plot/__init__.py index 0de392497..6a66bd346 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, - ModifierLimitation, + ModifierLimitations, ) 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 a7f333eb2..f5052ab58 100644 --- a/src/backend_api/app/core/schemas/plot/input.py +++ b/src/backend_api/app/core/schemas/plot/input.py @@ -26,8 +26,7 @@ class BaseSpecs(_pydantic.BaseModel): subCategory: str | None = None -class ModifierLimitation(_pydantic.BaseModel): - position: int +class ModifierLimitations(_pydantic.BaseModel): maxRoll: float | None = None minRoll: float | None = None textRoll: int | None = None @@ -35,7 +34,7 @@ class ModifierLimitation(_pydantic.BaseModel): class WantedModifier(_pydantic.BaseModel): modifierId: int - modifierLimitations: list[ModifierLimitation] | None = None + modifierLimitations: ModifierLimitations | None = None class BasePlotQuery(_pydantic.BaseModel): @@ -49,13 +48,13 @@ class BasePlotQuery(_pydantic.BaseModel): class PlotQuery(BasePlotQuery): "Plots for items with or without modifiers" - wantedModifiers: list[WantedModifier] | None = None + wantedModifiers: list[list[WantedModifier]] | None = None class IdentifiedPlotQuery(BasePlotQuery): "Plots for items with modifiers" - wantedModifiers: list[WantedModifier] + wantedModifiers: list[list[WantedModifier]] class UnidentifiedPlotQuery(BasePlotQuery): diff --git a/src/backend_api/app/crud/extensions/crud_modifier.py b/src/backend_api/app/crud/extensions/crud_modifier.py index 1c8f0652f..864e6bf61 100644 --- a/src/backend_api/app/crud/extensions/crud_modifier.py +++ b/src/backend_api/app/crud/extensions/crud_modifier.py @@ -1,6 +1,7 @@ +import pandas as pd from fastapi import HTTPException from pydantic import TypeAdapter -from sqlalchemy import func, select +from sqlalchemy import select from sqlalchemy.orm import Session from app.core.models.models import Modifier as model_Modifier @@ -24,29 +25,52 @@ class CRUDModifier( async def get_grouped_modifier_by_effect(self, db: Session): stmt = select( model_Modifier.modifierId, - 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() - - if not grouped_modifier_by_effect_record: + 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") + validate = TypeAdapter( GroupedModifierByEffect | list[GroupedModifierByEffect] ).validate_python diff --git a/src/backend_api/app/plotting/plotter.py b/src/backend_api/app/plotting/plotter.py index bbdb033eb..29b101d53 100644 --- a/src/backend_api/app/plotting/plotter.py +++ b/src/backend_api/app/plotting/plotter.py @@ -43,7 +43,7 @@ BasePlotQuery, IdentifiedPlotQuery, ItemSpecs, - ModifierLimitation, + ModifierLimitations, PlotData, PlotQuery, UnidentifiedPlotQuery, @@ -277,7 +277,7 @@ def _raise_invalid_query(self, query: PlotQuery) -> None: ) def _check_rolls( - self, modifier_limitations: ModifierLimitation + self, modifier_limitations: ModifierLimitations ) -> 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[WantedModifier], + wanted_modifier_query: list[list[WantedModifier]], start: int | None, end: int | None, ) -> Select: @@ -308,17 +308,20 @@ def _add_wanted_modifiers( one is chosen. """ exists_conditions = [] - 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) - + 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 + ) 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 @@ -328,6 +331,7 @@ def _add_wanted_modifiers( and_conditions.append( model_ItemModifier.createdHoursSinceLaunch <= end ) + exists_conditions.append( select(1) .where( @@ -338,7 +342,8 @@ def _add_wanted_modifiers( ) .exists() ) - else: + + if wanted_modifier and not modifier_roll_limitation_found: and_conditions = [ model_Item.itemId == model_ItemModifier.itemId, model_ItemModifier.modifierId == wanted_modifier.modifierId, diff --git a/src/backend_api/app/tests/test_simulating_env/api/api_routes_test_base.py b/src/backend_api/app/tests/test_simulating_env/api/api_routes_test_base.py index 60eed6638..5a404ecd5 100644 --- a/src/backend_api/app/tests/test_simulating_env/api/api_routes_test_base.py +++ b/src/backend_api/app/tests/test_simulating_env/api/api_routes_test_base.py @@ -176,7 +176,7 @@ async def test_create_on_duplicate_pkey( on_duplicate_params: tuple[bool, str | None], ) -> None: """Test create found duplicate instance""" - # on_duplicate_pkey_do_nothing = on_duplicate_params[0] + on_duplicate_pkey_do_nothing = on_duplicate_params[0] # if not on_duplicate_pkey_do_nothing: # TODO: Make test database mock with unique constraints pytest.skip( @@ -398,6 +398,7 @@ async def test_update_instance( object_generator_func: Callable[[], tuple[dict, ModelType]], route_prefix: str, model_table_name: str, + unique_identifier: str, update_request_params: bool, ignore_test_columns: list[str], is_hypertable: bool, @@ -428,8 +429,7 @@ async def test_update_instance( update_obj_pk_map = self._create_primary_key_map(update_object_out) delete_response = await async_client.delete( - f"{settings.API_V1_STR}/{route_prefix}/", - params=update_obj_pk_map, + f"{settings.API_V1_STR}/{route_prefix}/{update_obj_pk_map[unique_identifier]}", headers=superuser_token_headers, ) # delete the object to avoid unique constraint errorspÄ assert delete_response.status_code == 200 @@ -439,18 +439,24 @@ async def test_update_instance( content_delete == get_delete_return_msg( model_table_name=model_table_name, - filter=update_obj_pk_map, + filter={unique_identifier: update_obj_pk_map[unique_identifier]}, ).message ) if update_request_params: obj_out_pk_map = self._create_primary_key_map(object_out) - response = await async_client.put( - f"{settings.API_V1_STR}/{route_prefix}/", - headers=superuser_token_headers, - json=update_object_dict, - params=obj_out_pk_map, - ) + response = await async_client.put( + f"{settings.API_V1_STR}/{route_prefix}/", + headers=superuser_token_headers, + json=update_object_dict, + params=obj_out_pk_map, + ) + else: + response = await async_client.put( + f"{settings.API_V1_STR}/{route_prefix}/{obj_out_pk_map[unique_identifier]}", + headers=superuser_token_headers, + json=update_object_dict, + ) assert response.status_code == 200 content = response.json() @@ -470,6 +476,8 @@ async def test_update_instance_not_found( object_generator_func: Callable[[], tuple[dict, ModelType]], route_prefix: str, model_table_name: str, + update_request_params: bool, + unique_identifier: str, is_hypertable: bool, ) -> None: """Test update instance not found @@ -499,8 +507,7 @@ async def test_update_instance_not_found( update_obj_out_pk_map = self._create_primary_key_map(update_object_out) delete_response = await async_client.delete( - f"{settings.API_V1_STR}/{route_prefix}/", - params=update_obj_out_pk_map, + f"{settings.API_V1_STR}/{route_prefix}/{update_obj_out_pk_map[unique_identifier]}", headers=superuser_token_headers, ) # delete the object to avoid unique constraint errors assert delete_response.status_code == 200 @@ -511,7 +518,7 @@ async def test_update_instance_not_found( content_delete == get_delete_return_msg( model_table_name=model_table_name, - filter=update_obj_out_pk_map, + filter={unique_identifier: update_obj_out_pk_map[unique_identifier]}, ).message ) @@ -519,12 +526,19 @@ async def test_update_instance_not_found( for key in update_obj_out_pk_map: update_obj_out_pk_map[key] = not_found_object - response = await async_client.put( - f"{settings.API_V1_STR}/{route_prefix}/", - headers=superuser_token_headers, - json=update_object_dict, - params=update_obj_out_pk_map, - ) + if update_request_params: + response = await async_client.put( + f"{settings.API_V1_STR}/{route_prefix}/", + headers=superuser_token_headers, + json=update_object_dict, + params=update_obj_out_pk_map, + ) + else: + response = await async_client.put( + f"{settings.API_V1_STR}/{route_prefix}/{not_found_object}", + headers=superuser_token_headers, + json=update_object_dict, + ) db_obj_does_not_exist_error = DbObjectDoesNotExistError( model_table_name=model_table_name, @@ -544,6 +558,8 @@ async def test_update_instance_not_enough_permissions( route_prefix: str, model_table_name: str, superuser_token_headers: dict[str, str], + unique_identifier: str, + update_request_params: bool, is_hypertable: bool, ) -> None: """Test update instance not enough permissions @@ -575,8 +591,7 @@ async def test_update_instance_not_enough_permissions( update_obj_pk_map = self._create_primary_key_map(update_object_out) delete_response = await async_client.delete( - f"{settings.API_V1_STR}/{route_prefix}/", - params=update_obj_pk_map, + f"{settings.API_V1_STR}/{route_prefix}/{update_obj_pk_map[unique_identifier]}", headers=superuser_token_headers, ) # delete the object to avoid unique constraint errors assert delete_response.status_code == 200 @@ -587,15 +602,21 @@ async def test_update_instance_not_enough_permissions( content_delete == get_delete_return_msg( model_table_name=model_table_name, - filter=update_obj_pk_map, + filter={unique_identifier: update_obj_pk_map[unique_identifier]}, ).message ) - response = await async_client.put( - f"{settings.API_V1_STR}/{route_prefix}/", - json=update_object_dict, - params=obj_pk_map, - ) + if update_request_params: + response = await async_client.put( + f"{settings.API_V1_STR}/{route_prefix}/", + json=update_object_dict, + params=obj_pk_map, + ) + else: + response = await async_client.put( + f"{settings.API_V1_STR}/{route_prefix}/{obj_pk_map[unique_identifier]}", + json=update_object_dict, + ) invalid_token_error = InvalidTokenError( token=None, function_name=UserCache.verify_token.__name__, @@ -614,6 +635,7 @@ async def test_delete_instance( object_generator_func: Callable[[], tuple[dict, ModelType]], route_prefix: str, model_table_name: str, + unique_identifier: str, is_hypertable: bool, ) -> None: """Test delete instance @@ -638,8 +660,7 @@ async def test_delete_instance( update_obj_pk_map = self._create_primary_key_map(update_object_out) response = await async_client.delete( - f"{settings.API_V1_STR}/{route_prefix}/", - params=update_obj_pk_map, + f"{settings.API_V1_STR}/{route_prefix}/{update_obj_pk_map[unique_identifier]}", headers=superuser_token_headers, ) assert response.status_code == 200 @@ -649,7 +670,7 @@ async def test_delete_instance( content == get_delete_return_msg( model_table_name=model_table_name, - filter=update_obj_pk_map, + filter={unique_identifier: update_obj_pk_map[unique_identifier]}, ).message ) @@ -657,11 +678,10 @@ async def test_delete_instance( async def test_delete_instance_not_found( self, async_client: AsyncClient, - db: Session, superuser_token_headers: dict[str, str], - object_generator_func: Callable[[], tuple[dict, ModelType]], route_prefix: str, model_table_name: str, + unique_identifier: str, crud_instance: CRUDBase, is_hypertable: bool, ) -> None: @@ -677,23 +697,14 @@ async def test_delete_instance_not_found( if is_hypertable: pytest.skip("Hypertables doesn't support delete object operations") - object_dict, object_out = await self._create_random_object_crud( - db, object_generator_func - ) # create the object to update and add to the db - self._test_object(object_out, object_dict) - - delete_obj_out_pk_map = self._create_primary_key_map(object_out) - for key in delete_obj_out_pk_map.keys(): - delete_obj_out_pk_map[key] = 999 - + not_found_object = 999 response = await async_client.delete( - f"{settings.API_V1_STR}/{route_prefix}/", - params=delete_obj_out_pk_map, + f"{settings.API_V1_STR}/{route_prefix}/{not_found_object}", headers=superuser_token_headers, ) db_obj_not_exists_error = DbObjectDoesNotExistError( model_table_name=model_table_name, - filter=delete_obj_out_pk_map, + filter={unique_identifier: not_found_object}, function_name=crud_instance.remove.__name__, class_name=crud_instance.__class__.__name__, ) @@ -708,6 +719,7 @@ async def test_delete_instance_not_enough_permissions( db: Session, object_generator_func: Callable[[], tuple[dict, ModelType]], route_prefix: str, + unique_identifier: str, is_hypertable: bool, ) -> None: """Test delete instance not enough permissions @@ -728,7 +740,7 @@ async def test_delete_instance_not_enough_permissions( _, object_out = await self._create_random_object_crud(db, object_generator_func) obj_out_pk_map = self._create_primary_key_map(object_out) response = await async_client.delete( - f"{settings.API_V1_STR}/{route_prefix}/", params=obj_out_pk_map + f"{settings.API_V1_STR}/{route_prefix}/{obj_out_pk_map[unique_identifier]}", ) invalid_token_error = InvalidTokenError( token=None, diff --git a/src/backend_api/app/tests/test_simulating_env/api/routes/test_modifier.py b/src/backend_api/app/tests/test_simulating_env/api/routes/test_modifier.py index 1c51af94a..c68e75b88 100644 --- a/src/backend_api/app/tests/test_simulating_env/api/routes/test_modifier.py +++ b/src/backend_api/app/tests/test_simulating_env/api/routes/test_modifier.py @@ -107,14 +107,13 @@ def create_random_object_func() -> Callable[[], dict]: async def get_object_from_api_normal_user( async_client: AsyncClient, route_prefix: str, - _: str, + unique_identifier: str, normal_user_token_headers: dict[str, str], ) -> Callable[[Any, Any], Awaitable[Any]]: async def _get_object(object_pk_map: dict[str, Any]) -> Response: response = await async_client.get( - f"{settings.API_V1_STR}/{route_prefix}/", + f"{settings.API_V1_STR}/{route_prefix}/{object_pk_map[unique_identifier]}", headers=normal_user_token_headers, - params=object_pk_map, ) return response 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 5a7d5d5bb..363a672dc 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 | 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)` | +| 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)` | ## Item Base types 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 bad7d88bb..82c5ceedd 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( 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 41921c4ff..050291230 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: 5|6 +# Total modifiers on each item: 5|6 # Can have duplicate modifiers: False|False|False|False -# Modifier distribution: 1:1,5:1,8:1,"rest":2|3 +# Modifier distrubution: 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 3ba2aa920..157f41004 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: 3 +# Total modifiers on each item: 3 # Can have duplicate modifiers: False|False -# Modifier distribution: 1:1,"rest":2 +# Modifier distrubution: 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 45b028289..5e6b5c896 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: 3 +# Total modifiers on each item: 3 # Can have duplicate modifiers: False|False|False -# Modifier distribution: 18:1,36:1,"rest":1 +# Modifier distrubution: 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 bddc12f1c..1d5220889 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: 2 +# Total modifiers on each item: 2 # Can have duplicate modifiers: False -# Modifier distribution: "rest":2 +# Modifier distrubution: "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 70f3092db..49ce755f6 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 distribution: 1:1,6:1,25:1|2,"rest":0|1 +# Modifier distrubution: 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 4ac367f29..a22791fb2 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: 6 +# Total modifiers on each item: 6 # Can have duplicate modifiers: False|False -# Modifier distribution: 2:2,"rest":4 +# Modifier distrubution: 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 b1584603f..2b351ff60 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: 5 +# Total modifiers on each item: 5 # Can have duplicate modifiers: False|False -# Modifier distribution: 4:3,"rest":2 +# Modifier distrubution: 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 2726d11d5..59ded8898 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: 5 +# Total modifiers on each item: 5 # Can have duplicate modifiers: False|False -# Modifier distribution: 4:3,"rest":2 +# Modifier distrubution: 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 b4d5cebb8..517788397 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: 5 +# Total modifiers on each item: 5 # Can have duplicate modifiers: False|False -# Modifier distribution: 4:3,"rest":2 +# Modifier distrubution: 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 1222452c4..a7f27af4d 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: 5 +# Total modifiers on each item: 5 # Can have duplicate modifiers: False|False -# Modifier distribution: 4:3,"rest":2 +# Modifier distrubution: 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 a58278523..5a7c4ca5c 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: 5 +# Total modifiers on each item: 5 # Can have duplicate modifiers: False|False -# Modifier distribution: 4:3,"rest":2 +# Modifier distrubution: 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 d9833ebdc..44c555a99 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: 2 +# Total modifiers on each item: 2 # Can have duplicate modifiers: False -# Modifier distribution: "rest":2 +# Modifier distrubution: "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 551db82a3..29e800089 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: 1 +# Total modifiers on each item: 1 # Can have duplicate modifiers: False -# Modifier distribution: "rest":1 +# Modifier distrubution: "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 84da411a1..5947e54bf 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: 1 +# Total modifiers on each item: 1 # Can have duplicate modifiers: False -# Modifier distribution: "rest":1 +# Modifier distrubution: "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 1f18bef01..c4997ffa0 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: 3 +# Total modifiers on each item: 3 # Can have duplicate modifiers: True -# Modifier distribution: "rest":3 +# Modifier distrubution: "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 ee9bd7a11..f6e93f90d 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: 2 +# Total modifiers on each item: 2 # Can have duplicate modifiers: False -# Modifier distribution: "rest":2 +# Modifier distrubution: "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 1efd7ae1d..f21caa2db 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: 1 +# Total modifiers on each item: 1 # Can have duplicate modifiers: False -# Modifier distribution: "rest":1 +# Modifier distrubution: "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 6e2fb806d..5428766e9 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: 2 +# Total modifiers on each item: 2 # Can have duplicate modifiers: False -# Modifier distribution: "rest":2 +# Modifier distrubution: "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 02a5c60a2..f2da540c0 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: 1 +# Total modifiers on each item: 1 # Can have duplicate modifiers: False -# Modifier distribution: "rest":1 +# Modifier distrubution: "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 399de7c3c..fd243d7d1 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: 2 +# Total modifiers on each item: 2 # Can have duplicate modifiers: False -# Modifier distribution: "rest":2 +# Modifier distrubution: "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 57482eedc..3988bb6ec 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: 5 +# Total modifiers on each item: 6 # Can have duplicate modifiers: False -# Modifier distribution: "rest":5 +# Modifier distrubution: "rest":6 # 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 0570ee4c5..fb4de1f60 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: 4 +# Total modifiers on each item: 4 # Can have duplicate modifiers: False|False -# Modifier distribution: 3:2,"rest":2 +# Modifier distrubution: 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 ce78d4d2c..492d4c1f5 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: 3 +# Total modifiers on each item: 3 # Can have duplicate modifiers: False|False -# Modifier distribution: 1:1,"rest":2 +# Modifier distrubution: 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 22b6dc997..fc704e797 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: 6 +# Total modifiers on each item: 6 # Can have duplicate modifiers: False|True -# Modifier distribution: 9:3,"rest":3 +# Modifier distrubution: 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 1914b72b0..9304d5b45 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: 4 +# Total modifiers on each item: 4 # Can have duplicate modifiers: False -# Modifier distribution: "rest":4 +# Modifier distrubution: "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 8c3358db7..d55d2fc26 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: 3|4 -# Can have duplicate modifiers: False|True -# Modifier distribution: 2:2,"rest":1|2 +# Total modifiers on each item: 3|4|5 +# Can have duplicate modifiers: False +# Modifier distrubution: 2:2,"rest":1|2|3 # 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 cec88d487..e2de7b33f 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: 6 +# Total modifiers on each item: 6 # Can have duplicate modifiers: True -# Modifier distribution: "rest":6 +# Modifier distrubution: "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 386fce70f..18e490d0c 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: 5 +# Total modifiers on each item: 5 # Can have duplicate modifiers: False -# Modifier distribution: "rest":5 +# Modifier distrubution: "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 5e26914e1..ffc56372b 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: 2 +# Total modifiers on each item: 2 # Can have duplicate modifiers: False -# Modifier distribution: "rest":2 +# Modifier distrubution: "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 644fac577..9d29533db 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: 3 +# Total modifiers on each item: 3 # Can have duplicate modifiers: False|False|False -# Modifier distribution: 2:1,3:1,"rest":1 +# Modifier distrubution: 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 4e4dc9ff3..2f70c1a88 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: 4 +# Total modifiers on each item: 4 # Can have duplicate modifiers: False -# Modifier distribution: "rest":4 +# Modifier distrubution: "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 cd37957f6..26693eaed 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: 1 +# Total modifiers on each item: 1 # Can have duplicate modifiers: False -# Modifier distribution: "rest":1 +# Modifier distrubution: "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 e464f4603..19fca0dfa 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: 1 +# Total modifiers on each item: 1 # Can have duplicate modifiers: False -# Modifier distribution: "rest":1 +# Modifier distrubution: "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 0d385289b..42bea2d87 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: 4 +# Total modifiers on each item: 4 # Can have duplicate modifiers: False -# Modifier distribution: "rest":4 +# Modifier distrubution: "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 246550e80..bdfce79c0 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: 3 +# Total modifiers on each item: 3 # Can have duplicate modifiers: False -# Modifier distribution: "rest":3 +# Modifier distrubution: "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 a47c0c5f6..969381ebc 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: 2 +# Total modifiers on each item: 2 # Can have duplicate modifiers: False -# Modifier distribution: "rest":2 +# Modifier distrubution: "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 cc3f4d6da..28dca4b0a 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 distribution: 3:3,"rest":2|3 +# Modifier distrubution: 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 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 7d16ee9f2..1728c95cd 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={}&position={}" + update_url = self.data_url + "?modifierId={}" 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"], row_cur["position"]), + update_url.format(row_cur["modifierId"]), json=data, headers=headers, # add HTTP Basic Auth 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 edeed8ab6..3f47e30ce 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,12 +28,10 @@ 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 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 118203542..ae55b3910 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,7 +58,6 @@ 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 70a0da357..df4c2157d 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,7 +463,6 @@ 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/config.py b/src/backend_data_retrieval/data_retrieval_app/tests/scripts/create_public_stashes_test_data/config.py index 46c18da79..afb13815e 100644 --- a/src/backend_data_retrieval/data_retrieval_app/tests/scripts/create_public_stashes_test_data/config.py +++ b/src/backend_data_retrieval/data_retrieval_app/tests/scripts/create_public_stashes_test_data/config.py @@ -26,12 +26,7 @@ def dispersed_timing_enabled(self) -> bool: ] # , "divine", "mirror", etc. MEAN_ITEM_PRICE: int = 200 - ALL_SOFTCORE_LEAGUES: str - - @computed_field # type: ignore[prop-decorator] - @property - def SOFTCORE_LEAGUES(self) -> list[str]: - return self.ALL_SOFTCORE_LEAGUES.split("|") + SOFTCORE_LEAGUES: list[str] = ["Mercenaries", "Phrecia"] @computed_field # type: ignore[prop-decorator] @property 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 de8694d4b..11b415d2f 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,32 +111,35 @@ def _parse_comment( return output - def _find_modifier_and_rolls( + def _find_connected_modifier_ids_and_rolls( self, modifier_df: pd.DataFrame, - modifier_distribution: dict[int | str, list[int]], + modifier_distrubution: dict[int | str, list[int]], ) -> tuple[ dict[int | str, list[list[int]]], dict[int | str, list[tuple[int, int] | str]] ]: """ - Example input modifier_distribution dict: + Example input modifier_distrubution dict: { - 3: [2], + 3: [1], "rest": [1] } Example output connected_modifier_ids_dict: { 3: [ - 1, - 5 + [1], + [5, 6] ], "rest": [ - 2 + [1] ] } Example output connected_rolls_dict: { - 3: {1: {"rolls": [[1,20], "Balbala|Xibaqua"]}, 5: {"rolls": [[1,4]]}}, + 3: [ + [1, 20], + "Balbala|Xibaqua" + ], "rest": [ [] ] @@ -147,13 +150,14 @@ def _find_modifier_and_rolls( roll is seperated by `|` """ - def get_roll_ranges( + def get_modifier_ids_rolls_effect( row: pd.Series, ) -> pd.Series: - modifier_id = row["modifierId"] + choosable_modifier_ids = row["groupedModifierProperties"]["modifierId"] + effect = row["effect"] df: pd.DataFrame = self.db_modifier_df.loc[ - self.db_modifier_df["modifierId"] == modifier_id + self.db_modifier_df["effect"] == effect ] not_static = all(df["static"].isna()) rolls = [] @@ -174,13 +178,19 @@ def get_roll_ranges( .to_list() ) - return pd.Series({"modifier_id": modifier_id, "rolls": rolls}) + return pd.Series( + { + "modifier_ids": choosable_modifier_ids, + "rolls": rolls, + "effect": effect, + } + ) grouped_modifier_df = self.grouped_modifier_df prev_key = 0 connected_modifier_ids_dict = {} connected_rolls_dict = {} - for key in modifier_distribution: + for key in modifier_distrubution: upper_bound = key if key == "rest": upper_bound = len(modifier_df) @@ -203,18 +213,22 @@ def get_roll_ranges( choosable_effects_mask ] - modifier_ids = choosable_grouped_modifier_df["modifierId"].to_list() - effects = choosable_grouped_modifier_df["effect"].to_list() + modifier_ids_rolls_effect_df = choosable_grouped_modifier_df.apply( + get_modifier_ids_rolls_effect, axis=1 + ) + modifier_ids: list[list[int]] = modifier_ids_rolls_effect_df[ + "modifier_ids" + ].to_list() connected_modifier_ids_dict[key] = modifier_ids - rolls = choosable_grouped_modifier_df.apply(get_roll_ranges, axis=1) - rolls.set_index("modifier_id", inplace=True) - rolls = rolls.to_dict()["rolls"] + + rolls = modifier_ids_rolls_effect_df["rolls"].to_list() 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 modifier_id not in self.modifier_ids_to_effect_map: - self.modifier_ids_to_effect_map[modifier_id] = effect + 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 @@ -249,26 +263,25 @@ def create_templates(self) -> None: modifier_comments["Can have duplicate modifiers"] ) - if "Modifier distribution" in modifier_comments: - modifier_template["distribution"] = self._parse_comment( - modifier_comments["Modifier distribution"] + if "Modifier distrubution" in modifier_comments: + modifier_template["distrubution"] = self._parse_comment( + modifier_comments["Modifier distrubution"] ) else: - modifier_template["distribution"] = {"rest": 6} + modifier_template["distrubution"] = {"rest": 6} ( modifier_template["modifier_ids_to_choose"], modifier_template["roll_ranges"], - ) = self._find_modifier_and_rolls( - modifier_df, modifier_template["distribution"] + ) = self._find_connected_modifier_ids_and_rolls( + modifier_df, modifier_template["distrubution"] ) - # 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["distribution"].keys(), + modifier_template["distrubution"].keys(), modifier_template["modifier_ids_to_choose"].keys(), modifier_template["roll_ranges"].keys(), ] @@ -280,7 +293,7 @@ def create_templates(self) -> None: + str( [ len(modifier_template["can_duplicate"]), - len(modifier_template["distribution"].keys()), + len(modifier_template["distrubution"].keys()), len(modifier_template["modifier_ids_to_choose"].keys()), len(modifier_template["roll_ranges"].keys()), ] @@ -332,34 +345,35 @@ def _choose_and_make_modifiers( Note: Only works when len(can_duplicate) == - len(distribution.keys()) + len(distrubution.keys()) == len(modifier_ids_to_choose.keys()) == len(roll_ranges.keys()) """ can_duplicate = template["can_duplicate"] - distribution = template["distribution"] + distrubution = template["distrubution"] modifier_ids_to_choose = template["modifier_ids_to_choose"] roll_ranges = template["roll_ranges"] modifiers = [] - for i, key in enumerate(distribution): - n_modifiers_to_create: int = random.choice(distribution[key]) + for i, key in enumerate(distrubution): + n_modifiers_to_create: int = random.choice(distrubution[key]) remove_chosen_modifiers_from_pool: bool = not can_duplicate[i] - modifier_ids_to_choose_from: list[int] = modifier_ids_to_choose[key].copy() - complementing_roll_ranges: dict[int, list[str | list[float]]] = roll_ranges[ + modifier_ids_to_choose_from: list[list[int]] = modifier_ids_to_choose[ key ].copy() + complementing_roll_ranges: list[str | list[float]] = roll_ranges[key].copy() for _ in range(n_modifiers_to_create): - modifier_id = random.choice(modifier_ids_to_choose_from) + choice_made = random.choice(range(len(modifier_ids_to_choose_from))) - chosen_rolls = complementing_roll_ranges[modifier_id] + chosen_modifier_ids = modifier_ids_to_choose_from[choice_made] + chosen_rolls = complementing_roll_ranges[choice_made] - effect = self.modifier_ids_to_effect_map[modifier_id] + effect = self.modifier_ids_to_effect_map[tuple(chosen_modifier_ids)] if chosen_rolls: # equivalent to modifier not being static @@ -369,8 +383,9 @@ 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.remove(modifier_id) - complementing_roll_ranges.pop(modifier_id) + modifier_ids_to_choose_from.pop(choice_made) + complementing_roll_ranges.pop(choice_made) + return modifiers def _create_item_dict_from_template( @@ -412,16 +427,12 @@ def _create_item_dict_from_template( def create_test_data( self, ) -> Iterator[tuple[str, list[dict[str, Any]]]]: - 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 + 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 def main() -> int: diff --git a/src/frontend/Dockerfile b/src/frontend/Dockerfile index b0b426297..126ed9f63 100644 --- a/src/frontend/Dockerfile +++ b/src/frontend/Dockerfile @@ -5,7 +5,7 @@ WORKDIR /app COPY package*.json /app/ -RUN npm ci +RUN npm install COPY ./ /app/ diff --git a/src/frontend/openapi.json b/src/frontend/openapi.json index fdf3cf208..89ff6f5d3 100644 --- a/src/frontend/openapi.json +++ b/src/frontend/openapi.json @@ -1538,15 +1538,6 @@ "type": "integer", "title": "Modifierid" } - }, - { - "name": "position", - "in": "query", - "required": true, - "schema": { - "type": "integer", - "title": "Position" - } } ], "responses": { @@ -1802,15 +1793,6 @@ "type": "integer", "title": "Modifierid" } - }, - { - "name": "position", - "in": "query", - "required": true, - "schema": { - "type": "integer", - "title": "Position" - } } ], "requestBody": { @@ -2313,10 +2295,6 @@ }, "GroupedModifierByEffect": { "properties": { - "modifierId": { - "type": "integer", - "title": "Modifierid" - }, "effect": { "type": "string", "title": "Effect" @@ -2353,7 +2331,6 @@ }, "type": "object", "required": [ - "modifierId", "effect", "regex", "static", @@ -2364,12 +2341,12 @@ }, "GroupedModifierProperties": { "properties": { - "position": { + "modifierId": { "items": { "type": "integer" }, "type": "array", - "title": "Position" + "title": "Modifierid" }, "textRolls": { "items": { @@ -2388,7 +2365,7 @@ }, "type": "object", "required": [ - "position", + "modifierId", "textRolls" ], "title": "GroupedModifierProperties" @@ -2507,17 +2484,6 @@ "type": "string", "title": "Rarity" }, - "gameItemId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Gameitemid" - }, "identified": { "type": "boolean", "title": "Identified", @@ -2839,17 +2805,6 @@ "type": "string", "title": "Rarity" }, - "gameItemId": { - "anyOf": [ - { - "type": "string" - }, - { - "type": "null" - } - ], - "title": "Gameitemid" - }, "identified": { "type": "boolean", "title": "Identified", @@ -3022,10 +2977,6 @@ "type": "integer", "title": "Modifierid" }, - "position": { - "type": "integer", - "title": "Position" - }, "roll": { "anyOf": [ { @@ -3046,7 +2997,6 @@ "required": [ "itemId", "modifierId", - "position", "createdHoursSinceLaunch" ], "title": "ItemModifier" @@ -3061,10 +3011,6 @@ "type": "integer", "title": "Modifierid" }, - "position": { - "type": "integer", - "title": "Position" - }, "roll": { "anyOf": [ { @@ -3085,7 +3031,6 @@ "required": [ "itemId", "modifierId", - "position", "createdHoursSinceLaunch" ], "title": "ItemModifierCreate" @@ -3655,12 +3600,8 @@ ], "title": "ModifierCreate" }, - "ModifierLimitation": { + "ModifierLimitations": { "properties": { - "position": { - "type": "integer", - "title": "Position" - }, "maxRoll": { "anyOf": [ { @@ -3696,10 +3637,7 @@ } }, "type": "object", - "required": [ - "position" - ], - "title": "ModifierLimitation" + "title": "ModifierLimitations" }, "ModifierUpdate": { "properties": { @@ -3967,7 +3905,10 @@ "anyOf": [ { "items": { - "$ref": "#/components/schemas/WantedModifier" + "items": { + "$ref": "#/components/schemas/WantedModifier" + }, + "type": "array" }, "type": "array" }, @@ -4327,16 +4268,12 @@ "modifierLimitations": { "anyOf": [ { - "items": { - "$ref": "#/components/schemas/ModifierLimitation" - }, - "type": "array" + "$ref": "#/components/schemas/ModifierLimitations" }, { "type": "null" } - ], - "title": "Modifierlimitations" + ] } }, "type": "object", @@ -4358,4 +4295,4 @@ } } } -} +} \ No newline at end of file diff --git a/src/frontend/src/client/index.ts b/src/frontend/src/client/index.ts index 8ac0e3140..f3b32a728 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 { ModifierLimitation } from './models/ModifierLimitation'; +export type { ModifierLimitations } from './models/ModifierLimitations'; 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 { $ModifierLimitation } from './schemas/$ModifierLimitation'; +export { $ModifierLimitations } from './schemas/$ModifierLimitations'; 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 6bbc0ce95..83426c20f 100644 --- a/src/frontend/src/client/models/GroupedModifierByEffect.ts +++ b/src/frontend/src/client/models/GroupedModifierByEffect.ts @@ -4,7 +4,6 @@ /* 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 79b70ac18..08775f3eb 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 = { - position: Array; + modifierId: Array; textRolls: Array<(string | null)>; }; diff --git a/src/frontend/src/client/models/Item.ts b/src/frontend/src/client/models/Item.ts index f82c264ec..04007f7ac 100644 --- a/src/frontend/src/client/models/Item.ts +++ b/src/frontend/src/client/models/Item.ts @@ -9,7 +9,6 @@ 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 e371bdcb5..00a32c350 100644 --- a/src/frontend/src/client/models/ItemCreate.ts +++ b/src/frontend/src/client/models/ItemCreate.ts @@ -9,7 +9,6 @@ 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 c47396310..54283e291 100644 --- a/src/frontend/src/client/models/ItemModifier.ts +++ b/src/frontend/src/client/models/ItemModifier.ts @@ -5,7 +5,6 @@ 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 a4d74b9ec..b58dbba58 100644 --- a/src/frontend/src/client/models/ItemModifierCreate.ts +++ b/src/frontend/src/client/models/ItemModifierCreate.ts @@ -5,7 +5,6 @@ export type ItemModifierCreate = { itemId: number; modifierId: number; - position: number; roll?: (number | null); createdHoursSinceLaunch: number; }; diff --git a/src/frontend/src/client/models/ModifierLimitation.ts b/src/frontend/src/client/models/ModifierLimitations.ts similarity index 80% rename from src/frontend/src/client/models/ModifierLimitation.ts rename to src/frontend/src/client/models/ModifierLimitations.ts index 1fe9ed89f..e67c6b45e 100644 --- a/src/frontend/src/client/models/ModifierLimitation.ts +++ b/src/frontend/src/client/models/ModifierLimitations.ts @@ -2,8 +2,7 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export type ModifierLimitation = { - position: number; +export type ModifierLimitations = { 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 d00b12cff..759b413aa 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 9dc1ad28c..06ab2d513 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 { ModifierLimitation } from './ModifierLimitation'; +import type { ModifierLimitations } from './ModifierLimitations'; export type WantedModifier = { modifierId: number; - modifierLimitations?: (Array | null); + modifierLimitations?: (ModifierLimitations | null); }; diff --git a/src/frontend/src/client/schemas/$GroupedModifierByEffect.ts b/src/frontend/src/client/schemas/$GroupedModifierByEffect.ts index 47241a172..92fa02115 100644 --- a/src/frontend/src/client/schemas/$GroupedModifierByEffect.ts +++ b/src/frontend/src/client/schemas/$GroupedModifierByEffect.ts @@ -4,10 +4,6 @@ /* 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 4f9e454b0..3b5785610 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: { - position: { + modifierId: { type: 'array', contains: { type: 'number', diff --git a/src/frontend/src/client/schemas/$Item.ts b/src/frontend/src/client/schemas/$Item.ts index 6beee37e6..ffd5e86b3 100644 --- a/src/frontend/src/client/schemas/$Item.ts +++ b/src/frontend/src/client/schemas/$Item.ts @@ -28,14 +28,6 @@ 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 1995e1f36..b8b441801 100644 --- a/src/frontend/src/client/schemas/$ItemCreate.ts +++ b/src/frontend/src/client/schemas/$ItemCreate.ts @@ -28,14 +28,6 @@ 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 4a505a920..88958f943 100644 --- a/src/frontend/src/client/schemas/$ItemModifier.ts +++ b/src/frontend/src/client/schemas/$ItemModifier.ts @@ -12,10 +12,6 @@ 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 cf1aeb4bb..e1e26f7a4 100644 --- a/src/frontend/src/client/schemas/$ItemModifierCreate.ts +++ b/src/frontend/src/client/schemas/$ItemModifierCreate.ts @@ -12,10 +12,6 @@ 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/$ModifierLimitation.ts b/src/frontend/src/client/schemas/$ModifierLimitations.ts similarity index 84% rename from src/frontend/src/client/schemas/$ModifierLimitation.ts rename to src/frontend/src/client/schemas/$ModifierLimitations.ts index 92d9460d3..e11223711 100644 --- a/src/frontend/src/client/schemas/$ModifierLimitation.ts +++ b/src/frontend/src/client/schemas/$ModifierLimitations.ts @@ -2,12 +2,8 @@ /* istanbul ignore file */ /* tslint:disable */ /* eslint-disable */ -export const $ModifierLimitation = { +export const $ModifierLimitations = { 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 502d1d031..b930568e5 100644 --- a/src/frontend/src/client/schemas/$PlotQuery.ts +++ b/src/frontend/src/client/schemas/$PlotQuery.ts @@ -54,7 +54,10 @@ export const $PlotQuery = { contains: [{ type: 'array', contains: { - type: 'WantedModifier', + type: 'array', + contains: { + type: 'WantedModifier', + }, }, }, { type: 'null', diff --git a/src/frontend/src/client/schemas/$WantedModifier.ts b/src/frontend/src/client/schemas/$WantedModifier.ts index f4ece94ec..c14035855 100644 --- a/src/frontend/src/client/schemas/$WantedModifier.ts +++ b/src/frontend/src/client/schemas/$WantedModifier.ts @@ -11,10 +11,7 @@ export const $WantedModifier = { modifierLimitations: { type: 'any-of', contains: [{ - type: 'array', - contains: { - type: 'ModifierLimitation', - }, + type: 'ModifierLimitations', }, { type: 'null', }], diff --git a/src/frontend/src/client/services/ModifiersService.ts b/src/frontend/src/client/services/ModifiersService.ts index 840e2ada3..d63b9ccfe 100644 --- a/src/frontend/src/client/services/ModifiersService.ts +++ b/src/frontend/src/client/services/ModifiersService.ts @@ -48,10 +48,8 @@ export class ModifiersService { */ public static deleteModifier({ modifierId, - position, }: { modifierId: number, - position: number, }): CancelablePromise { return __request(OpenAPI, { method: 'DELETE', @@ -59,9 +57,6 @@ export class ModifiersService { path: { 'modifierId': modifierId, }, - query: { - 'position': position, - }, errors: { 422: `Validation Error`, }, @@ -140,11 +135,9 @@ export class ModifiersService { */ public static updateModifier({ modifierId, - position, requestBody, }: { modifierId: number, - position: number, requestBody: ModifierUpdate, }): CancelablePromise { return __request(OpenAPI, { @@ -152,7 +145,6 @@ 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 aee131314..255079891 100644 --- a/src/frontend/src/components/Input/ModifierInputComp/FancyModifierInput.tsx +++ b/src/frontend/src/components/Input/ModifierInputComp/FancyModifierInput.tsx @@ -5,11 +5,10 @@ import { DefaultMinMaxValues, } from "../StandardLayoutInput/MinMaxNumberInput"; import { useGraphInputStore } from "../../../store/GraphInputStore"; -import { ModifierLimitationState } from "../../../store/StateInterface"; +import { WantedModifierExtended } from "../../../store/StateInterface"; type HandleChangeEventFunction = ( modifierId: number, - position: number, value: string | undefined, selectedModifierIndex: number, numericalType?: "min" | "max", @@ -20,9 +19,8 @@ export type TakingInputEventFunction = (orderIndex: number) => void; interface InputChangeHandler { modifierId: number; - position: number; selectedModifierIndex: number; - modifierLimitation: ModifierLimitationState | null | undefined; + currentRelevantModifierSpec: WantedModifierExtended; orderIndex: number; isNumerical: boolean; textRolls: string | null | undefined; @@ -31,18 +29,19 @@ interface InputChangeHandler { } const InputChangeHandler = (props: InputChangeHandler) => { + const modifierLimitations = + props.currentRelevantModifierSpec.modifierLimitations; const textRolls = props.textRolls; if (props.isNumerical) { const defaultMinMaxValues: DefaultMinMaxValues = { - max: props.modifierLimitation?.maxRoll ?? undefined, - min: props.modifierLimitation?.minRoll ?? undefined, + max: modifierLimitations?.maxRoll ?? undefined, + min: modifierLimitations?.minRoll ?? undefined, }; return ( props.handleAnyChange( props.modifierId, - props.position, value, props.selectedModifierIndex, numericalType, @@ -60,7 +59,7 @@ const InputChangeHandler = (props: InputChangeHandler) => { } else if (textRolls == null) { throw "'textRolls' cannot be undefined at the same time as 'isNumerical===false'"; } else { - const defaultTextIndex = props.modifierLimitation?.textRoll ?? undefined; + const defaultTextIndex = modifierLimitations?.textRoll ?? undefined; let defaultTextValue: string | undefined = undefined; if (defaultTextIndex !== undefined) { defaultTextValue = textRolls.split("|")[defaultTextIndex]; @@ -72,7 +71,6 @@ const InputChangeHandler = (props: InputChangeHandler) => { handleTextChange={(value) => props.handleAnyChange( props.modifierId, - props.position, value, props.selectedModifierIndex, undefined, @@ -90,34 +88,32 @@ const InputChangeHandler = (props: InputChangeHandler) => { }; interface DefaultOutputHandlerProps { - modifierLimitation: ModifierLimitationState | null | undefined; + currentRelevantModifierSpec: WantedModifierExtended; isNumerical: boolean; textRolls: string | null | undefined; } const DefaultOutputHandler = (props: DefaultOutputHandlerProps) => { - if (props.modifierLimitation == null) { + const modifierLimitations = + props.currentRelevantModifierSpec.modifierLimitations; + if (modifierLimitations == null) { return #; } if (props.isNumerical) { if ( - props.modifierLimitation.minRoll == null && - props.modifierLimitation.maxRoll == null + modifierLimitations.minRoll == null && + modifierLimitations.maxRoll == null ) { return #; } return ( - {props.modifierLimitation.minRoll - ? props.modifierLimitation.minRoll - : "Min"} + {modifierLimitations.minRoll ? modifierLimitations.minRoll : "Min"} - {props.modifierLimitation.maxRoll - ? props.modifierLimitation.maxRoll - : "Max"} + {modifierLimitations.maxRoll ? modifierLimitations.maxRoll : "Max"} ); @@ -126,8 +122,8 @@ const DefaultOutputHandler = (props: DefaultOutputHandlerProps) => { } else { return ( - {props.modifierLimitation.textRoll != null - ? props.textRolls.split("|")[props.modifierLimitation.textRoll] + {modifierLimitations.textRoll != null + ? props.textRolls.split("|")[modifierLimitations.textRoll] : "#"} ); @@ -137,7 +133,6 @@ const DefaultOutputHandler = (props: DefaultOutputHandlerProps) => { interface FancyModifierInputProps { currentlyTakingInput: boolean; modifierId: number; - position: number; selectedModifierIndex: number; textRolls: string | null | undefined; orderIndex: number; @@ -156,7 +151,6 @@ 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", @@ -168,19 +162,9 @@ export const FancyModifierInput = (props: FancyModifierInputProps) => { if (numericalType !== undefined) { const numValue = value ? Number(value) : undefined; if (numericalType === "min") { - setWantedModifierMinRoll( - modifierId, - position, - numValue, - selectedModifierIndex, - ); + setWantedModifierMinRoll(modifierId, numValue, selectedModifierIndex); } else { - setWantedModifierMaxRoll( - modifierId, - position, - numValue, - selectedModifierIndex, - ); + setWantedModifierMaxRoll(modifierId, numValue, selectedModifierIndex); } } else if (textRolls) { if (value === "Any") { @@ -188,12 +172,7 @@ export const FancyModifierInput = (props: FancyModifierInputProps) => { } const textValue = value !== undefined ? textRolls.split("|").indexOf(value) : undefined; - setWantedModifierTextRoll( - modifierId, - position, - textValue, - selectedModifierIndex, - ); + setWantedModifierTextRoll(modifierId, textValue, selectedModifierIndex); } else { throw "Modifier must have text rolls if the roll is not numerical."; } @@ -205,35 +184,21 @@ export const FancyModifierInput = (props: FancyModifierInputProps) => { (spec) => spec.index == props.selectedModifierIndex, ); - 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, - ); - } + const currentRelevantModifierSpec = + currentWantedModifierExtended[props.orderIndex]; // This happens when 'Clear Query' is pressed: // For a split second this element is rerendered, but there are no selected modifiers. - // which makes 'currentModifierLimitations' null | undefined - // if (currentModifierLimitations == null) { - // return; - // } + // which makes 'currentRelevantModifierSpec' null | undefined + if (currentRelevantModifierSpec == 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 950f57fa9..d99e11392 100644 --- a/src/frontend/src/components/Input/ModifierInputComp/FancySelectedModifier.tsx +++ b/src/frontend/src/components/Input/ModifierInputComp/FancySelectedModifier.tsx @@ -4,104 +4,109 @@ 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, - position: number, - value: string | undefined, - index_to_handle: number, - numericalType?: string, - textRolls?: string, + isNumerical: boolean, + modifierId: 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.modifierId; - const position = - selectedModifier.groupedModifierProperties.position[ - 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.groupedModifierProperties + .modifierId[labelIndex]; + return ( + + + {word} + + + + ); + } + }); + } + )} + + + ); }; diff --git a/src/frontend/src/components/Input/ModifierInputComp/ModifierInput.tsx b/src/frontend/src/components/Input/ModifierInputComp/ModifierInput.tsx index d0f24dfae..5ce714906 100644 --- a/src/frontend/src/components/Input/ModifierInputComp/ModifierInput.tsx +++ b/src/frontend/src/components/Input/ModifierInputComp/ModifierInput.tsx @@ -20,7 +20,6 @@ export interface ModifierOption extends SelectBoxOptionValue { index?: number; static?: boolean; relatedUniques?: string; - modifierId: number; groupedModifierProperties: GroupedModifierProperties; } @@ -42,7 +41,6 @@ export const ModifierInput = () => { regex: prefetchedModifier.regex, static: prefetchedModifier.static ?? undefined, relatedUniques: prefetchedModifier.relatedUniques ?? undefined, - modifierId: prefetchedModifier.modifierId, groupedModifierProperties: prefetchedModifier.groupedModifierProperties, })); const [choosableModifierOptions, setChoosableModifierOptions] = useState< @@ -61,8 +59,10 @@ export const ModifierInput = () => { if (wantedModifierExtended.length > 0) { prevSelectedModifiers = wantedModifierExtended.reduce( (selectedModifiers, wantedModifier) => { - const prevSelectedModifier = choosableModifierOptions.find( - (modifier) => modifier.modifierId === wantedModifier.modifierId, + const prevSelectedModifier = choosableModifierOptions.find((modifier) => + modifier.groupedModifierProperties.modifierId.includes( + wantedModifier.modifierId, + ), ); if (prevSelectedModifier === undefined) { return selectedModifiers; @@ -137,20 +137,28 @@ export const ModifierInput = () => { ]); removeWantedModifierExtended(overrideIndex); - addWantedModifierExtended( - { modifierId: newlySelectedModifier.modifierId }, - overrideIndex, - newlySelectedModifier.relatedUniques, + newlySelectedModifier.groupedModifierProperties.modifierId.map( + (modifierId) => { + addWantedModifierExtended( + { modifierId: modifierId }, + overrideIndex, + newlySelectedModifier.relatedUniques, + ); + }, ); } else { setSelectedModifiers((currentSelectedModifiers) => [ ...currentSelectedModifiers, { ...newlySelectedModifier, index: selectedModifiers.length }, ]); - addWantedModifierExtended( - { modifierId: newlySelectedModifier.modifierId }, - selectedModifiers.length, - newlySelectedModifier.relatedUniques, + newlySelectedModifier.groupedModifierProperties.modifierId.map( + (modifierId) => { + addWantedModifierExtended( + { modifierId: modifierId }, + selectedModifiers.length, + newlySelectedModifier.relatedUniques, + ); + }, ); } }; @@ -216,7 +224,9 @@ export const ModifierInput = () => { isChecked={selectedModifier.isSelected} key={selectedIndex} onChange={() => { - if (selectedModifier.modifierId !== null) { + if ( + selectedModifier.groupedModifierProperties.modifierId[0] !== null + ) { handleCheckboxChange(selectedModifier, selectedIndex); } }} diff --git a/src/frontend/src/hooks/graphing/utils.tsx b/src/frontend/src/hooks/graphing/utils.tsx index aa08cfd04..503475f62 100644 --- a/src/frontend/src/hooks/graphing/utils.tsx +++ b/src/frontend/src/hooks/graphing/utils.tsx @@ -1,10 +1,9 @@ -import { PlotQuery } from "../../client"; +import { PlotQuery, WantedModifier } from "../../client"; import { useErrorStore } from "../../store/ErrorStore"; import { useGraphInputStore } from "../../store/GraphInputStore"; import { BaseSpecState, - GraphInputState, - ModifierLimitationState, + WantedModifierExtended, } from "../../store/StateInterface"; import { LEAGUE_LAUNCH_TIME, PLOTTING_WINDOW_HOURS } from "../../config"; @@ -18,7 +17,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 ); }; @@ -41,117 +40,11 @@ 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; }; -const removeNumericalRoll = ( - modifierLimitations: ModifierLimitationState[] | null | undefined, - position: number, - rollType: "min" | "max", -) => { - if (modifierLimitations == null) { - return false; - } - for (let i = 0; i < modifierLimitations.length; i++) { - const limitation = modifierLimitations[i]; - if (limitation.position === position) { - const minRoll = limitation.minRoll; - const maxRoll = limitation.maxRoll; - // checks if all rolls will be gone after deletion - if ( - (!minRoll || rollType === "min") && - (!maxRoll || rollType === "max") - ) { - delete modifierLimitations[i]; - if (modifierLimitations.length === 1) { - return true; - } - } else { - delete limitation[rollType === "min" ? "minRoll" : "maxRoll"]; - } - return false; - } - } -}; - -const addNumericalRoll = ( - modifierLimitations: ModifierLimitationState[] | null | undefined, - position: number, - roll: number, - rollType: "min" | "max", -) => { - let modifierLimitation: ModifierLimitationState; - if (!modifierLimitations) { - modifierLimitations = []; - modifierLimitation = { - position: position, - }; - modifierLimitations.push(modifierLimitation); - } else { - const idx = modifierLimitations.findIndex( - (modifierLimitation) => modifierLimitation.position === position, - ); - if (idx === -1) { - modifierLimitation = { - position: position, - }; - modifierLimitations.push(modifierLimitation); - } else { - modifierLimitation = modifierLimitations[idx]; - } - } - modifierLimitation[rollType === "min" ? "minRoll" : "maxRoll"] = roll; - return modifierLimitations; -}; - -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 - ) { - // roll is undefined => delete roll - if (roll === undefined) { - const noMoreLimitations = removeNumericalRoll( - wantedModifierExtended.modifierLimitations, - position, - rollType, - ); - if (noMoreLimitations) { - return { - ...wantedModifierExtended, - modifierLimitations: null, - }; - } - } else { - const updatedModifierLimitations = addNumericalRoll( - wantedModifierExtended.modifierLimitations, - position, - roll, - rollType, - ); - return { - ...wantedModifierExtended, - modifierLimitations: updatedModifierLimitations, - }; - } - } - 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 @@ -170,10 +63,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); @@ -232,12 +125,32 @@ export const getOptimizedPlotQuery = (): PlotQuery | undefined => { } itemSpec = { ...itemSpec, name: possibleUniques.join("|") }; - const wantedModifier = state.wantedModifierExtended + const wantedModifier: WantedModifier[][] = state.wantedModifierExtended .filter((wantedModifier) => wantedModifier.isSelected) - .map((wantedMoidifierExtended) => ({ - modifierId: wantedMoidifierExtended.modifierId, - modifierLimitations: wantedMoidifierExtended.modifierLimitations, - })); + .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, + })) + ); const currentTime = new Date(); const end = getHoursSinceLaunch(currentTime); const window = PLOTTING_WINDOW_HOURS; diff --git a/src/frontend/src/store/GraphInputStore.tsx b/src/frontend/src/store/GraphInputStore.tsx index fad2acf9c..649a65849 100644 --- a/src/frontend/src/store/GraphInputStore.tsx +++ b/src/frontend/src/store/GraphInputStore.tsx @@ -12,7 +12,6 @@ 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) => ({ @@ -177,20 +176,19 @@ 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 })), @@ -417,29 +415,43 @@ export const useGraphInputStore = create((set) => ({ setWantedModifierMinRoll: ( modifierId: number, - position: number, minRoll: number | undefined, index: number, ) => - set((state) => - updateNumericalRoll(state, modifierId, position, minRoll, "min", index), - ), + set((state) => { + const updatedModifiersExtended = state.wantedModifierExtended.map( + (wantedModifierExtended) => { + if ( + wantedModifierExtended.modifierId === modifierId && + wantedModifierExtended.index === index + ) { + if (minRoll === undefined) { + if (!wantedModifierExtended.modifierLimitations?.maxRoll) { + delete wantedModifierExtended["modifierLimitations"]; + } else { + delete wantedModifierExtended.modifierLimitations["minRoll"]; + } + return wantedModifierExtended; + } + return { + ...wantedModifierExtended, + modifierLimitations: { + ...wantedModifierExtended.modifierLimitations, + minRoll: minRoll, + }, + }; + } else { + return wantedModifierExtended; + } + }, + ); + return { wantedModifierExtended: updatedModifiersExtended }; + }), setWantedModifierMaxRoll: ( modifierId: number, - position: number, maxRoll: number | undefined, index: number, - ) => - set((state) => - updateNumericalRoll(state, modifierId, position, maxRoll, "max", index), - ), - - setWantedModifierTextRoll: ( - modifierId: number, - position: number, - textRoll: number | undefined, - index: number, ) => set((state) => { const updatedModifiersExtended = state.wantedModifierExtended.map( @@ -448,38 +460,20 @@ export const useGraphInputStore = create((set) => ({ wantedModifierExtended.modifierId === modifierId && wantedModifierExtended.index === index ) { - let updatedModifierLimitations = - wantedModifierExtended.modifierLimitations; - // textRoll === undefined => remove textRoll - if (updatedModifierLimitations == null) { - if (textRoll === undefined) { - return wantedModifierExtended; - } - 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); + if (maxRoll === undefined) { + if (!wantedModifierExtended.modifierLimitations?.minRoll) { + delete wantedModifierExtended["modifierLimitations"]; } else { - updatedModifierLimitation.textRoll = textRoll; + delete wantedModifierExtended.modifierLimitations["maxRoll"]; } + return wantedModifierExtended; } return { ...wantedModifierExtended, - modifierLimitations: updatedModifierLimitations, + modifierLimitations: { + ...wantedModifierExtended.modifierLimitations, + maxRoll: maxRoll, + }, }; } else { return wantedModifierExtended; @@ -488,4 +482,29 @@ export const useGraphInputStore = create((set) => ({ ); return { wantedModifierExtended: updatedModifiersExtended }; }), + + setWantedModifierTextRoll: ( + modifierId: number, + textRoll: number | undefined, + index: number, + ) => + set((state) => { + const updatedModifiersExtended = state.wantedModifierExtended.map( + (wantedModifierExtended) => + wantedModifierExtended.modifierId === modifierId && + wantedModifierExtended.index === index + ? { + ...wantedModifierExtended, + modifierLimitations: + textRoll !== undefined + ? { + ...wantedModifierExtended.modifierLimitations, + textRoll: textRoll, + } + : undefined, + } + : wantedModifierExtended, + ); + return { wantedModifierExtended: updatedModifiersExtended }; + }), })); diff --git a/src/frontend/src/store/StateInterface.tsx b/src/frontend/src/store/StateInterface.tsx index 2d6c2543d..45d4195ea 100644 --- a/src/frontend/src/store/StateInterface.tsx +++ b/src/frontend/src/store/StateInterface.tsx @@ -45,7 +45,6 @@ export interface BaseSpecState { } export interface ModifierLimitationState { - position: number; minRoll?: number | null; maxRoll?: number | null; textRoll?: number | null; @@ -53,7 +52,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 { @@ -120,6 +119,7 @@ export interface GraphInputState { removeLeague: (league: string) => void; removeAllLeagues: () => void; + setItemName: (name: string | undefined) => void; setItemSpec: (itemSpec: ItemSpecState) => void; @@ -165,19 +165,16 @@ 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; @@ -198,14 +195,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; }