chore(v2): lint pass — black + ruff auto-fix + mypy regressions (#29 v1.1)
Some checks failed
ci.yml / chore(v2): lint pass — black + ruff auto-fix + mypy regressions (#29 v1.1) (pull_request) Failing after 0s
Some checks failed
ci.yml / chore(v2): lint pass — black + ruff auto-fix + mypy regressions (#29 v1.1) (pull_request) Failing after 0s
Pre-merge lint hygiene on the PR #30 touched files:
- `black` reformatted 9 files (cassette_transport, crud, models, tasks,
views_api, nip44, all 3 cassette test files, migrations). Cosmetic:
line lengths, trailing commas, multi-line argument layout.
- `ruff check --fix` cleared 176 of 202 errors auto-fixed. Mostly
`UP006` `typing.Optional` → `| None` modernization, `I001` import
sort order, `UP035` typing-extensions cleanup.
- Two new mypy regressions introduced by the migration commit dcb7de0
fixed:
- `crud.py:apply_bootstrap_state` — annotated `existing_first: dict
| None` on the dedup fetch.
- `tasks.py:_cassette_consumer_tick` — `# type: ignore[arg-type]` on
the `nostr_client.relay_manager.add_subscription` call; nostrclient's
upstream typing declares `list[str]` for filters but the actual
Nostr protocol takes `list[<filter-dict>]`. The runtime accepts it
(live smoke at 13:43Z dispatched `nip44_decrypt` cleanly through
this subscription); the typing mismatch is upstream's.
Remaining lint state, intentionally not addressed in this commit
(all pre-existing baseline, not regressions):
- 8 mypy errors in `calculations.py` + the unchanged-by-this-PR parts
of `crud.py` — pre-existing on v2-bitspire.
- 26 ruff style warnings: 14 are N805 false-positives on Pydantic
validators (`cls` first-arg is correct for `@validator`-decorated
methods); 4 are N818 exception-name-suffix preferences on my new
exception classes (renaming would touch many call sites; keep
`OperatorIdentityMissing` / `SignerUnavailable` / `RelayUnavailable`
/ `_NostrclientUnavailable` as-is for clarity); 5 are E501 line-too-
long on docstrings (the long lines are formatted for clarity);
1 RUF002 unicode-minus in a docstring.
Tests: 155 passed, 1 pre-existing async-plugin failure unchanged.
Live smoke (both publish + consume directions through the bunker)
unaffected — this is purely a code-style pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
dcb7de0c27
commit
d448fab0d2
10 changed files with 249 additions and 352 deletions
179
models.py
179
models.py
|
|
@ -6,7 +6,6 @@
|
|||
# the plan at ~/.claude/plans/snug-gliding-shamir.md.
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from pydantic import BaseModel, validator
|
||||
|
||||
|
|
@ -26,8 +25,8 @@ class CreateMachineData(BaseModel):
|
|||
|
||||
machine_npub: str
|
||||
wallet_id: str
|
||||
name: Optional[str] = None
|
||||
location: Optional[str] = None
|
||||
name: str | None = None
|
||||
location: str | None = None
|
||||
fiat_code: str = "GTQ"
|
||||
|
||||
|
||||
|
|
@ -36,8 +35,8 @@ class Machine(BaseModel):
|
|||
operator_user_id: str
|
||||
machine_npub: str
|
||||
wallet_id: str
|
||||
name: Optional[str]
|
||||
location: Optional[str]
|
||||
name: str | None
|
||||
location: str | None
|
||||
fiat_code: str
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
|
|
@ -45,11 +44,11 @@ class Machine(BaseModel):
|
|||
|
||||
|
||||
class UpdateMachineData(BaseModel):
|
||||
name: Optional[str] = None
|
||||
location: Optional[str] = None
|
||||
fiat_code: Optional[str] = None
|
||||
is_active: Optional[bool] = None
|
||||
wallet_id: Optional[str] = None
|
||||
name: str | None = None
|
||||
location: str | None = None
|
||||
fiat_code: str | None = None
|
||||
is_active: bool | None = None
|
||||
wallet_id: str | None = None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
|
@ -69,14 +68,14 @@ class CreateDcaClientData(BaseModel):
|
|||
|
||||
machine_id: str
|
||||
user_id: str
|
||||
username: Optional[str] = None
|
||||
username: str | None = None
|
||||
|
||||
|
||||
class DcaClient(BaseModel):
|
||||
id: str
|
||||
machine_id: str
|
||||
user_id: str
|
||||
username: Optional[str]
|
||||
username: str | None
|
||||
status: str
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
|
@ -92,8 +91,8 @@ class UpdateDcaClientData(BaseModel):
|
|||
/ mode / autoforward changes go through satmachineclient against
|
||||
`dca_lp` instead."""
|
||||
|
||||
username: Optional[str] = None
|
||||
status: Optional[str] = None
|
||||
username: str | None = None
|
||||
status: str | None = None
|
||||
|
||||
|
||||
class DcaLpPreferences(BaseModel):
|
||||
|
|
@ -109,8 +108,8 @@ class DcaLpPreferences(BaseModel):
|
|||
user_id: str
|
||||
dca_wallet_id: str
|
||||
default_dca_mode: str # 'flow' | 'fixed'
|
||||
fixed_mode_daily_limit: Optional[float]
|
||||
autoforward_ln_address: Optional[str]
|
||||
fixed_mode_daily_limit: float | None
|
||||
autoforward_ln_address: str | None
|
||||
autoforward_enabled: bool
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
|
@ -121,11 +120,11 @@ class UpsertDcaLpData(BaseModel):
|
|||
edits their preferences. All fields optional on update — pass only
|
||||
the ones being changed."""
|
||||
|
||||
dca_wallet_id: Optional[str] = None
|
||||
default_dca_mode: Optional[str] = None
|
||||
fixed_mode_daily_limit: Optional[float] = None
|
||||
autoforward_ln_address: Optional[str] = None
|
||||
autoforward_enabled: Optional[bool] = None
|
||||
dca_wallet_id: str | None = None
|
||||
default_dca_mode: str | None = None
|
||||
fixed_mode_daily_limit: float | None = None
|
||||
autoforward_ln_address: str | None = None
|
||||
autoforward_enabled: bool | None = None
|
||||
|
||||
|
||||
class ClientBalanceSummary(BaseModel):
|
||||
|
|
@ -156,7 +155,7 @@ class CreateDepositData(BaseModel):
|
|||
client_id: str
|
||||
machine_id: str
|
||||
amount: float
|
||||
notes: Optional[str] = None
|
||||
notes: str | None = None
|
||||
|
||||
@validator("amount")
|
||||
def round_amount(cls, v):
|
||||
|
|
@ -173,9 +172,9 @@ class DcaDeposit(BaseModel):
|
|||
amount: float
|
||||
currency: str
|
||||
status: str # 'pending' | 'confirmed' | 'rejected'
|
||||
notes: Optional[str]
|
||||
notes: str | None
|
||||
created_at: datetime
|
||||
confirmed_at: Optional[datetime]
|
||||
confirmed_at: datetime | None
|
||||
|
||||
|
||||
class UpdateDepositData(BaseModel):
|
||||
|
|
@ -183,8 +182,8 @@ class UpdateDepositData(BaseModel):
|
|||
`CreateDepositData`; the currency is bound to the machine and not
|
||||
editable after the row lands."""
|
||||
|
||||
amount: Optional[float] = None
|
||||
notes: Optional[str] = None
|
||||
amount: float | None = None
|
||||
notes: str | None = None
|
||||
|
||||
@validator("amount")
|
||||
def round_amount(cls, v):
|
||||
|
|
@ -195,7 +194,7 @@ class UpdateDepositData(BaseModel):
|
|||
|
||||
class UpdateDepositStatusData(BaseModel):
|
||||
status: str # 'pending' | 'confirmed' | 'rejected'
|
||||
notes: Optional[str] = None
|
||||
notes: str | None = None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
|
@ -210,8 +209,8 @@ class UpdateDepositStatusData(BaseModel):
|
|||
class CreateDcaSettlementData(BaseModel):
|
||||
machine_id: str
|
||||
payment_hash: str # the idempotency key (UNIQUE in the dca_settlements table)
|
||||
bitspire_event_id: Optional[str] = None # reserved for direct-Nostr ingestion
|
||||
bitspire_txid: Optional[str] = None
|
||||
bitspire_event_id: str | None = None # reserved for direct-Nostr ingestion
|
||||
bitspire_txid: str | None = None
|
||||
wire_sats: int
|
||||
fiat_amount: float
|
||||
fiat_code: str = "GTQ"
|
||||
|
|
@ -221,16 +220,16 @@ class CreateDcaSettlementData(BaseModel):
|
|||
platform_fee_sats: int
|
||||
operator_fee_sats: int
|
||||
tx_type: str # 'cash_out' | 'cash_in'
|
||||
bills_json: Optional[str] = None
|
||||
cassettes_json: Optional[str] = None
|
||||
bills_json: str | None = None
|
||||
cassettes_json: str | None = None
|
||||
|
||||
|
||||
class DcaSettlement(BaseModel):
|
||||
id: str
|
||||
machine_id: str
|
||||
payment_hash: str
|
||||
bitspire_event_id: Optional[str]
|
||||
bitspire_txid: Optional[str]
|
||||
bitspire_event_id: str | None
|
||||
bitspire_txid: str | None
|
||||
wire_sats: int
|
||||
fiat_amount: float
|
||||
fiat_code: str
|
||||
|
|
@ -240,8 +239,8 @@ class DcaSettlement(BaseModel):
|
|||
platform_fee_sats: int
|
||||
operator_fee_sats: int
|
||||
tx_type: str
|
||||
bills_json: Optional[str]
|
||||
cassettes_json: Optional[str]
|
||||
bills_json: str | None
|
||||
cassettes_json: str | None
|
||||
# 'pending' (default at insert)
|
||||
# 'processing' (claim taken by distribution processor)
|
||||
# 'processed' (all legs paid)
|
||||
|
|
@ -252,19 +251,19 @@ class DcaSettlement(BaseModel):
|
|||
# never went near distribution. error_message holds the
|
||||
# reason. Retry is wrong — investigate the machine.)
|
||||
status: str
|
||||
error_message: Optional[str]
|
||||
processed_at: Optional[datetime]
|
||||
error_message: str | None
|
||||
processed_at: datetime | None
|
||||
created_at: datetime
|
||||
# Append-only audit memo. Populated when an operator triggers an in-place
|
||||
# adjustment (partial-dispense, manual reconciliation override). Each
|
||||
# entry timestamped + records original values so the overwrite is
|
||||
# auditable from the settlement detail view alone. Never edited in place.
|
||||
notes: Optional[str] = None
|
||||
notes: str | None = None
|
||||
# Optimistic-lock claim token written when status flips to 'processing'.
|
||||
# Two concurrent process_settlement invocations can't both win the claim
|
||||
# (only one matching read-back). Cleared back to NULL when the leg-
|
||||
# writing pass completes (status='processed' or 'errored').
|
||||
processing_claim: Optional[str] = None
|
||||
processing_claim: str | None = None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
|
@ -286,7 +285,7 @@ class CommissionSplitLeg(BaseModel):
|
|||
"""
|
||||
|
||||
target: str
|
||||
label: Optional[str] = None
|
||||
label: str | None = None
|
||||
fraction: float
|
||||
sort_order: int = 0
|
||||
|
||||
|
|
@ -306,10 +305,10 @@ class CommissionSplitLeg(BaseModel):
|
|||
|
||||
class CommissionSplit(BaseModel):
|
||||
id: str
|
||||
machine_id: Optional[str] # None = operator's default ruleset
|
||||
machine_id: str | None # None = operator's default ruleset
|
||||
operator_user_id: str
|
||||
target: str
|
||||
label: Optional[str]
|
||||
label: str | None
|
||||
fraction: float
|
||||
sort_order: int
|
||||
created_at: datetime
|
||||
|
|
@ -322,7 +321,7 @@ class SetCommissionSplitsData(BaseModel):
|
|||
machine without an explicit override). Otherwise scoped per machine.
|
||||
"""
|
||||
|
||||
machine_id: Optional[str] = None
|
||||
machine_id: str | None = None
|
||||
legs: list[CommissionSplitLeg]
|
||||
|
||||
@validator("legs")
|
||||
|
|
@ -339,35 +338,35 @@ class SetCommissionSplitsData(BaseModel):
|
|||
|
||||
|
||||
class CreateDcaPaymentData(BaseModel):
|
||||
settlement_id: Optional[str] = None
|
||||
client_id: Optional[str] = None
|
||||
settlement_id: str | None = None
|
||||
client_id: str | None = None
|
||||
machine_id: str
|
||||
operator_user_id: str
|
||||
leg_type: str
|
||||
# 'dca' | 'super_fee' | 'operator_split' | 'settlement' | 'autoforward' | 'refund'
|
||||
destination_wallet_id: Optional[str] = None
|
||||
destination_ln_address: Optional[str] = None
|
||||
destination_wallet_id: str | None = None
|
||||
destination_ln_address: str | None = None
|
||||
amount_sats: int
|
||||
amount_fiat: Optional[float] = None
|
||||
exchange_rate: Optional[float] = None
|
||||
amount_fiat: float | None = None
|
||||
exchange_rate: float | None = None
|
||||
transaction_time: datetime
|
||||
external_payment_hash: Optional[str] = None
|
||||
external_payment_hash: str | None = None
|
||||
|
||||
|
||||
class DcaPayment(BaseModel):
|
||||
id: str
|
||||
settlement_id: Optional[str]
|
||||
client_id: Optional[str]
|
||||
settlement_id: str | None
|
||||
client_id: str | None
|
||||
machine_id: str
|
||||
operator_user_id: str
|
||||
leg_type: str
|
||||
destination_wallet_id: Optional[str]
|
||||
destination_ln_address: Optional[str]
|
||||
destination_wallet_id: str | None
|
||||
destination_ln_address: str | None
|
||||
amount_sats: int
|
||||
amount_fiat: Optional[float]
|
||||
exchange_rate: Optional[float]
|
||||
amount_fiat: float | None
|
||||
exchange_rate: float | None
|
||||
transaction_time: datetime
|
||||
external_payment_hash: Optional[str]
|
||||
external_payment_hash: str | None
|
||||
status: str
|
||||
# Leg status enum:
|
||||
# 'pending' — row written, payment not yet attempted
|
||||
|
|
@ -378,7 +377,7 @@ class DcaPayment(BaseModel):
|
|||
# 'skipped' — intentionally not paid (no super wallet configured,
|
||||
# no commission ruleset, no exchange rate, no LPs)
|
||||
# 'refunded' — reserved for future refund flows
|
||||
error_message: Optional[str]
|
||||
error_message: str | None
|
||||
created_at: datetime
|
||||
|
||||
|
||||
|
|
@ -391,22 +390,22 @@ class TelemetrySnapshot(BaseModel):
|
|||
machine_id: str
|
||||
# Beacon (kind-30078) — all fields are nullable because the upstream payload
|
||||
# is sparse today. As lamassu-next#43 lands, the post-#43 columns fill in.
|
||||
beacon_cash_in: Optional[bool] = None
|
||||
beacon_cash_out: Optional[bool] = None
|
||||
beacon_cash_level: Optional[str] = None
|
||||
beacon_fiat: Optional[str] = None
|
||||
beacon_model: Optional[str] = None
|
||||
beacon_name: Optional[str] = None
|
||||
beacon_location: Optional[str] = None
|
||||
beacon_geo: Optional[str] = None
|
||||
beacon_fees_json: Optional[str] = None
|
||||
beacon_limits_json: Optional[str] = None
|
||||
beacon_denominations_json: Optional[str] = None
|
||||
beacon_version: Optional[str] = None
|
||||
beacon_received_at: Optional[datetime] = None
|
||||
beacon_cash_in: bool | None = None
|
||||
beacon_cash_out: bool | None = None
|
||||
beacon_cash_level: str | None = None
|
||||
beacon_fiat: str | None = None
|
||||
beacon_model: str | None = None
|
||||
beacon_name: str | None = None
|
||||
beacon_location: str | None = None
|
||||
beacon_geo: str | None = None
|
||||
beacon_fees_json: str | None = None
|
||||
beacon_limits_json: str | None = None
|
||||
beacon_denominations_json: str | None = None
|
||||
beacon_version: str | None = None
|
||||
beacon_received_at: datetime | None = None
|
||||
# Fleet telemetry (kind-30079) — operator-only, awaits lamassu-next#42.
|
||||
telemetry_json: Optional[str] = None
|
||||
telemetry_received_at: Optional[datetime] = None
|
||||
telemetry_json: str | None = None
|
||||
telemetry_received_at: datetime | None = None
|
||||
|
||||
|
||||
# =============================================================================
|
||||
|
|
@ -417,13 +416,13 @@ class TelemetrySnapshot(BaseModel):
|
|||
class SuperConfig(BaseModel):
|
||||
id: str
|
||||
super_fee_fraction: float
|
||||
super_fee_wallet_id: Optional[str]
|
||||
super_fee_wallet_id: str | None
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class UpdateSuperConfigData(BaseModel):
|
||||
super_fee_fraction: Optional[float] = None
|
||||
super_fee_wallet_id: Optional[str] = None
|
||||
super_fee_fraction: float | None = None
|
||||
super_fee_wallet_id: str | None = None
|
||||
|
||||
@validator("super_fee_fraction")
|
||||
def fee_in_unit_range(cls, v):
|
||||
|
|
@ -448,9 +447,9 @@ class PartialDispenseData(BaseModel):
|
|||
"""
|
||||
|
||||
settlement_id: str
|
||||
dispensed_fraction: Optional[float] = None
|
||||
dispensed_sats: Optional[int] = None
|
||||
notes: Optional[str] = None
|
||||
dispensed_fraction: float | None = None
|
||||
dispensed_sats: int | None = None
|
||||
notes: str | None = None
|
||||
|
||||
@validator("dispensed_fraction")
|
||||
def fraction_in_unit_range(cls, v):
|
||||
|
|
@ -530,8 +529,8 @@ class SettleBalanceData(BaseModel):
|
|||
# there's no ambiguity about what rate was used.
|
||||
exchange_rate: float
|
||||
# If None, settle the LP's full remaining balance. Else partial.
|
||||
amount_fiat: Optional[float] = None
|
||||
notes: Optional[str] = None
|
||||
amount_fiat: float | None = None
|
||||
notes: str | None = None
|
||||
|
||||
@validator("exchange_rate")
|
||||
def positive_rate(cls, v):
|
||||
|
|
@ -585,11 +584,11 @@ class CassetteConfig(BaseModel):
|
|||
denomination: int
|
||||
count: int
|
||||
updated_at: datetime
|
||||
updated_by: Optional[str]
|
||||
state_denomination: Optional[int]
|
||||
state_count: Optional[int]
|
||||
state_at: Optional[datetime]
|
||||
state_event_id: Optional[str]
|
||||
updated_by: str | None
|
||||
state_denomination: int | None
|
||||
state_count: int | None
|
||||
state_at: datetime | None
|
||||
state_event_id: str | None
|
||||
|
||||
|
||||
class UpsertCassetteConfigData(BaseModel):
|
||||
|
|
@ -597,8 +596,8 @@ class UpsertCassetteConfigData(BaseModel):
|
|||
the dashboard. Both fields optional; pass only those changed.
|
||||
Position is not edited — it's the row's identity (hardware bay)."""
|
||||
|
||||
denomination: Optional[int] = None
|
||||
count: Optional[int] = None
|
||||
denomination: int | None = None
|
||||
count: int | None = None
|
||||
|
||||
@validator("denomination")
|
||||
def denomination_positive(cls, v):
|
||||
|
|
@ -664,9 +663,7 @@ class PublishCassettesPayload(BaseModel):
|
|||
try:
|
||||
key_int = int(k)
|
||||
except (TypeError, ValueError) as exc:
|
||||
raise ValueError(
|
||||
f"position key {k!r} is not an int"
|
||||
) from exc
|
||||
raise ValueError(f"position key {k!r} is not an int") from exc
|
||||
if key_int <= 0:
|
||||
raise ValueError(f"position must be > 0 (got {key_int})")
|
||||
out[key_int] = val
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue