diff --git a/alembic/versions/235d823a56ee_drop_objective_thread_pitch_from_scope_.py b/alembic/versions/235d823a56ee_drop_objective_thread_pitch_from_scope_.py new file mode 100644 index 0000000..bdb3578 --- /dev/null +++ b/alembic/versions/235d823a56ee_drop_objective_thread_pitch_from_scope_.py @@ -0,0 +1,29 @@ +"""drop_objective_thread_pitch_from_scope_infos + +Revision ID: 235d823a56ee +Revises: 04425ce3d4d5 +Create Date: 2026-08-17 15:18:26.763831 +""" + +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + +# revision identifiers, used by Alembic. +revision: str = "235d823a56ee" +down_revision: Union[str, None] = "04425ce3d4d5" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + with op.batch_alter_table("scope_infos", schema=None) as batch_op: + batch_op.drop_column("objective_thread_pitch") + + +def downgrade() -> None: + with op.batch_alter_table("scope_infos", schema=None) as batch_op: + batch_op.add_column( + sa.Column("objective_thread_pitch", sa.String(), nullable=True) + ) diff --git a/app/crud/scope_info.py b/app/crud/scope_info.py index 59b8112..72f22fd 100644 --- a/app/crud/scope_info.py +++ b/app/crud/scope_info.py @@ -37,7 +37,6 @@ async def create( magnification_max: float | None = None, model_suffix: str | None = None, objective_lens_diameter_mm: float | None = None, - objective_thread_pitch: str | None = None, ) -> ScopeInfo: info = ScopeInfo( augmented_part_id=augmented_part_id, @@ -45,7 +44,6 @@ async def create( magnification_max=magnification_max, model_suffix=model_suffix, objective_lens_diameter_mm=objective_lens_diameter_mm, - objective_thread_pitch=objective_thread_pitch, ) db.add(info) await db.commit() diff --git a/app/models/scope_info.py b/app/models/scope_info.py index 7167766..002bd87 100644 --- a/app/models/scope_info.py +++ b/app/models/scope_info.py @@ -32,8 +32,3 @@ class ScopeInfo(Base): objective_lens_diameter_mm: Mapped[float | None] = mapped_column( Float, nullable=True, default=None ) - # Kept as the raw string from the sheet (e.g. "0.75", "1/16") so no precision - # or notation is lost; the parsed thread lives on its own AttachmentPoint. - objective_thread_pitch: Mapped[str | None] = mapped_column( - String, nullable=True, default=None - ) diff --git a/app/schemas/scope_info.py b/app/schemas/scope_info.py index ffa359f..e055f07 100644 --- a/app/schemas/scope_info.py +++ b/app/schemas/scope_info.py @@ -6,7 +6,6 @@ class ScopeInfoBase(BaseModel): magnification_max: float | None = None model_suffix: str | None = None objective_lens_diameter_mm: float | None = None - objective_thread_pitch: str | None = None class ScopeInfoRead(ScopeInfoBase): diff --git a/app/services/scope_import.py b/app/services/scope_import.py index eeb94bf..0116711 100644 --- a/app/services/scope_import.py +++ b/app/services/scope_import.py @@ -15,7 +15,6 @@ sub_part_type → SubPartType (get-or-create under "Scope" product_number → AugmentedPart.sku (also the upsert / fit-lookup key) magnification_min / _max → ScopeInfo objective_lens_diameter_mm → ScopeInfo (descriptive only, no attachment point) -objective_thread_pitch → ScopeInfo ocular_outer_diameter_mm → StaticConnection + AttachmentPoint "Ocular" objective_thread → ISO/UNC thread + AttachmentPoint "Objective" ocular_fit → Fit: scope's Ocular AP ↔ referenced part's first AP @@ -75,7 +74,6 @@ EXPECTED_COLUMNS = [ "url", "ocular_outer_diameter_mm", "ocular_fit", - "objective_thread_pitch", "objective_thread", "objective_fit", ] @@ -321,7 +319,6 @@ async def import_scopes(db: AsyncSession, rows: list[dict]) -> ScopeImportResult magnification_max=_num(row.get("magnification_max")), model_suffix=model_suffix, objective_lens_diameter_mm=_num(row.get("objective_lens_diameter_mm")), - objective_thread_pitch=_text(row.get("objective_thread_pitch")), ) # Rebuild attachment points from scratch so re-imports stay idempotent diff --git a/app/templates/_scopes_table_card.html b/app/templates/_scopes_table_card.html index f49aebe..832cf9a 100644 --- a/app/templates/_scopes_table_card.html +++ b/app/templates/_scopes_table_card.html @@ -51,8 +51,6 @@ {% if row.objective_attachment_point %} {{ connection_spec(row.objective_attachment_point) }} - {% elif info and info.objective_thread_pitch %} - {{ info.objective_thread_pitch }} {% else %}—{% endif %} {% for ct in column_types %} diff --git a/app/templates/admin/augmented_part_detail.html b/app/templates/admin/augmented_part_detail.html index 6514756..419515c 100644 --- a/app/templates/admin/augmented_part_detail.html +++ b/app/templates/admin/augmented_part_detail.html @@ -50,7 +50,6 @@ {{ scope_info.objective_lens_diameter_mm }} mm {% else %}—{% endif %} -
Objective thread
{{ scope_info.objective_thread_pitch or "—" }}
{% endif %} diff --git a/tests/test_augmented_part_detail.py b/tests/test_augmented_part_detail.py index 9c93f9e..1cd05a1 100644 --- a/tests/test_augmented_part_detail.py +++ b/tests/test_augmented_part_detail.py @@ -38,7 +38,6 @@ async def _seed_part_with_fit(db): augmented_part_id=scope.id, magnification_min=3, magnification_max=18, - objective_thread_pitch="M52x0.75", ) ) ap_s = AttachmentPoint(augmented_part_id=scope.id, attachment_point_name_id=apn.id) @@ -80,7 +79,6 @@ async def test_detail_shows_attributes_and_fits_readonly( # Attributes assert "Alpha 3-18x50" in body assert "ALP-318" in body - assert "M52x0.75" in body # scope info assert "Objective" in body # attachment point # Fits are shown (the other part in the fit) assert "Flip cover 50mm" in body diff --git a/tests/test_scope_guide.py b/tests/test_scope_guide.py index 3a133df..a31af15 100644 --- a/tests/test_scope_guide.py +++ b/tests/test_scope_guide.py @@ -49,7 +49,6 @@ async def _seed_guide_data(db): magnification_min=3, magnification_max=18, objective_lens_diameter_mm=50, - objective_thread_pitch="M52x0.75", ) ) @@ -222,7 +221,6 @@ async def test_scopes_page_renders_rows_and_fits( assert "Alpha 3-18x50" in body # scope row assert "Flip cover 50mm" in body # a matching part in a fit column assert "fit-badge-exact" in body # the exact fit badge - assert "M52x0.75" in body # scope info: objective thread assert "3-18" in body # magnification range, integral values un-padded @@ -294,9 +292,7 @@ async def test_scopes_page_renders_objective_attachment_point_thread( resp = await client.get("/scopes") assert resp.status_code == 200 body = resp.text - # The attachment point's thread spec wins over ScopeInfo.objective_thread_pitch. assert "M44.0×0.5" in body - assert "M52x0.75" not in body @pytest.mark.asyncio diff --git a/tests/test_scope_import.py b/tests/test_scope_import.py index d758297..d0499bf 100644 --- a/tests/test_scope_import.py +++ b/tests/test_scope_import.py @@ -26,7 +26,6 @@ HEADERS = [ "url", "ocular_outer_diameter_mm", "ocular_fit", - "objective_thread_pitch", "objective_thread", "objective_fit", ] @@ -59,7 +58,6 @@ def _scope_row(**overrides): "url": "https://example.com/viper", "ocular_outer_diameter_mm": 40, "ocular_fit": None, - "objective_thread_pitch": "0.75", "objective_thread": "M40x0.75", "objective_fit": None, } @@ -142,7 +140,6 @@ async def test_import_creates_scope_with_info_and_attachment_points(db_sessionma assert info.magnification_max == 25 assert info.model_suffix == "PST" assert info.objective_lens_diameter_mm == 56 - assert info.objective_thread_pitch == "0.75" aps = await attachment_point_crud.list_for_augmented_part(db, part.id) by_label = {}