Add API interaction for charger data retrieval and CSV export functionality
- Implemented login and logout endpoints for user authentication. - Added functionality to fetch charger information and transaction data. - Introduced methods to parse and save raw API data into CSV format. - Enhanced logging for debugging and tracking API responses. - Included environment variable loading for sensitive configuration.
This commit is contained in:
@@ -48,31 +48,61 @@ def fetch_page(offset):
|
||||
def get_all_raw():
|
||||
"""Haalt alle transactiepagina's op via paginering.
|
||||
Stopt zodra de record-nummers terugvallen (circulaire buffer bereikt).
|
||||
|
||||
Een niet-afgesloten transactie kan een "gat" in de recordnummering
|
||||
achterlaten (een gereserveerde maar nooit weggeschreven slot). Een
|
||||
pagina die op zo'n gat begint komt leeg terug, ook al staat er verderop
|
||||
gewoon weer geldige data. Daarom wordt bij een lege pagina niet meteen
|
||||
gestopt, maar met oplopende stappen verderop geprobeerd of de data
|
||||
doorloopt, voordat we concluderen dat het echt het einde is.
|
||||
"""
|
||||
import sys, itertools
|
||||
spin = itertools.cycle('|/-\\')
|
||||
debug = os.getenv("DEBUG") == "1"
|
||||
MAX_GAP = 5000 # verst dat we een leeg/kapot record proberen te overspringen
|
||||
|
||||
all_raw = ""
|
||||
offset = 0
|
||||
|
||||
while True:
|
||||
#sys.stdout.write(next(spin) + '\b')
|
||||
#sys.stdout.flush()
|
||||
raw = fetch_page(offset)
|
||||
|
||||
stripped = raw.strip().rstrip('}').strip()
|
||||
if debug:
|
||||
print(f"\n[DEBUG] offset={offset} len(raw)={len(raw)} stripped_preview={stripped[:80]!r}")
|
||||
if not stripped or stripped in ('{"version":2,', '{"version":2'):
|
||||
if debug:
|
||||
print(f"[DEBUG] stop: lege/afgesloten pagina bij offset={offset}")
|
||||
break
|
||||
|
||||
offsets = re.findall(r'^(\d+)_\w+:', raw, re.MULTILINE)
|
||||
|
||||
if not offsets:
|
||||
# Mogelijk een gat door een niet-afgesloten transactie: probeer
|
||||
# verderop met oplopende stappen of de data daar doorloopt.
|
||||
gap = 1
|
||||
while gap <= MAX_GAP:
|
||||
probe_offset = offset + gap
|
||||
probe_raw = fetch_page(probe_offset)
|
||||
probe_offsets = re.findall(r'^(\d+)_\w+:', probe_raw, re.MULTILINE)
|
||||
if debug:
|
||||
print(f"[DEBUG] gat-probe offset={probe_offset} -> {'data gevonden' if probe_offsets else 'nog leeg'}")
|
||||
if probe_offsets:
|
||||
offset, raw, offsets = probe_offset, probe_raw, probe_offsets
|
||||
break
|
||||
gap *= 4
|
||||
else:
|
||||
if debug:
|
||||
print(f"[DEBUG] stop: geen data gevonden binnen {MAX_GAP} na offset={offset}")
|
||||
break
|
||||
|
||||
all_raw += raw
|
||||
|
||||
offsets = re.findall(r'^(\d+)_\w+:', raw, re.MULTILINE)
|
||||
if not offsets:
|
||||
break
|
||||
|
||||
next_offset = max(int(x) for x in offsets) + 1
|
||||
if debug:
|
||||
print(f"[DEBUG] gevonden offsets {min(int(x) for x in offsets)}..{max(int(x) for x in offsets)}, next_offset={next_offset}")
|
||||
|
||||
if next_offset <= offset:
|
||||
if debug:
|
||||
print(f"[DEBUG] stop: next_offset({next_offset}) <= offset({offset}) — geen voortgang")
|
||||
break
|
||||
|
||||
offset = next_offset
|
||||
@@ -85,7 +115,8 @@ def get_all_raw():
|
||||
def parse_transactions(raw):
|
||||
transactions = []
|
||||
current_tx = None
|
||||
stop_parsing = False
|
||||
last_offset = None
|
||||
seen_starts = set()
|
||||
|
||||
# Verwijder pagina-headers eerst, zodat }{"version":2,305085_ correct wordt gesplitst
|
||||
raw = re.sub(r'\{"version":\d+,\s*', '', raw)
|
||||
@@ -94,9 +125,6 @@ def parse_transactions(raw):
|
||||
raw = re.sub(r'\}\s*(\d+_)', r'\n\1', raw)
|
||||
|
||||
for line in raw.splitlines():
|
||||
if stop_parsing:
|
||||
break
|
||||
|
||||
line = line.strip().rstrip('}').strip()
|
||||
if not line:
|
||||
continue
|
||||
@@ -105,7 +133,17 @@ def parse_transactions(raw):
|
||||
if not match:
|
||||
continue
|
||||
|
||||
_, record_type, data = match.groups()
|
||||
offset_str, record_type, data = match.groups()
|
||||
offset = int(offset_str)
|
||||
|
||||
# Cirkelbuffer: recordnummers lopen monotoon op. Val terug = oude/herhaalde
|
||||
# data (buffer-wraparound) → stop met parsen. Timestamps zijn hiervoor niet
|
||||
# betrouwbaar: na een onverwachte reset (bv. door een niet-afgesloten
|
||||
# transactie) kan de klok van de paal tijdelijk terugvallen, waardoor latere,
|
||||
# geldige transacties anders ten onrechte als "oude data" werden gezien.
|
||||
if last_offset is not None and offset < last_offset:
|
||||
break
|
||||
last_offset = offset
|
||||
|
||||
if record_type == 'txstart2':
|
||||
m = re.match(
|
||||
@@ -116,11 +154,16 @@ def parse_transactions(raw):
|
||||
)
|
||||
if m:
|
||||
start_time = datetime.strptime(m.group(3), '%Y-%m-%d %H:%M:%S')
|
||||
# Cirkelbuffer: als de starttijd eerder is dan de hoogste geziene starttijd,
|
||||
# zijn we in herhaalde data beland → stop met parsen
|
||||
latest_start = max((tx['start_time'] for tx in transactions), default=None)
|
||||
if latest_start and start_time < latest_start:
|
||||
|
||||
# Inhoudelijke dedup: de paal kan bij het overspringen van een gat
|
||||
# (zie get_all_raw) oude data teruggeven onder een nieuw, oplopend
|
||||
# offset-label. Komt dezelfde sessie-signatuur al voor, dan is de
|
||||
# cirkelbuffer echt rond → stop met parsen.
|
||||
start_key = (m.group(1), int(m.group(2)), start_time, float(m.group(4)))
|
||||
if start_key in seen_starts:
|
||||
break
|
||||
seen_starts.add(start_key)
|
||||
|
||||
current_tx = {
|
||||
'rfid': m.group(1),
|
||||
'socket': int(m.group(2)),
|
||||
@@ -227,23 +270,33 @@ def save_as_csv(raw, device_id, filename=None):
|
||||
raw = re.sub(r'\{"version":\d+,\s*', '', raw)
|
||||
raw = re.sub(r'\}\s*(\d+_)', r'\n\1', raw)
|
||||
|
||||
latest_start = None
|
||||
last_offset = None
|
||||
seen_starts = set()
|
||||
for line in raw.splitlines():
|
||||
line = line.rstrip('}').strip()
|
||||
if not line:
|
||||
continue
|
||||
|
||||
# Cirkelbuffer: recordnummers lopen monotoon op; val terug = oude/herhaalde data
|
||||
offset_match = re.match(r'^(\d+)_', line)
|
||||
if offset_match:
|
||||
offset = int(offset_match.group(1))
|
||||
if last_offset is not None and offset < last_offset:
|
||||
break
|
||||
last_offset = offset
|
||||
|
||||
# Verwijder offset-nummers: "76_mv:" → "mv:", "0_txstart2:" → "txstart2:"
|
||||
line = re.sub(r'^\d+_', '', line)
|
||||
|
||||
# Cirkelbuffer: stop als txstart2 terug in de tijd gaat
|
||||
# Inhoudelijke dedup: de paal kan bij een gat in de nummering oude data
|
||||
# teruggeven onder een nieuw, oplopend offset-label (zie get_all_raw).
|
||||
# Komt dezelfde txstart2-payload al voor, dan is de cirkelbuffer echt
|
||||
# rond → stop.
|
||||
if line.startswith('txstart2:'):
|
||||
m = re.search(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', line)
|
||||
if m:
|
||||
start_time = datetime.strptime(m.group(1), '%Y-%m-%d %H:%M:%S')
|
||||
if latest_start and start_time < latest_start:
|
||||
break
|
||||
latest_start = start_time
|
||||
payload = line[len('txstart2:'):].strip()
|
||||
if payload in seen_starts:
|
||||
break
|
||||
seen_starts.add(payload)
|
||||
|
||||
lines.append(line)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user