Demo
Source private · Open dashboard
Publish-to-web Power BI report.
Problem
Resale decisions need how often a model×size trades, at what price, and at what premium to retail. A few recent rows on a product page are not enough for seasonality or size gaps. Histories are long and easy to interrupt, so a one-shot scrape is not enough.
Approach
- Collect — Playwright attaches to logged-in Chrome; single / batch / queue modes collect trades by size into CSV
- Resume & update — incremental newer trades, resume from temp files, fill oldest-side gaps; a size is “done” near ~90% of expected page volume
- Clean — size match checks, relative→absolute dates, dedupe, master cache, split size keys
- Model — per-size CSV → model merge → cross-model
sales_fact→ Power BI star schema (fact + product / brand / date + what-if)
The dashboard is for which model×size to watch, avoid, or revisit — not a buy button.
Results
Sample scale: about 3M trades, ~912bn KRW sales, ~59% average premium.
- Sales concentration: Jordan → Nike → Adidas; Jordan is a clear premium outlier versus retail
- Premium also splits by size, delivery type, and collaboration
- Monitor: high volume + elevated premium with a clear size split
- Avoid / wait: thin history or premium that only looks good on a short window
- Revisit: weekday/weekend gaps or delivery-type shifts that change expected net
Limitations
- Source collector stays private; the public artifact is the Publish-to-web report (fully open, no auth)
- Coverage and freshness depend on which products were queued and when the last incremental run finished
- Premium and Bollinger-style signals are descriptive on historical trades — not a calibrated pricing or inventory model
- Publish-to-web is unsuitable for sensitive data; this demo uses an analysis model intended for portfolio viewing
Stack
Python · Playwright (Chrome attach) · CSV → sales_fact ETL · Power BI (star schema, DAX, what-if)
Notes
Report pages, collection ops, star schema, DAX
Report pages
| Page | Contents |
|---|---|
| Overview | Sales, quantity, average premium; sales by date and brand |
| Price trend | Daily average price by brand / product / size |
| Signals | Quantity vs price, Bollinger bands, period / profit-target what-if |
| Premium by brand | Brand premium, retail vs resale, weekday vs weekend |
| Size heatmap | Size × brand premium, trend by delivery type |
Collection steps: product/queue → model + expected volume per size → incomplete sizes only → CSV (size, price, date, delivery) → merge → sales fact → Power BI.
When something blocks: wait/reload/retry; re-login on session expiry; nudge stuck scrolls; drop wrong size/date rows; re-run size analysis; skip extremely high-volume sizes.
Star schema
| Table | Role |
|---|---|
| fact_sales | One trade (SKU, Size, Price, DeliveryType, DateKey …) |
| dim_product | Model, names, category, retail, release date |
| dim_brand | Brand |
| dim_date | Date |
| What-if | Analysis period, profit target, Sales/Qty, signal toggles |
%Average Premium =
DIVIDE (
[Daily Avg Price] - SELECTEDVALUE ( dim_product[Retail_Price] ),
SELECTEDVALUE ( dim_product[Retail_Price] )
)Attributes come through relationships; calculations live in measures. Signals combine fact measures with what-if period and profit target.