style: format UI files and remove lint excludes

- Remove app/components/ and app/pages/ from ruff/black excludes
- Pre-commit reformatted multi-line strings for consistency
- All files now follow the same code style
This commit is contained in:
Bu5hm4nn
2026-04-01 13:55:55 +02:00
parent 9af654d9f2
commit 6bcf78e5df
9 changed files with 94 additions and 59 deletions

View File

@@ -13,9 +13,11 @@ def _ensure_lightweight_charts_assets() -> None:
global _CHARTS_SCRIPT_ADDED global _CHARTS_SCRIPT_ADDED
if _CHARTS_SCRIPT_ADDED: if _CHARTS_SCRIPT_ADDED:
return return
ui.add_head_html(""" ui.add_head_html(
"""
<script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script> <script src="https://unpkg.com/lightweight-charts/dist/lightweight-charts.standalone.production.js"></script>
""") """
)
_CHARTS_SCRIPT_ADDED = True _CHARTS_SCRIPT_ADDED = True
@@ -46,7 +48,8 @@ class CandlestickChart:
self._initialize_chart() self._initialize_chart()
def _initialize_chart(self) -> None: def _initialize_chart(self) -> None:
ui.run_javascript(f""" ui.run_javascript(
f"""
(function() {{ (function() {{
const root = document.getElementById({json.dumps(self.chart_id)}); const root = document.getElementById({json.dumps(self.chart_id)});
if (!root || typeof LightweightCharts === 'undefined') return; if (!root || typeof LightweightCharts === 'undefined') return;
@@ -91,48 +94,57 @@ class CandlestickChart:
indicators: {{}}, indicators: {{}},
}}; }};
}})(); }})();
""") """
)
def set_candles(self, candles: list[dict[str, Any]]) -> None: def set_candles(self, candles: list[dict[str, Any]]) -> None:
payload = json.dumps(candles) payload = json.dumps(candles)
ui.run_javascript(f""" ui.run_javascript(
f"""
(function() {{ (function() {{
const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}]; const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}];
if (!ref) return; if (!ref) return;
ref.candleSeries.setData({payload}); ref.candleSeries.setData({payload});
ref.chart.timeScale().fitContent(); ref.chart.timeScale().fitContent();
}})(); }})();
""") """
)
def update_price(self, candle: dict[str, Any]) -> None: def update_price(self, candle: dict[str, Any]) -> None:
payload = json.dumps(candle) payload = json.dumps(candle)
ui.run_javascript(f""" ui.run_javascript(
f"""
(function() {{ (function() {{
const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}]; const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}];
if (!ref) return; if (!ref) return;
ref.candleSeries.update({payload}); ref.candleSeries.update({payload});
}})(); }})();
""") """
)
def set_volume(self, volume_points: list[dict[str, Any]]) -> None: def set_volume(self, volume_points: list[dict[str, Any]]) -> None:
payload = json.dumps(volume_points) payload = json.dumps(volume_points)
ui.run_javascript(f""" ui.run_javascript(
f"""
(function() {{ (function() {{
const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}]; const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}];
if (!ref) return; if (!ref) return;
ref.volumeSeries.setData({payload}); ref.volumeSeries.setData({payload});
}})(); }})();
""") """
)
def update_volume(self, volume_point: dict[str, Any]) -> None: def update_volume(self, volume_point: dict[str, Any]) -> None:
payload = json.dumps(volume_point) payload = json.dumps(volume_point)
ui.run_javascript(f""" ui.run_javascript(
f"""
(function() {{ (function() {{
const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}]; const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}];
if (!ref) return; if (!ref) return;
ref.volumeSeries.update({payload}); ref.volumeSeries.update({payload});
}})(); }})();
""") """
)
def set_indicator( def set_indicator(
self, self,
@@ -144,7 +156,8 @@ class CandlestickChart:
) -> None: ) -> None:
key = json.dumps(name) key = json.dumps(name)
payload = json.dumps(points) payload = json.dumps(points)
ui.run_javascript(f""" ui.run_javascript(
f"""
(function() {{ (function() {{
const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}]; const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}];
if (!ref) return; if (!ref) return;
@@ -158,16 +171,19 @@ class CandlestickChart:
}} }}
ref.indicators[{key}].setData({payload}); ref.indicators[{key}].setData({payload});
}})(); }})();
""") """
)
def update_indicator(self, name: str, point: dict[str, Any]) -> None: def update_indicator(self, name: str, point: dict[str, Any]) -> None:
key = json.dumps(name) key = json.dumps(name)
payload = json.dumps(point) payload = json.dumps(point)
ui.run_javascript(f""" ui.run_javascript(
f"""
(function() {{ (function() {{
const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}]; const ref = window.vaultDashCharts?.[{json.dumps(self.chart_id)}];
const series = ref?.indicators?.[{key}]; const series = ref?.indicators?.[{key}];
if (!series) return; if (!series) return;
series.update({payload}); series.update({payload});
}})(); }})();
""") """
)

