Adds point_alarm.py (flattening, secret masking, SQLite upsert store, paged fetch with 403 fallback, CSV/TXT export, shared filter loader), nowcast-point-page.py CLI and unit tests. Register will read the shared filter from nowcast-point-alarm-filter.json instead of repeating it in the CSV.
140 lines
4.5 KiB
Python
140 lines
4.5 KiB
Python
#!/usr/bin/env python3
|
||
"""List point alarm registrations page by page into SQLite, screen or CSV/TXT."""
|
||
|
||
from __future__ import annotations
|
||
|
||
import argparse
|
||
import logging
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
from common import ApiError, ConfigurationError, IklimClient, configure_logging
|
||
from point_alarm import (
|
||
MAX_PAGE_SIZE,
|
||
PointAlarmService,
|
||
PointStore,
|
||
fetch_pages,
|
||
parse_polygon,
|
||
print_records,
|
||
prompt_export,
|
||
timestamped_path,
|
||
write_records,
|
||
)
|
||
|
||
|
||
BASE_DIR = Path(__file__).resolve().parent
|
||
|
||
|
||
def parse_args() -> argparse.Namespace:
|
||
parser = argparse.ArgumentParser(
|
||
description=(
|
||
"iklim.co nokta alarm kayıtlarını /v1/alarms/points/page ile listeler. "
|
||
"Sonuçlar SQLite'a (registrationId anahtarlı) yazılır; varsayılan olarak ekrana "
|
||
"basılır, --output ile CSV/TXT'ye yazılır."
|
||
),
|
||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||
)
|
||
parser.add_argument(
|
||
"--account-id",
|
||
help="Sorgulanacak hesap ID'si (varsayılan: /v1/users/me içindeki account.id)",
|
||
)
|
||
parser.add_argument(
|
||
"--recipient-id",
|
||
action="append",
|
||
default=[],
|
||
help="Alıcı ID'sine göre süz; birden fazla kez verilebilir",
|
||
)
|
||
parser.add_argument(
|
||
"--polygon",
|
||
help="Poligon kesişim filtresi: 'lng,lat;lng,lat;...' (en az 4 nokta, kapalı halka)",
|
||
)
|
||
parser.add_argument(
|
||
"--page-number",
|
||
type=int,
|
||
help="Yalnızca bu sayfayı getir (0'dan başlar); verilmezse tüm sayfalar gezilir",
|
||
)
|
||
parser.add_argument(
|
||
"--page-size",
|
||
type=int,
|
||
default=MAX_PAGE_SIZE,
|
||
help=f"Sayfa boyutu, 1-{MAX_PAGE_SIZE} (varsayılan: {MAX_PAGE_SIZE})",
|
||
)
|
||
parser.add_argument(
|
||
"--output",
|
||
type=Path,
|
||
help=(
|
||
"Çıktı dosyası (.csv noktalı virgüllü, .txt JSON Lines); dosya adına "
|
||
"zaman soneki eklenir (dosya-YYYYmmdd-HHMMSS.csv). Verilmezse ekrana yazılır"
|
||
),
|
||
)
|
||
parser.add_argument(
|
||
"--db",
|
||
type=Path,
|
||
default=BASE_DIR / "iklim-sandbox.db",
|
||
help="SQLite dosyası (varsayılan: iklim-sandbox.db)",
|
||
)
|
||
parser.add_argument(
|
||
"--debug-log",
|
||
type=Path,
|
||
default=BASE_DIR / "logs" / "iklim-api-debug.log",
|
||
help="Maskelenmiş HTTP debug log dosyası",
|
||
)
|
||
args = parser.parse_args()
|
||
if not 1 <= args.page_size <= MAX_PAGE_SIZE:
|
||
parser.error(f"--page-size 1-{MAX_PAGE_SIZE} aralığında olmalıdır")
|
||
if args.page_number is not None and args.page_number < 0:
|
||
parser.error("--page-number 0 veya daha büyük olmalıdır")
|
||
return args
|
||
|
||
|
||
def main() -> int:
|
||
args = parse_args()
|
||
configure_logging(args.debug_log)
|
||
store = None
|
||
try:
|
||
polygon = parse_polygon(args.polygon) if args.polygon else None
|
||
client = IklimClient.from_env(BASE_DIR / ".env")
|
||
service = PointAlarmService(client)
|
||
account_id = args.account_id or service.get_account_id()
|
||
store = PointStore(args.db)
|
||
summary = fetch_pages(
|
||
service,
|
||
store,
|
||
account_id=account_id,
|
||
recipient_ids=args.recipient_id or None,
|
||
polygon=polygon,
|
||
page_size=args.page_size,
|
||
page_number=args.page_number,
|
||
)
|
||
records = store.records(summary.account_id, summary.registration_ids)
|
||
webhooks = store.webhooks(summary.account_id)
|
||
if args.output:
|
||
path = timestamped_path(args.output)
|
||
write_records(path, records, webhooks)
|
||
print(f"{len(records)} kayıt yazıldı: {path.resolve()}")
|
||
else:
|
||
print_records(records, webhooks)
|
||
if args.page_number is None and summary.missing:
|
||
logging.warning(
|
||
"Sunucu %s kayıt bildirdi, %s benzersiz kayıt alındı (%s eksik). Sayfalama sırasında "
|
||
"kayıt eklenmiş/silinmiş olabilir; komutu yeniden çalıştırın.",
|
||
summary.total_records,
|
||
summary.distinct,
|
||
summary.missing,
|
||
)
|
||
if summary.duplicates:
|
||
logging.warning("Sayfalar arasında %s tekrar eden kayıt görüldü.", summary.duplicates)
|
||
if not args.output:
|
||
prompt_export(store, summary.account_id)
|
||
except (ConfigurationError, ApiError, OSError) as exc:
|
||
logging.error("İşlem durduruldu: %s", exc)
|
||
return 1
|
||
finally:
|
||
if store:
|
||
store.close()
|
||
return 0
|
||
|
||
|
||
if __name__ == "__main__":
|
||
sys.exit(main())
|