Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions src/backend_api/app/alembic/replaceable_objects/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ def __init__(self, name, sqltext):


class ReplaceableTrigger(ReplaceableObject):
def __init__(self, name, table, function, trigger):
def __init__(self, name: str, table: str, function: str, trigger: str):
self.name = name
self.table = table
self.function = function
self.trigger = trigger
self.function = function.format(name=name)
self.trigger = trigger.format(name=name, table=table)


ObjectType = TypeVar("ObjectType", bound=ReplaceableObject)
Expand Down Expand Up @@ -89,22 +89,24 @@ def reverse(self):
@Operations.implementation_for(CreateViewOp)
def create_view(operations: Operations, operation: CreateViewOp):
operations.execute(
"CREATE VIEW %s AS %s" % (operation.target.name, operation.target.sqltext)
"CREATE VIEW {} AS {}".format(operation.target.name, operation.target.sqltext)
)


@Operations.implementation_for(DropViewOp)
def drop_view(operations: Operations, operation: DropViewOp):
operations.execute("DROP VIEW %s" % operation.target.name)
operations.execute("DROP VIEW {}".format(operation.target.name))


@Operations.implementation_for(CreateTriggerOp)
def create_trigger(operations: Operations, operation: CreateTriggerOp):
operations.execute(
"CREATE FUNCTION %s() %s" % (operation.target.name, operation.target.function)
"CREATE FUNCTION {}() {}".format(
operation.target.name, operation.target.function
)
)
operations.execute(
"CREATE TRIGGER %s %s" % (operation.target.name, operation.target.trigger)
"CREATE TRIGGER {} {}".format(operation.target.name, operation.target.trigger)
)


Expand All @@ -113,4 +115,4 @@ def drop_trigger(operations: Operations, operation: DropTriggerOp):
operations.execute(
"DROP TRIGGER {} ON {};".format(operation.target.name, operation.target.table)
)
operations.execute("DROP FUNCTION %s();" % operation.target.name)
operations.execute("DROP FUNCTION {}();".format(operation.target.name))
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
"""Removing modifier auto increment

Revision ID: 17daa1c96438
Revises: cc39d4eb113b
Create Date: 2026-05-09 16:05:14.813497

"""

from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa

from app.alembic.replaceable_objects.main import ReplaceableTrigger

# revision identifiers, used by Alembic.
revision: str = "17daa1c96438"
down_revision: Union[str, None] = "cc39d4eb113b"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

modifier_id_trigger = ReplaceableTrigger(
"increment_modifier_id",
"modifier",
"""
RETURNS trigger AS ${name}$
DECLARE
exists boolean;
BEGIN
exists := EXISTS(SELECT 1 FROM modifier WHERE "effect" = NEW.effect);
IF NOT exists THEN
NEW."modifierId" := nextval('modifier_id_seq');

ELSIF exists THEN
NEW."modifierId" := (SELECT "modifierId" FROM modifier WHERE "effect" = NEW.effect LIMIT 1);
END IF;

RETURN NEW;
END;
${name}$ LANGUAGE plpgsql;
""",
"""
BEFORE INSERT ON {table}
FOR EACH ROW
EXECUTE FUNCTION {name}();
""",
)


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###

# droping old item_modifier key
op.drop_constraint(
op.f("item_modifier_modifierId_fkey"), "item_modifier", type_="foreignkey"
)
op.drop_constraint(
op.f("item_modifier_modifierId_fkey1"), "item_modifier", type_="foreignkey"
)

# Dropping old modifier keys
op.f("""ALTER TABLE modifier ALTER COLUMN "modifierId" DROP IDENTITY;""")
op.drop_constraint(op.f("modifier_pkey"), "modifier", type_="primary")
op.drop_constraint(
op.f("modifier_modifierId_position_key"), "modifier", type_="unique"
)