View File

@@ -117,7 +117,8 @@ class StrategyComparisonPanel:
scenario_class = ( scenario_class = (
"text-emerald-600 dark:text-emerald-400" if scenario >= 0 else "text-rose-600 dark:text-rose-400" "text-emerald-600 dark:text-emerald-400" if scenario >= 0 else "text-rose-600 dark:text-rose-400"
) )
rows.append(f""" rows.append(
f"""
<tr class=\"border-b border-slate-200 dark:border-slate-800\"> <tr class=\"border-b border-slate-200 dark:border-slate-800\">
<td class=\"px-4 py-3 font-medium text-slate-900 dark:text-slate-100\">{name}</td> <td class=\"px-4 py-3 font-medium text-slate-900 dark:text-slate-100\">{name}</td>
<td class=\"px-4 py-3 text-slate-600 dark:text-slate-300\">${cost:,.2f}</td> <td class=\"px-4 py-3 text-slate-600 dark:text-slate-300\">${cost:,.2f}</td>
@@ -125,7 +126,8 @@ class StrategyComparisonPanel:
<td class=\"px-4 py-3 text-slate-600 dark:text-slate-300\">{self._format_cap(strategy)}</td> <td class=\"px-4 py-3 text-slate-600 dark:text-slate-300\">{self._format_cap(strategy)}</td>
<td class=\"px-4 py-3 font-semibold {scenario_class}\">${scenario:,.2f}</td> <td class=\"px-4 py-3 font-semibold {scenario_class}\">${scenario:,.2f}</td>
</tr> </tr>
""") """
)
return f""" return f"""
<div class=\"overflow-x-auto\"> <div class=\"overflow-x-auto\">
<table class=\"min-w-full rounded-xl overflow-hidden\"> <table class=\"min-w-full rounded-xl overflow-hidden\">

View File

