From 1d6830d769fa3f8547316fca1cc32aab50259497 Mon Sep 17 00:00:00 2001 From: Matt Gerega Date: Tue, 26 Mar 2024 13:55:44 -0400 Subject: [PATCH] Added initial_weight and empty_weight to spool --- client/src/pages/spools/create.tsx | 42 +++++++++++++++++ client/src/pages/spools/edit.tsx | 46 +++++++++++++++++++ ...4_03_26_0948-aafcd7fb0e84_spool_weights.py | 30 ++++++++++++ ...49-304a32906234_spool_weight_population.py | 46 +++++++++++++++++++ spoolman/api/v1/spool.py | 12 +++++ spoolman/database/models.py | 2 + spoolman/database/spool.py | 36 ++++++++++++--- 7 files changed, 207 insertions(+), 7 deletions(-) create mode 100644 migrations/versions/2024_03_26_0948-aafcd7fb0e84_spool_weights.py create mode 100644 migrations/versions/2024_03_26_1349-304a32906234_spool_weight_population.py diff --git a/client/src/pages/spools/create.tsx b/client/src/pages/spools/create.tsx index 5381897..deeaf4e 100644 --- a/client/src/pages/spools/create.tsx +++ b/client/src/pages/spools/create.tsx @@ -107,6 +107,9 @@ export const SpoolCreate: React.FC a.label.localeCompare(b.label, undefined, { sensitivity: "base" })); + const [defaultEmptySpoolWeight, setDefaultEmptySpoolWeight] = useState(0); + const [defaultInitialTotalWeight, setDefaultInitialTotalWeight] = useState(0); + const [weightToEnter, setWeightToEnter] = useState(1); const [usedWeight, setUsedWeight] = useState(0); @@ -124,6 +127,9 @@ export const SpoolCreate: React.FC= 3) { if (!(newFilamentWeight && newSpoolWeight)) { setWeightToEnter(2); @@ -254,6 +260,42 @@ export const SpoolCreate: React.FC + + + + + + + + diff --git a/client/src/pages/spools/edit.tsx b/client/src/pages/spools/edit.tsx index a592dfc..35b335b 100644 --- a/client/src/pages/spools/edit.tsx +++ b/client/src/pages/spools/edit.tsx @@ -56,6 +56,9 @@ export const SpoolEdit: React.FC = () => { formProps.initialValues = ParsedExtras(formProps.initialValues); } + const [defaultEmptySpoolWeight, setDefaultEmptySpoolWeight] = useState(0); + const [defaultInitialTotalWeight, setDefaultInitialTotalWeight] = useState(0); + // Override the form's onFinish method to stringify the extra fields const originalOnFinish = formProps.onFinish; formProps.onFinish = (allValues: ISpoolParsedExtras) => { @@ -107,6 +110,13 @@ export const SpoolEdit: React.FC = () => { const newSelectedFilament = filamentOptions?.find((obj) => { return obj.value === newID; }); + + const newFilamentWeight = newSelectedFilament?.weight || 0; + const newSpoolWeight = newSelectedFilament?.spool_weight || 0; + + setDefaultEmptySpoolWeight(newSpoolWeight); + setDefaultInitialTotalWeight(newFilamentWeight + newSpoolWeight); + const filamentHasWeight = newSelectedFilament?.weight || 0; const filamentHasSpoolWeight = newSelectedFilament?.spool_weight || 0; @@ -238,6 +248,42 @@ export const SpoolEdit: React.FC = () => { parser={numberParser} /> + + + + + + + + diff --git a/migrations/versions/2024_03_26_0948-aafcd7fb0e84_spool_weights.py b/migrations/versions/2024_03_26_0948-aafcd7fb0e84_spool_weights.py new file mode 100644 index 0000000..0d834be --- /dev/null +++ b/migrations/versions/2024_03_26_0948-aafcd7fb0e84_spool_weights.py @@ -0,0 +1,30 @@ +"""spool weights. + +Revision ID: aafcd7fb0e84 +Revises: b8881bdb716c +Create Date: 2024-03-26 09:48:09.930022 +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = 'aafcd7fb0e84' +down_revision = 'b8881bdb716c' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + """Perform the upgrade.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('spool', sa.Column('initial_weight', sa.Float(), nullable=True, comment="The initial total weight of the spool (gross weight).")) + op.add_column('spool', sa.Column('empty_weight', sa.Float(), nullable=True, comment="The weight of the empty spool (tare weight).")) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Perform the downgrade.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('spool', 'empty_weight') + op.drop_column('spool', 'initial_weight') + # ### end Alembic commands ### diff --git a/migrations/versions/2024_03_26_1349-304a32906234_spool_weight_population.py b/migrations/versions/2024_03_26_1349-304a32906234_spool_weight_population.py new file mode 100644 index 0000000..73861be --- /dev/null +++ b/migrations/versions/2024_03_26_1349-304a32906234_spool_weight_population.py @@ -0,0 +1,46 @@ +"""spool weight population. + +Revision ID: 304a32906234 +Revises: aafcd7fb0e84 +Create Date: 2024-03-26 13:49:26.594399 +""" +import sqlalchemy as sa +from alembic import op + +# revision identifiers, used by Alembic. +revision = '304a32906234' +down_revision = 'aafcd7fb0e84' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + filament = sa.Table('filament', + sa.MetaData(), + sa.Column('id', sa.Integer, primary_key=True), + sa.Column('weight', sa.Float(), nullable=True), + sa.Column('spool_weight', sa.Float(), nullable=True)) + + spool = sa.Table( + 'spool', + sa.MetaData(), + sa.Column('id', sa.Integer, primary_key=True), + sa.Column('filament_id', sa.Integer), + sa.Column('initial_weight', sa.Float(), nullable=False, comment="The initial total weight of the spool (gross weight)."), + sa.Column('empty_weight', sa.Float(), nullable=False, comment="The weight of the empty spool (tare weight).") + ) + + initial_weight = sa.select((filament.c.weight + filament.c.spool_weight).label("initial_weight")).where(filament.c.id == spool.c.filament_id).scalar_subquery() + empty_weight = sa.select(filament.c.spool_weight).where(filament.c.id == spool.c.filament_id).scalar_subquery() + + set_initial_weight = sa.update(spool).values(initial_weight=initial_weight) + op.execute(set_initial_weight) + + set_empty_weight = sa.update(spool).values(empty_weight=empty_weight) + op.execute(set_empty_weight) + pass + + +def downgrade() -> None: + """Perform the downgrade.""" + pass diff --git a/spoolman/api/v1/spool.py b/spoolman/api/v1/spool.py index 1391dfd..e701fca 100644 --- a/spoolman/api/v1/spool.py +++ b/spoolman/api/v1/spool.py @@ -40,6 +40,16 @@ class SpoolParameters(BaseModel): description="The price of this filament in the system configured currency.", example=20.0, ) + initial_weight: Optional[float] = Field( + ge=0, + description="The initial total weight of the spool.", + example=200, + ) + empty_weight: Optional[float] = Field( + ge=0, + description="The weight of an empty spool.", + example=200, + ) remaining_weight: Optional[float] = Field( ge=0, description=( @@ -354,6 +364,8 @@ async def create( # noqa: ANN201 db=db, filament_id=body.filament_id, price=body.price, + initial_weight=body.initial_weight, + empty_weight=body.empty_weight, remaining_weight=body.remaining_weight, used_weight=body.used_weight, first_used=body.first_used, diff --git a/spoolman/database/models.py b/spoolman/database/models.py index f06ac6f..b009bdf 100644 --- a/spoolman/database/models.py +++ b/spoolman/database/models.py @@ -64,6 +64,8 @@ class Spool(Base): price: Mapped[Optional[float]] = mapped_column() filament_id: Mapped[int] = mapped_column(ForeignKey("filament.id")) filament: Mapped["Filament"] = relationship(back_populates="spools") + initial_weight: Mapped[float] = mapped_column() + empty_weight: Mapped[float] = mapped_column() used_weight: Mapped[float] = mapped_column() location: Mapped[Optional[str]] = mapped_column(String(64)) lot_nr: Mapped[Optional[str]] = mapped_column(String(64)) diff --git a/spoolman/database/spool.py b/spoolman/database/spool.py index dfbaad8..ca384d8 100644 --- a/spoolman/database/spool.py +++ b/spoolman/database/spool.py @@ -35,6 +35,8 @@ async def create( db: AsyncSession, filament_id: int, remaining_weight: Optional[float] = None, + initial_weight: Optional[float] = None, + empty_weight: Optional[float] = None, used_weight: Optional[float] = None, first_used: Optional[datetime] = None, last_used: Optional[datetime] = None, @@ -47,11 +49,19 @@ async def create( ) -> models.Spool: """Add a new spool to the database. Leave weight empty to assume full spool.""" filament_item = await filament.get_by_id(db, filament_id) + + if empty_weight is None: + empty_weight = filament_item.spool_weight if filament_item.spool_weight is not None else 0 + + # Calculate initial_weight if not provided + if initial_weight is None: + initial_weight = (filament_item.weight if filament_item.weight is not None else 0) + empty_weight + if used_weight is None: if remaining_weight is not None: - if filament_item.weight is None: - raise ItemCreateError("remaining_weight can only be used if the filament type has a weight set.") - used_weight = max(filament_item.weight - remaining_weight, 0) + if initial_weight is None or initial_weight == 0: + raise ItemCreateError("remaining_weight can only be used if the initial_weight is defined.") + used_weight = max(initial_weight - empty_weight - remaining_weight, 0) else: used_weight = 0 @@ -64,6 +74,8 @@ async def create( spool = models.Spool( filament=filament_item, registered=datetime.utcnow().replace(microsecond=0), + initial_weight=initial_weight, + empty_weight=empty_weight, used_weight=used_weight, price=price, first_used=first_used, @@ -150,7 +162,7 @@ async def find( for fieldstr, order in sort_by.items(): sorts = [] if fieldstr in {"remaining_weight", "remaining_length"}: - sorts.append(models.Filament.weight - models.Spool.used_weight) + sorts.append(models.Spool.initial_weight - models.Spool.empty_weight - models.Spool.used_weight) elif fieldstr == "filament.combined_name": sorts.append(models.Vendor.name) sorts.append(models.Filament.name) @@ -181,10 +193,20 @@ async def update( for k, v in data.items(): if k == "filament_id": spool.filament = await filament.get_by_id(db, v) + # If there is no initial_weight, calculate it from the filament weight + if ( + spool.initial_weight is None + and spool.empty_weight is None + and spool.filament.weight is not None + ): + spool_weight = (spool.empty_weight if spool.empty_weight is not None else 0) + spool.initial_weight = spool.filament.weight + spool_weight + spool.empty_weight = spool_weight + elif k == "remaining_weight": - if spool.filament.weight is None: - raise ItemCreateError("remaining_weight can only be used if the filament type has a weight set.") - spool.used_weight = max(spool.filament.weight - v, 0) + if spool.initial_weight is None: + raise ItemCreateError("remaining_weight can only be used if initial_weight is set.") + spool.used_weight = max((spool.initial_weight - spool.empty_weight) - v, 0) elif isinstance(v, datetime): setattr(spool, k, utc_timezone_naive(v)) elif k == "extra":