# creating new keys
op.create_primary_key("modifier_pkey", "modifier", ["modifierId", "position"])
op.add_column(
"item_modifier", sa.Column("position", sa.SmallInteger(), nullable=False)
)
op.create_foreign_key(
"fk_item_modifier_modfierId_position",
"item_modifier",
"modifier",
["modifierId", "position"],
["modifierId", "position"],
ondelete="CASCADE",
onupdate="CASCADE",
)

op.execute("""
UPDATE modifier AS m
SET "modifierId" = "modifierId" + 10000;
""")

op.execute("""
WITH subquery as (SELECT
DENSE_RANK() OVER (
ORDER BY m.effect
) AS newModifierId,
m."position",
m."modifierId" AS modifierId
FROM modifier AS m
ORDER BY newModifierId, m.position)

UPDATE modifier AS m
SET "modifierId" = sq.newModifierId
FROM subquery AS sq
WHERE "modifierId" = sq.modifierId;
""")

op.execute("CREATE SEQUENCE modifier_id_seq;")
op.execute(
"""SELECT setval('modifier_id_seq', (SELECT MAX("modifierId") FROM modifier));"""
)

op.create_trigger(modifier_id_trigger)
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_trigger(modifier_id_trigger)

# dropping new modifier keys
op.drop_constraint(
op.f("fk_item_modifier_modfierId_position"), "item_modifier", type_="foreignkey"
)
op.drop_column("item_modifier", "position")
op.drop_constraint(op.f("modifier_pkey"), "modifier", type_="primary")

op.execute("""
WITH subquery as (SELECT
ROW_NUMBER() OVER(ORDER BY "modifierId", position ASC) AS newModifierId,
m."position",
m."modifierId" AS modifierId
FROM modifier AS m
ORDER BY newModifierId, m.position)

UPDATE modifier AS m
SET "modifierId" = sq.newModifierId
FROM subquery AS sq
WHERE "modifierId" = sq.modifierId
AND m.position = sq.position;
""")

op.execute("""
DROP SEQUENCE modifier_id_seq;
""")

# adding old keys
op.create_primary_key("modifier_pkey", "modifier", ["modifierId"])
op.create_unique_constraint(
op.f("modifier_modifierId_position_key"),
"modifier",
["modifierId", "position"],
postgresql_nulls_not_distinct=False,
)

op.create_foreign_key(
"item_modifier_modifierId_fkey",
"item_modifier",
"modifier",
["modifierId"],
["modifierId"],
)
op.create_foreign_key(
"item_modifier_modifierId_fkey1",
"item_modifier",
"modifier",
["modifierId"],
["modifierId"],
)

op.alter_column(
"modifier",
"modifierId",
existing_type=sa.SMALLINT(),
server_default=sa.Identity(
always=False,
start=1,
increment=1,
minvalue=1,
maxvalue=32767,
cycle=True,
cache=1,
),
existing_nullable=False,
)
op.execute(
"""SELECT setval(pg_get_serial_sequence('modifier', 'modifierId'), (SELECT MAX("modifierId") FROM modifier));"""
)
# ### end Alembic commands ###
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@

from app.alembic.replaceable_objects.main import ReplaceableTrigger


# revision identifiers, used by Alembic.
revision: str = "e38727349f3f"
down_revision: Union[str, None] = "0f3f15f56b7d"
Expand All @@ -25,7 +24,7 @@
"aggregate_unidentified",
"unidentified_item",
"""
RETURNS TRIGGER AS $aggregate_unidentified$
RETURNS TRIGGER AS ${name}$
DECLARE
current_hour INT;
divine_id INT;
Expand Down Expand Up @@ -65,12 +64,12 @@

RETURN NEW;
END;
$aggregate_unidentified$ LANGUAGE plpgsql;
${name}$ LANGUAGE plpgsql;
""",
"""
BEFORE INSERT ON unidentified_item
BEFORE INSERT ON {table}
FOR EACH ROW
EXECUTE FUNCTION aggregate_unidentified();
EXECUTE FUNCTION {name}();
""",
)