@@ -119,10 +119,12 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
ui.label( ui.label(
"Changing the preset resets strategy templates to that preset's default comparison set." "Changing the preset resets strategy templates to that preset's default comparison set."
).classes("text-xs text-slate-500 dark:text-slate-400") ).classes("text-xs text-slate-500 dark:text-slate-400")
ui.label( ui.label("Underlying units will be calculated from initial value ÷ entry spot.").classes(
"Underlying units will be calculated from initial value ÷ entry spot." "text-xs text-slate-500 dark:text-slate-400"
).classes("text-xs text-slate-500 dark:text-slate-400") )
initial_value_input = ui.number("Initial portfolio value ($)", value=default_units * default_entry_spot, min=0.01, step=1000).classes("w-full") initial_value_input = ui.number(
"Initial portfolio value ($)", value=default_units * default_entry_spot, min=0.01, step=1000
).classes("w-full")
loan_input = ui.number("Loan amount", value=default_loan, min=0, step=1000).classes("w-full") loan_input = ui.number("Loan amount", value=default_loan, min=0, step=1000).classes("w-full")
ltv_input = ui.number( ltv_input = ui.number(
"Margin call LTV", "Margin call LTV",
@@ -183,7 +185,11 @@ def _render_event_comparison_page(workspace_id: str | None = None) -> None:
# Show validation errors (units_error takes priority, then entry_spot_error) # Show validation errors (units_error takes priority, then entry_spot_error)
display_error = units_error or entry_spot_error display_error = units_error or entry_spot_error
if display_error: if display_error:
tone_class = "text-rose-600 dark:text-rose-300" if "must be positive" in display_error else "text-amber-700 dark:text-amber-300" tone_class = (
"text-rose-600 dark:text-rose-300"
if "must be positive" in display_error
else "text-amber-700 dark:text-amber-300"
)
ui.label(display_error).classes(f"text-sm {tone_class}") ui.label(display_error).classes(f"text-sm {tone_class}")
def render_result_state(title: str, message: str, *, tone: str = "info") -> None: def render_result_state(title: str, message: str, *, tone: str = "info") -> None:

View File

@@ -88,9 +88,7 @@ async def _resolve_hedge_spot(workspace_id: str | None = None) -> tuple[dict[str
data_service = get_data_service() data_service = get_data_service()
underlying = config.underlying or "GLD" underlying = config.underlying or "GLD"
quote = await data_service.get_quote(underlying) quote = await data_service.get_quote(underlying)
spot, source, updated_at = resolve_portfolio_spot_from_quote( spot, source, updated_at = resolve_portfolio_spot_from_quote(config, quote, fallback_symbol=underlying)
config, quote, fallback_symbol=underlying
)
portfolio = portfolio_snapshot(config, runtime_spot_price=spot) portfolio = portfolio_snapshot(config, runtime_spot_price=spot)
return portfolio, source, updated_at return portfolio, source, updated_at
except Exception as exc: except Exception as exc:

View File

@@ -88,9 +88,7 @@ async def options_page() -> None:
def filtered_rows() -> list[dict[str, Any]]: def filtered_rows() -> list[dict[str, Any]]:
return [ return [
row row for row in chain_state["rows"] if strike_range["min"] <= float(row["strike"]) <= strike_range["max"]
for row in chain_state["rows"]
if strike_range["min"] <= float(row["strike"]) <= strike_range["max"]
] ]
def render_selection() -> None: def render_selection() -> None:
@@ -186,7 +184,9 @@ async def options_page() -> None:
next_chain = await data_service.get_options_chain_for_expiry("GLD", expiry) next_chain = await data_service.get_options_chain_for_expiry("GLD", expiry)
chain_state["data"] = next_chain chain_state["data"] = next_chain
chain_state["rows"] = list(next_chain.get("rows") or [*next_chain.get("calls", []), *next_chain.get("puts", [])]) chain_state["rows"] = list(
next_chain.get("rows") or [*next_chain.get("calls", []), *next_chain.get("puts", [])]
)
min_value, max_value = strike_bounds(chain_state["rows"]) min_value, max_value = strike_bounds(chain_state["rows"])
strike_range["min"] = min_value strike_range["min"] = min_value

View File

@@ -124,11 +124,13 @@ def welcome_page(request: Request):
if turnstile.uses_test_keys if turnstile.uses_test_keys
else "" else ""
) )
ui.html(f"""<form method="post" action="/workspaces/bootstrap" class="flex items-center gap-4"> ui.html(
f"""<form method="post" action="/workspaces/bootstrap" class="flex items-center gap-4">
{hidden_token} {hidden_token}
<div class="cf-turnstile" data-sitekey="{turnstile.site_key}"></div> <div class="cf-turnstile" data-sitekey="{turnstile.site_key}"></div>
<button type="submit" class="rounded-lg bg-slate-900 px-5 py-3 text-sm font-semibold text-white no-underline dark:bg-slate-100 dark:text-slate-900">Get started</button> <button type="submit" class="rounded-lg bg-slate-900 px-5 py-3 text-sm font-semibold text-white no-underline dark:bg-slate-100 dark:text-slate-900">Get started</button>
</form>""") </form>"""
)
ui.label("You can always create a fresh workspace later if a link is lost.").classes( ui.label("You can always create a fresh workspace later if a link is lost.").classes(
"text-sm text-slate-500 dark:text-slate-400" "text-sm text-slate-500 dark:text-slate-400"
) )
@@ -174,7 +176,11 @@ async def overview_page(workspace_id: str) -> None:
current_values[str(pos.id)] = pos.entry_value current_values[str(pos.id)] = pos.entry_value
total_annual_storage_cost = calculate_total_storage_cost(positions, current_values) total_annual_storage_cost = calculate_total_storage_cost(positions, current_values)
portfolio["annual_storage_cost"] = float(total_annual_storage_cost) portfolio["annual_storage_cost"] = float(total_annual_storage_cost)
portfolio["storage_cost_pct"] = (float(total_annual_storage_cost) / float(portfolio["gold_value"]) * 100) if portfolio["gold_value"] > 0 else 0.0 portfolio["storage_cost_pct"] = (
(float(total_annual_storage_cost) / float(portfolio["gold_value"]) * 100)
if portfolio["gold_value"] > 0
else 0.0
)
alert_status = AlertService().evaluate(config, portfolio) alert_status = AlertService().evaluate(config, portfolio)
ltv_history_service = LtvHistoryService(repository=LtvHistoryRepository(base_path=repo.base_path)) ltv_history_service = LtvHistoryService(repository=LtvHistoryRepository(base_path=repo.base_path))

View File

@@ -384,8 +384,12 @@ def settings_page(workspace_id: str) -> None:
).classes("w-full") ).classes("w-full")
ui.separator().classes("my-3") ui.separator().classes("my-3")
ui.label("Storage Costs (optional)").classes("text-sm font-semibold text-slate-700 dark:text-slate-300") ui.label("Storage Costs (optional)").classes(
ui.label("For physical gold (XAU), defaults to 0.12% annual vault storage.").classes("text-xs text-slate-500 dark:text-slate-400 mb-2") "text-sm font-semibold text-slate-700 dark:text-slate-300"
)
ui.label("For physical gold (XAU), defaults to 0.12% annual vault storage.").classes(
"text-xs text-slate-500 dark:text-slate-400 mb-2"
)
pos_storage_cost_basis = ui.number( pos_storage_cost_basis = ui.number(
"Storage cost (% per year or fixed $)", "Storage cost (% per year or fixed $)",
@@ -401,8 +405,12 @@ def settings_page(workspace_id: str) -> None:
).classes("w-full") ).classes("w-full")
ui.separator().classes("my-3") ui.separator().classes("my-3")
ui.label("Premium & Spread (optional)").classes("text-sm font-semibold text-slate-700 dark:text-slate-300") ui.label("Premium & Spread (optional)").classes(
ui.label("For physical gold, accounts for dealer markup and bid/ask spread.").classes("text-xs text-slate-500 dark:text-slate-400 mb-2") "text-sm font-semibold text-slate-700 dark:text-slate-300"
)
ui.label("For physical gold, accounts for dealer markup and bid/ask spread.").classes(
"text-xs text-slate-500 dark:text-slate-400 mb-2"
)
pos_purchase_premium = ui.number( pos_purchase_premium = ui.number(
"Purchase premium over spot (%)", "Purchase premium over spot (%)",
@@ -429,10 +437,14 @@ def settings_page(workspace_id: str) -> None:
try: try:
underlying = str(pos_underlying.value) underlying = str(pos_underlying.value)
storage_cost_basis_val = float(pos_storage_cost_basis.value) storage_cost_basis_val = float(pos_storage_cost_basis.value)
storage_cost_basis = Decimal(str(storage_cost_basis_val)) if storage_cost_basis_val > 0 else None storage_cost_basis = (
Decimal(str(storage_cost_basis_val)) if storage_cost_basis_val > 0 else None
)
storage_cost_period = str(pos_storage_cost_period.value) if storage_cost_basis else None storage_cost_period = str(pos_storage_cost_period.value) if storage_cost_basis else None
purchase_premium_val = float(pos_purchase_premium.value) purchase_premium_val = float(pos_purchase_premium.value)
purchase_premium = Decimal(str(purchase_premium_val / 100)) if purchase_premium_val > 0 else None purchase_premium = (
Decimal(str(purchase_premium_val / 100)) if purchase_premium_val > 0 else None
)
bid_ask_spread_val = float(pos_bid_ask_spread.value) bid_ask_spread_val = float(pos_bid_ask_spread.value)
bid_ask_spread = Decimal(str(bid_ask_spread_val / 100)) if bid_ask_spread_val > 0 else None bid_ask_spread = Decimal(str(bid_ask_spread_val / 100)) if bid_ask_spread_val > 0 else None

View File

@@ -6,7 +6,6 @@ requires-python = ">=3.11"
[tool.ruff] [tool.ruff]
line-length = 120 line-length = 120
exclude = ["app/components/*.py", "app/pages/*.py"]
[tool.ruff.lint] [tool.ruff.lint]
select = ["E4", "E7", "E9", "F", "I"] select = ["E4", "E7", "E9", "F", "I"]
@@ -14,12 +13,6 @@ select = ["E4", "E7", "E9", "F", "I"]
[tool.black] [tool.black]
line-length = 120 line-length = 120
target-version = ["py312"] target-version = ["py312"]
extend-exclude = '''
/(
app/components
| app/pages
)/
'''
[tool.mypy] [tool.mypy]
ignore_missing_imports = true ignore_missing_imports = true

View File

@@ -26,14 +26,16 @@ def test_overview_shows_ltv_history_and_exports_csv() -> None:
expect(page.locator("text=90 Day").first).to_be_visible(timeout=15000) expect(page.locator("text=90 Day").first).to_be_visible(timeout=15000)
expect(page.get_by_role("button", name="Export CSV")).to_be_visible(timeout=15000) expect(page.get_by_role("button", name="Export CSV")).to_be_visible(timeout=15000)
series_names = page.evaluate(""" series_names = page.evaluate(
"""
async () => { async () => {
const importMap = JSON.parse(document.querySelector('script[type="importmap"]').textContent).imports; const importMap = JSON.parse(document.querySelector('script[type="importmap"]').textContent).imports;
const mod = await import(importMap['nicegui-echart']); const mod = await import(importMap['nicegui-echart']);
const chart = mod.echarts.getInstanceByDom(document.querySelector('.nicegui-echart')); const chart = mod.echarts.getInstanceByDom(document.querySelector('.nicegui-echart'));
return chart ? chart.getOption().series.map(series => series.name) : []; return chart ? chart.getOption().series.map(series => series.name) : [];
} }
""") """
)
assert series_names == ["LTV", "Margin threshold"] assert series_names == ["LTV", "Margin threshold"]
with page.expect_download() as download_info: with page.expect_download() as download_info: