Facility: 085428

Tietan Storage Walla Walla

Stale Data Warning: This facility has not been successfully scraped in 30 days (threshold: 3 days). Data may be outdated.
Facility Information active
Facility ID
085428
Name
Tietan Storage Walla Walla
URL
https://www.tietanstoragewallawalla.com/#Units
Address
N/A
Platform
custom_facility_085428
Parser File
src/parsers/custom/facility_085428_parser.py
Last Scraped
2026-03-23 03:22:02.615820
Created
2026-03-06 23:45:35.865957
Updated
2026-03-23 03:22:02.623973
Parser & Healing Diagnosis working
Parser Status
✓ Working
Status Reason
N/A
Last Healing Attempt
Not attempted
Parser Source (src/parsers/custom/facility_085428_parser.py)
"""Parser for Tietan Storage Depot (Walla Walla, WA) facility.

This is a Webflow site that lists unit sizes and prices as card headings
in the format "5' x 10' = $65" inside `.various-section-card` elements.
"""

from __future__ import annotations

import re

from bs4 import BeautifulSoup

from src.parsers.base import BaseParser, ParseResult, UnitResult


class Facility085428Parser(BaseParser):
    """Extract storage units from Tietan Storage Depot.

    Units are displayed as cards with headings in the format:
        5' x 10' = $65
        10' x 10' = $85
        8' x 20' = $105
        10' x 22' = $125
    """

    platform = "custom_facility_085428"

    # Matches "5' x 10' = $65" or "10' x 22' = $125"
    _UNIT_RE = re.compile(
        r"(\d+(?:\.\d+)?)['\u2019\u2032]?\s*[xX\u00d7]\s*(\d+(?:\.\d+)?)['\u2019\u2032]?"
        r"\s*=\s*\$"
        r"([\d,]+(?:\.\d+)?)",
        re.IGNORECASE,
    )

    def parse(self, html: str, url: str = "") -> ParseResult:
        soup = BeautifulSoup(html, "lxml")
        result = ParseResult(platform=self.platform, parser_name=self.__class__.__name__)

        # Find all unit cards
        cards = soup.find_all(class_="various-section-card")

        if not cards:
            result.warnings.append("No .various-section-card elements found on page")
            return result

        for card in cards:
            # The heading contains the size and price, e.g. "5' x 10' = $65"
            heading = card.find(class_="various-card-head")
            if not heading:
                continue

            heading_text = heading.get_text(separator=" ", strip=True)
            match = self._UNIT_RE.search(heading_text)
            if not match:
                continue

            width = float(match.group(1))
            length = float(match.group(2))
            price_str = match.group(3).replace(",", "")
            price = float(price_str)

            # Get the description from the card paragraph
            paragraph = card.find(class_="various-card-paragraph")
            description = paragraph.get_text(strip=True) if paragraph else heading_text

            unit = UnitResult(
                size=self.normalize_size(f"{int(width)}x{int(length)}"),
                price=price,
                description=description,
                url=url,
                metadata={
                    "width": width,
                    "length": length,
                    "sqft": width * length,
                },
            )
            result.units.append(unit)

        if not result.units:
            result.warnings.append("Unit cards found but no size/price patterns matched")

        return result

Scrape Runs (5)

Run #96 Details

Status
exported
Parser Used
Facility085428Parser
Platform Detected
unknown
Units Found
4
Stage Reached
exported
Timestamp
2026-03-14 01:02:45.970551
Timing
Stage Duration
Fetch3359ms
Detect5ms
Parse3ms
Export16ms

Snapshot: 085428_20260314T010249Z.html · Show Snapshot · Open in New Tab

Parsed Units (4)

(5.0,10.0,50.0)

$65.00/mo

(10.0,10.0,100.0)

$85.00/mo

(8.0,20.0,160.0)

$105.00/mo

(10.0,22.0,220.0)

$125.00/mo

← Back to dashboard