Expand Down
6 changes: 4 additions & 2 deletions src/backend_api/app/api/routes/modifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ async def create_modifier(
)
async def update_modifier(
modifierId: int,
position: int,
modifier_update: schemas.ModifierUpdate,
db: Session = Depends(get_db),
):
Expand All @@ -155,7 +156,7 @@ async def update_modifier(
Returns the updated modifier.
"""

modifier_map = {"modifierId": modifierId}
modifier_map = {"modifierId": modifierId, "position": position}

modifier = await CRUD_modifier.get(
db=db,
Expand All @@ -172,6 +173,7 @@ async def update_modifier(
)
async def delete_modifier(
modifierId: int,
position: int,
db: Session = Depends(get_db),
):
"""
Expand All @@ -181,7 +183,7 @@ async def delete_modifier(
Always deletes one modifier.
"""

modifier_map = {"modifierId": modifierId}
modifier_map = {"modifierId": modifierId, "position": position}
await CRUD_modifier.remove(db=db, filter=modifier_map)

return get_delete_return_msg(
Expand Down
26 changes: 14 additions & 12 deletions src/backend_api/app/core/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
DateTime,
Float,
ForeignKey,
ForeignKeyConstraint,
Identity,
Index,
Integer,
PrimaryKeyConstraint,
SmallInteger,
String,
Text,
UniqueConstraint,
func,
)
from sqlalchemy.dialects.postgresql import JSONB, UUID
Expand Down Expand Up @@ -144,11 +145,7 @@ class UnidentifiedItem(_ItemBase, Base):
class Modifier(Base):
__tablename__ = "modifier"

modifierId: Mapped[int] = mapped_column(
SmallInteger,
Identity(start=1, increment=1, cycle=True),
primary_key=True,
)
modifierId: Mapped[int] = mapped_column(SmallInteger, nullable=False)
position: Mapped[int] = mapped_column(SmallInteger, nullable=False)
minRoll: Mapped[float | None] = mapped_column(Float(4))
maxRoll: Mapped[float | None] = mapped_column(Float(4))
Expand All @@ -175,6 +172,7 @@ class Modifier(Base):
)

__table_args__ = (
PrimaryKeyConstraint("modifierId", "position"),
CheckConstraint(
"""
CASE
Expand Down Expand Up @@ -220,7 +218,6 @@ class Modifier(Base):
""" modifier."maxRoll" >= modifier."minRoll" """,
name="check_modifier_maxRoll_greaterThan_minRoll",
),
UniqueConstraint(modifierId, position),
)


Expand All @@ -232,11 +229,10 @@ class ItemModifier(Base):

modifierId: Mapped[int] = mapped_column(
SmallInteger,
ForeignKey(
"modifier.modifierId",
ondelete="CASCADE",
onupdate="CASCADE",
),
nullable=False,
)
position: Mapped[int] = mapped_column(
SmallInteger,
nullable=False,
)
createdHoursSinceLaunch: Mapped[int] = mapped_column(SmallInteger, nullable=False)
Expand All @@ -249,6 +245,12 @@ class ItemModifier(Base):
Float(4),
)
__table_args__ = (
ForeignKeyConstraint(
["modifierId", "position"],
["modifier.modifierId", "modifier.position"],
ondelete="CASCADE",
onupdate="CASCADE",
),
Index(
"ix_item_modifierId_createdHoursSinceLaunch_roll_itemId",
"modifierId",
Expand Down
1 change: 1 addition & 0 deletions src/backend_api/app/core/schemas/item_modifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class _BaseItemModifier(_pydantic.BaseModel):

itemId: int
modifierId: int
position: int
roll: float | None = None


Expand Down
3 changes: 2 additions & 1 deletion src/backend_api/app/core/schemas/modifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,12 @@ class _BaseModifier(_pydantic.BaseModel):


class GroupedModifierProperties(_pydantic.BaseModel):
modifierId: list[int]
position: list[int]
textRolls: list[str | None]


class GroupedModifierByEffect(_pydantic.BaseModel):
modifierId: int
effect: str
regex: str
static: bool | None
Expand Down
Loading
Loading