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
147
crud.py
147
crud.py
|
|
@ -6,7 +6,6 @@
|
|||
# machine model".
|
||||
|
||||
from datetime import datetime
|
||||
from typing import List, Optional
|
||||
|
||||
from lnbits.db import Database
|
||||
from lnbits.helpers import urlsafe_short_hash
|
||||
|
|
@ -47,7 +46,7 @@ db = Database("ext_satoshimachine")
|
|||
# =============================================================================
|
||||
|
||||
|
||||
async def get_super_config() -> Optional[SuperConfig]:
|
||||
async def get_super_config() -> SuperConfig | None:
|
||||
return await db.fetchone(
|
||||
"SELECT * FROM satoshimachine.super_config WHERE id = :id",
|
||||
{"id": "default"},
|
||||
|
|
@ -55,7 +54,7 @@ async def get_super_config() -> Optional[SuperConfig]:
|
|||
)
|
||||
|
||||
|
||||
async def update_super_config(data: UpdateSuperConfigData) -> Optional[SuperConfig]:
|
||||
async def update_super_config(data: UpdateSuperConfigData) -> SuperConfig | None:
|
||||
update_data = {k: v for k, v in data.dict().items() if v is not None}
|
||||
if not update_data:
|
||||
return await get_super_config()
|
||||
|
|
@ -103,7 +102,7 @@ async def create_machine(operator_user_id: str, data: CreateMachineData) -> Mach
|
|||
return machine
|
||||
|
||||
|
||||
async def get_machine(machine_id: str) -> Optional[Machine]:
|
||||
async def get_machine(machine_id: str) -> Machine | None:
|
||||
return await db.fetchone(
|
||||
"SELECT * FROM satoshimachine.dca_machines WHERE id = :id",
|
||||
{"id": machine_id},
|
||||
|
|
@ -111,7 +110,7 @@ async def get_machine(machine_id: str) -> Optional[Machine]:
|
|||
)
|
||||
|
||||
|
||||
async def get_machine_by_npub(machine_npub: str) -> Optional[Machine]:
|
||||
async def get_machine_by_npub(machine_npub: str) -> Machine | None:
|
||||
return await db.fetchone(
|
||||
"SELECT * FROM satoshimachine.dca_machines WHERE machine_npub = :npub",
|
||||
{"npub": machine_npub},
|
||||
|
|
@ -119,7 +118,7 @@ async def get_machine_by_npub(machine_npub: str) -> Optional[Machine]:
|
|||
)
|
||||
|
||||
|
||||
async def get_active_machine_by_wallet_id(wallet_id: str) -> Optional[Machine]:
|
||||
async def get_active_machine_by_wallet_id(wallet_id: str) -> Machine | None:
|
||||
"""Used by the invoice listener to route an incoming payment to a machine."""
|
||||
return await db.fetchone(
|
||||
"""
|
||||
|
|
@ -132,7 +131,7 @@ async def get_active_machine_by_wallet_id(wallet_id: str) -> Optional[Machine]:
|
|||
)
|
||||
|
||||
|
||||
async def get_machines_for_operator(operator_user_id: str) -> List[Machine]:
|
||||
async def get_machines_for_operator(operator_user_id: str) -> list[Machine]:
|
||||
return await db.fetchall(
|
||||
"""
|
||||
SELECT * FROM satoshimachine.dca_machines
|
||||
|
|
@ -144,7 +143,7 @@ async def get_machines_for_operator(operator_user_id: str) -> List[Machine]:
|
|||
)
|
||||
|
||||
|
||||
async def list_all_active_machines() -> List[Machine]:
|
||||
async def list_all_active_machines() -> list[Machine]:
|
||||
"""Used by the cassette bootstrap consumer task to build a single
|
||||
cross-operator subscription filter. Each event's pubkey routes to
|
||||
the right operator via get_machine_by_atm_pubkey_hex + the machine's
|
||||
|
|
@ -161,7 +160,7 @@ async def list_all_active_machines() -> List[Machine]:
|
|||
)
|
||||
|
||||
|
||||
async def get_machine_by_atm_pubkey_hex(atm_pubkey_hex: str) -> Optional[Machine]:
|
||||
async def get_machine_by_atm_pubkey_hex(atm_pubkey_hex: str) -> Machine | None:
|
||||
"""Look up an active machine by its ATM pubkey, accepting hex or bech32
|
||||
in machine_npub. Used by the cassette bootstrap consumer to route an
|
||||
incoming state event to the right machine row (and therefore operator
|
||||
|
|
@ -183,7 +182,7 @@ async def get_machine_by_atm_pubkey_hex(atm_pubkey_hex: str) -> Optional[Machine
|
|||
return None
|
||||
|
||||
|
||||
async def update_machine(machine_id: str, data: UpdateMachineData) -> Optional[Machine]:
|
||||
async def update_machine(machine_id: str, data: UpdateMachineData) -> Machine | None:
|
||||
update_data = {k: v for k, v in data.dict().items() if v is not None}
|
||||
if not update_data:
|
||||
return await get_machine(machine_id)
|
||||
|
|
@ -255,7 +254,7 @@ _CLIENT_FROM = (
|
|||
)
|
||||
|
||||
|
||||
async def get_dca_client(client_id: str) -> Optional[DcaClient]:
|
||||
async def get_dca_client(client_id: str) -> DcaClient | None:
|
||||
return await db.fetchone(
|
||||
f"SELECT {_CLIENT_SELECT} FROM {_CLIENT_FROM} WHERE c.id = :id",
|
||||
{"id": client_id},
|
||||
|
|
@ -265,7 +264,7 @@ async def get_dca_client(client_id: str) -> Optional[DcaClient]:
|
|||
|
||||
async def get_dca_client_for_machine_user(
|
||||
machine_id: str, user_id: str
|
||||
) -> Optional[DcaClient]:
|
||||
) -> DcaClient | None:
|
||||
return await db.fetchone(
|
||||
f"""
|
||||
SELECT {_CLIENT_SELECT} FROM {_CLIENT_FROM}
|
||||
|
|
@ -276,7 +275,7 @@ async def get_dca_client_for_machine_user(
|
|||
)
|
||||
|
||||
|
||||
async def get_dca_clients_for_machine(machine_id: str) -> List[DcaClient]:
|
||||
async def get_dca_clients_for_machine(machine_id: str) -> list[DcaClient]:
|
||||
return await db.fetchall(
|
||||
f"""
|
||||
SELECT {_CLIENT_SELECT} FROM {_CLIENT_FROM}
|
||||
|
|
@ -288,7 +287,7 @@ async def get_dca_clients_for_machine(machine_id: str) -> List[DcaClient]:
|
|||
)
|
||||
|
||||
|
||||
async def get_dca_clients_for_operator(operator_user_id: str) -> List[DcaClient]:
|
||||
async def get_dca_clients_for_operator(operator_user_id: str) -> list[DcaClient]:
|
||||
"""All clients across every machine this operator owns."""
|
||||
return await db.fetchall(
|
||||
f"""
|
||||
|
|
@ -303,7 +302,7 @@ async def get_dca_clients_for_operator(operator_user_id: str) -> List[DcaClient]
|
|||
)
|
||||
|
||||
|
||||
async def get_dca_clients_for_user(user_id: str) -> List[DcaClient]:
|
||||
async def get_dca_clients_for_user(user_id: str) -> list[DcaClient]:
|
||||
"""LP cross-operator view — every machine this LP is registered at."""
|
||||
return await db.fetchall(
|
||||
f"""
|
||||
|
|
@ -316,7 +315,7 @@ async def get_dca_clients_for_user(user_id: str) -> List[DcaClient]:
|
|||
)
|
||||
|
||||
|
||||
async def get_flow_mode_clients_for_machine(machine_id: str) -> List[DcaClient]:
|
||||
async def get_flow_mode_clients_for_machine(machine_id: str) -> list[DcaClient]:
|
||||
"""Active LPs enrolled at this machine whose per-user `dca_lp` row
|
||||
has `default_dca_mode = 'flow'`. Used by the distribution algorithm.
|
||||
|
||||
|
|
@ -344,7 +343,7 @@ async def get_flow_mode_clients_for_machine(machine_id: str) -> List[DcaClient]:
|
|||
# =============================================================================
|
||||
|
||||
|
||||
async def get_dca_lp(user_id: str) -> Optional[DcaLpPreferences]:
|
||||
async def get_dca_lp(user_id: str) -> DcaLpPreferences | None:
|
||||
"""Return the LP's preferences row, or None if they haven't onboarded
|
||||
via satmachineclient yet."""
|
||||
return await db.fetchone(
|
||||
|
|
@ -367,7 +366,7 @@ async def upsert_dca_lp(
|
|||
user_id: str,
|
||||
data: UpsertDcaLpData,
|
||||
*,
|
||||
fallback_wallet_id: Optional[str] = None,
|
||||
fallback_wallet_id: str | None = None,
|
||||
) -> DcaLpPreferences:
|
||||
"""Create or update the LP's preferences row.
|
||||
|
||||
|
|
@ -422,7 +421,7 @@ async def upsert_dca_lp(
|
|||
|
||||
async def update_dca_client(
|
||||
client_id: str, data: UpdateDcaClientData
|
||||
) -> Optional[DcaClient]:
|
||||
) -> DcaClient | None:
|
||||
update_data = {k: v for k, v in data.dict().items() if v is not None}
|
||||
if not update_data:
|
||||
return await get_dca_client(client_id)
|
||||
|
|
@ -484,7 +483,7 @@ async def create_deposit(
|
|||
return deposit
|
||||
|
||||
|
||||
async def get_deposit(deposit_id: str) -> Optional[DcaDeposit]:
|
||||
async def get_deposit(deposit_id: str) -> DcaDeposit | None:
|
||||
return await db.fetchone(
|
||||
"SELECT * FROM satoshimachine.dca_deposits WHERE id = :id",
|
||||
{"id": deposit_id},
|
||||
|
|
@ -492,7 +491,7 @@ async def get_deposit(deposit_id: str) -> Optional[DcaDeposit]:
|
|||
)
|
||||
|
||||
|
||||
async def get_deposits_for_client(client_id: str) -> List[DcaDeposit]:
|
||||
async def get_deposits_for_client(client_id: str) -> list[DcaDeposit]:
|
||||
return await db.fetchall(
|
||||
"""
|
||||
SELECT * FROM satoshimachine.dca_deposits
|
||||
|
|
@ -504,7 +503,7 @@ async def get_deposits_for_client(client_id: str) -> List[DcaDeposit]:
|
|||
)
|
||||
|
||||
|
||||
async def get_deposits_for_operator(operator_user_id: str) -> List[DcaDeposit]:
|
||||
async def get_deposits_for_operator(operator_user_id: str) -> list[DcaDeposit]:
|
||||
return await db.fetchall(
|
||||
"""
|
||||
SELECT d.*
|
||||
|
|
@ -520,7 +519,7 @@ async def get_deposits_for_operator(operator_user_id: str) -> List[DcaDeposit]:
|
|||
|
||||
async def update_deposit(
|
||||
deposit_id: str, data: UpdateDepositData
|
||||
) -> Optional[DcaDeposit]:
|
||||
) -> DcaDeposit | None:
|
||||
update_data = {k: v for k, v in data.dict().items() if v is not None}
|
||||
if not update_data:
|
||||
return await get_deposit(deposit_id)
|
||||
|
|
@ -535,7 +534,7 @@ async def update_deposit(
|
|||
|
||||
async def update_deposit_status(
|
||||
deposit_id: str, data: UpdateDepositStatusData
|
||||
) -> Optional[DcaDeposit]:
|
||||
) -> DcaDeposit | None:
|
||||
payload = {
|
||||
"id": deposit_id,
|
||||
"status": data.status,
|
||||
|
|
@ -570,8 +569,8 @@ async def delete_deposit(deposit_id: str) -> None:
|
|||
async def create_settlement_idempotent(
|
||||
data: CreateDcaSettlementData,
|
||||
initial_status: str,
|
||||
error_message: Optional[str] = None,
|
||||
) -> Optional[DcaSettlement]:
|
||||
error_message: str | None = None,
|
||||
) -> DcaSettlement | None:
|
||||
"""Insert a settlement keyed by payment_hash.
|
||||
|
||||
Returns the inserted row on first sight; returns the existing row
|
||||
|
|
@ -631,7 +630,7 @@ async def create_settlement_idempotent(
|
|||
return await get_settlement(settlement_id)
|
||||
|
||||
|
||||
async def get_settlement(settlement_id: str) -> Optional[DcaSettlement]:
|
||||
async def get_settlement(settlement_id: str) -> DcaSettlement | None:
|
||||
return await db.fetchone(
|
||||
"SELECT * FROM satoshimachine.dca_settlements WHERE id = :id",
|
||||
{"id": settlement_id},
|
||||
|
|
@ -641,7 +640,7 @@ async def get_settlement(settlement_id: str) -> Optional[DcaSettlement]:
|
|||
|
||||
async def get_settlement_by_payment_hash(
|
||||
payment_hash: str,
|
||||
) -> Optional[DcaSettlement]:
|
||||
) -> DcaSettlement | None:
|
||||
return await db.fetchone(
|
||||
"""
|
||||
SELECT * FROM satoshimachine.dca_settlements
|
||||
|
|
@ -654,7 +653,7 @@ async def get_settlement_by_payment_hash(
|
|||
|
||||
async def get_settlements_for_machine(
|
||||
machine_id: str, limit: int = 100
|
||||
) -> List[DcaSettlement]:
|
||||
) -> list[DcaSettlement]:
|
||||
return await db.fetchall(
|
||||
"""
|
||||
SELECT * FROM satoshimachine.dca_settlements
|
||||
|
|
@ -747,7 +746,7 @@ async def get_stuck_settlements_for_operator(
|
|||
|
||||
async def force_reset_stuck_settlement(
|
||||
settlement_id: str,
|
||||
) -> Optional[DcaSettlement]:
|
||||
) -> DcaSettlement | None:
|
||||
"""Operator escape hatch for genuinely stuck settlements (processor
|
||||
crashed mid-flight, etc.). Flips 'pending'/'processing' → 'errored' so
|
||||
the existing retry endpoint can take over. Clears processing_claim.
|
||||
|
|
@ -770,7 +769,7 @@ async def force_reset_stuck_settlement(
|
|||
|
||||
async def get_settlements_for_operator(
|
||||
operator_user_id: str, limit: int = 200
|
||||
) -> List[DcaSettlement]:
|
||||
) -> list[DcaSettlement]:
|
||||
return await db.fetchall(
|
||||
"""
|
||||
SELECT s.*
|
||||
|
|
@ -788,8 +787,8 @@ async def get_settlements_for_operator(
|
|||
async def mark_settlement_status(
|
||||
settlement_id: str,
|
||||
status: str,
|
||||
error_message: Optional[str] = None,
|
||||
) -> Optional[DcaSettlement]:
|
||||
error_message: str | None = None,
|
||||
) -> DcaSettlement | None:
|
||||
"""Status: 'pending' | 'processing' | 'processed' | 'partial' |
|
||||
'refunded' | 'errored'. Clears processing_claim on terminal states so a
|
||||
fresh claim attempt won't see a stale token."""
|
||||
|
|
@ -820,7 +819,7 @@ async def mark_settlement_status(
|
|||
|
||||
async def claim_settlement_for_processing(
|
||||
settlement_id: str,
|
||||
) -> Optional[DcaSettlement]:
|
||||
) -> DcaSettlement | None:
|
||||
"""Optimistic-lock claim: atomically flip a settlement to 'processing'
|
||||
and tag it with a per-invocation token. Returns the claimed row on
|
||||
success; None if another caller already won the claim or the settlement
|
||||
|
|
@ -850,7 +849,7 @@ async def claim_settlement_for_processing(
|
|||
|
||||
async def reset_settlement_for_retry(
|
||||
settlement_id: str,
|
||||
) -> Optional[DcaSettlement]:
|
||||
) -> DcaSettlement | None:
|
||||
"""Operator retry path. Flips 'errored' → 'pending' and voids any
|
||||
'failed' legs so process_settlement re-runs them fresh. Completed legs
|
||||
are left in place — we never re-pay sats that already moved."""
|
||||
|
|
@ -886,7 +885,7 @@ async def apply_partial_dispense(
|
|||
new_operator_fee_sats: int,
|
||||
new_fiat_amount: float,
|
||||
appended_note: str,
|
||||
) -> Optional[DcaSettlement]:
|
||||
) -> DcaSettlement | None:
|
||||
"""Overwrite the monetary fields on a settlement (partial-dispense
|
||||
recompute) and prepend `appended_note` to the notes column.
|
||||
|
||||
|
|
@ -941,7 +940,7 @@ async def count_completed_legs_for_settlement(settlement_id: str) -> int:
|
|||
|
||||
async def append_settlement_note(
|
||||
settlement_id: str, note: str, author_user_id: str
|
||||
) -> Optional[DcaSettlement]:
|
||||
) -> DcaSettlement | None:
|
||||
"""Prepend an operator-authored note to settlement.notes. Each entry is
|
||||
timestamped (UTC) and tagged with the author's user id so the trail
|
||||
is accountable. Append-only: existing entries are never edited."""
|
||||
|
|
@ -986,8 +985,8 @@ async def void_open_legs_for_settlement(settlement_id: str) -> None:
|
|||
|
||||
|
||||
async def get_commission_splits(
|
||||
operator_user_id: str, machine_id: Optional[str] = None
|
||||
) -> List[CommissionSplit]:
|
||||
operator_user_id: str, machine_id: str | None = None
|
||||
) -> list[CommissionSplit]:
|
||||
"""Returns the rule set for the given scope.
|
||||
|
||||
Precedence (caller's responsibility): try per-machine override first;
|
||||
|
|
@ -1016,7 +1015,7 @@ async def get_commission_splits(
|
|||
|
||||
async def get_effective_commission_splits(
|
||||
operator_user_id: str, machine_id: str
|
||||
) -> List[CommissionSplit]:
|
||||
) -> list[CommissionSplit]:
|
||||
"""Per-machine override if set, otherwise operator's default ruleset."""
|
||||
overrides = await get_commission_splits(operator_user_id, machine_id)
|
||||
if overrides:
|
||||
|
|
@ -1026,9 +1025,9 @@ async def get_effective_commission_splits(
|
|||
|
||||
async def replace_commission_splits(
|
||||
operator_user_id: str,
|
||||
machine_id: Optional[str],
|
||||
legs: List[CommissionSplitLeg],
|
||||
) -> List[CommissionSplit]:
|
||||
machine_id: str | None,
|
||||
legs: list[CommissionSplitLeg],
|
||||
) -> list[CommissionSplit]:
|
||||
"""Atomic replace for the (operator, machine) scope. Caller should have
|
||||
already validated legs sum to 1.0 via the Pydantic model."""
|
||||
if machine_id is None:
|
||||
|
|
@ -1114,7 +1113,7 @@ async def create_dca_payment(data: CreateDcaPaymentData) -> DcaPayment:
|
|||
return payment
|
||||
|
||||
|
||||
async def get_dca_payment(payment_id: str) -> Optional[DcaPayment]:
|
||||
async def get_dca_payment(payment_id: str) -> DcaPayment | None:
|
||||
return await db.fetchone(
|
||||
"SELECT * FROM satoshimachine.dca_payments WHERE id = :id",
|
||||
{"id": payment_id},
|
||||
|
|
@ -1122,7 +1121,7 @@ async def get_dca_payment(payment_id: str) -> Optional[DcaPayment]:
|
|||
)
|
||||
|
||||
|
||||
async def get_payments_for_settlement(settlement_id: str) -> List[DcaPayment]:
|
||||
async def get_payments_for_settlement(settlement_id: str) -> list[DcaPayment]:
|
||||
return await db.fetchall(
|
||||
"""
|
||||
SELECT * FROM satoshimachine.dca_payments
|
||||
|
|
@ -1134,7 +1133,7 @@ async def get_payments_for_settlement(settlement_id: str) -> List[DcaPayment]:
|
|||
)
|
||||
|
||||
|
||||
async def get_payments_for_client(client_id: str) -> List[DcaPayment]:
|
||||
async def get_payments_for_client(client_id: str) -> list[DcaPayment]:
|
||||
return await db.fetchall(
|
||||
"""
|
||||
SELECT * FROM satoshimachine.dca_payments
|
||||
|
|
@ -1147,8 +1146,8 @@ async def get_payments_for_client(client_id: str) -> List[DcaPayment]:
|
|||
|
||||
|
||||
async def get_payments_for_operator(
|
||||
operator_user_id: str, leg_type: Optional[str] = None, limit: int = 200
|
||||
) -> List[DcaPayment]:
|
||||
operator_user_id: str, leg_type: str | None = None, limit: int = 200
|
||||
) -> list[DcaPayment]:
|
||||
if leg_type is None:
|
||||
return await db.fetchall(
|
||||
"""
|
||||
|
|
@ -1175,9 +1174,9 @@ async def get_payments_for_operator(
|
|||
async def update_payment_status(
|
||||
payment_id: str,
|
||||
status: str,
|
||||
external_payment_hash: Optional[str] = None,
|
||||
error_message: Optional[str] = None,
|
||||
) -> Optional[DcaPayment]:
|
||||
external_payment_hash: str | None = None,
|
||||
error_message: str | None = None,
|
||||
) -> DcaPayment | None:
|
||||
await db.execute(
|
||||
"""
|
||||
UPDATE satoshimachine.dca_payments
|
||||
|
|
@ -1203,7 +1202,7 @@ async def update_payment_status(
|
|||
|
||||
async def get_client_balance_summary(
|
||||
client_id: str,
|
||||
) -> Optional[ClientBalanceSummary]:
|
||||
) -> ClientBalanceSummary | None:
|
||||
"""Per-client (and per-machine, since clients are per-machine in v2) summary.
|
||||
|
||||
DCA legs only — settlement/autoforward/super_fee/operator_split legs are
|
||||
|
|
@ -1252,7 +1251,7 @@ async def get_client_balance_summary(
|
|||
# =============================================================================
|
||||
|
||||
|
||||
async def get_telemetry(machine_id: str) -> Optional[TelemetrySnapshot]:
|
||||
async def get_telemetry(machine_id: str) -> TelemetrySnapshot | None:
|
||||
return await db.fetchone(
|
||||
"SELECT * FROM satoshimachine.dca_telemetry WHERE machine_id = :mid",
|
||||
{"mid": machine_id},
|
||||
|
|
@ -1263,19 +1262,19 @@ async def get_telemetry(machine_id: str) -> Optional[TelemetrySnapshot]:
|
|||
async def upsert_beacon_snapshot(
|
||||
machine_id: str,
|
||||
*,
|
||||
cash_in: Optional[bool] = None,
|
||||
cash_out: Optional[bool] = None,
|
||||
cash_level: Optional[str] = None,
|
||||
fiat: Optional[str] = None,
|
||||
model: Optional[str] = None,
|
||||
name: Optional[str] = None,
|
||||
location: Optional[str] = None,
|
||||
geo: Optional[str] = None,
|
||||
fees_json: Optional[str] = None,
|
||||
limits_json: Optional[str] = None,
|
||||
denominations_json: Optional[str] = None,
|
||||
version: Optional[str] = None,
|
||||
) -> Optional[TelemetrySnapshot]:
|
||||
cash_in: bool | None = None,
|
||||
cash_out: bool | None = None,
|
||||
cash_level: str | None = None,
|
||||
fiat: str | None = None,
|
||||
model: str | None = None,
|
||||
name: str | None = None,
|
||||
location: str | None = None,
|
||||
geo: str | None = None,
|
||||
fees_json: str | None = None,
|
||||
limits_json: str | None = None,
|
||||
denominations_json: str | None = None,
|
||||
version: str | None = None,
|
||||
) -> TelemetrySnapshot | None:
|
||||
"""Upsert kind-30078 beacon fields. All fields are nullable because today's
|
||||
upstream payload only carries cash_in/cash_out/cash_level/fiat/model (see
|
||||
lamassu-next#43 — the enrichment is not yet shipped)."""
|
||||
|
|
@ -1352,7 +1351,7 @@ async def upsert_beacon_snapshot(
|
|||
|
||||
async def upsert_fleet_snapshot(
|
||||
machine_id: str, telemetry_json: str
|
||||
) -> Optional[TelemetrySnapshot]:
|
||||
) -> TelemetrySnapshot | None:
|
||||
"""Upsert kind-30079 operator-only telemetry. Awaits lamassu-next#42 to
|
||||
produce a real schema; we store the raw JSON blob until then."""
|
||||
existing = await get_telemetry(machine_id)
|
||||
|
|
@ -1391,7 +1390,7 @@ async def upsert_fleet_snapshot(
|
|||
|
||||
|
||||
def _should_apply_bootstrap_state(
|
||||
existing_state_event_id: Optional[str], incoming_event_id: str
|
||||
existing_state_event_id: str | None, incoming_event_id: str
|
||||
) -> bool:
|
||||
"""Pure-function dedup gate for apply_bootstrap_state.
|
||||
|
||||
|
|
@ -1408,7 +1407,7 @@ def _should_apply_bootstrap_state(
|
|||
|
||||
async def get_cassette_config(
|
||||
machine_id: str, position: int
|
||||
) -> Optional[CassetteConfig]:
|
||||
) -> CassetteConfig | None:
|
||||
return await db.fetchone(
|
||||
"SELECT * FROM satoshimachine.cassette_configs "
|
||||
"WHERE machine_id = :mid AND position = :pos",
|
||||
|
|
@ -1419,7 +1418,7 @@ async def get_cassette_config(
|
|||
|
||||
async def list_cassette_configs_for_machine(
|
||||
machine_id: str,
|
||||
) -> List[CassetteConfig]:
|
||||
) -> list[CassetteConfig]:
|
||||
return await db.fetchall(
|
||||
"SELECT * FROM satoshimachine.cassette_configs "
|
||||
"WHERE machine_id = :mid ORDER BY position",
|
||||
|
|
@ -1433,8 +1432,8 @@ async def update_cassette_config(
|
|||
position: int,
|
||||
data: UpsertCassetteConfigData,
|
||||
*,
|
||||
updated_by: Optional[str] = None,
|
||||
) -> Optional[CassetteConfig]:
|
||||
updated_by: str | None = None,
|
||||
) -> CassetteConfig | None:
|
||||
"""Operator-driven row update: change denomination and/or count for a
|
||||
single cassette slot. Refuses to create new rows — those only land via
|
||||
apply_bootstrap_state() consuming an ATM bootstrap event (per #29 row
|
||||
|
|
@ -1480,12 +1479,12 @@ async def apply_bootstrap_state(
|
|||
reconciliation UI will diverge them when continuous reverse-channel
|
||||
events land + the operator subsequently edits.
|
||||
"""
|
||||
existing_first = await db.fetchone(
|
||||
existing_first: dict | None = await db.fetchone(
|
||||
"SELECT state_event_id FROM satoshimachine.cassette_configs "
|
||||
"WHERE machine_id = :mid LIMIT 1",
|
||||
{"mid": machine_id},
|
||||
)
|
||||
existing_event_id: Optional[str] = None
|
||||
existing_event_id: str | None = None
|
||||
if existing_first is not None:
|
||||
existing_event_id = (
|
||||
existing_first.get("state_event_id")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue