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:
4509
VAN_01971_Transactions_20260620_20260630.csv
Normal file
4509
VAN_01971_Transactions_20260620_20260630.csv
Normal file
File diff suppressed because it is too large
Load Diff
10139
VAN_01971_Transactions_20260620_20260712.csv
Normal file
10139
VAN_01971_Transactions_20260620_20260712.csv
Normal file
File diff suppressed because it is too large
Load Diff
BIN
__pycache__/uitlezen_laadpaal.cpython-312.pyc
Normal file
BIN
__pycache__/uitlezen_laadpaal.cpython-312.pyc
Normal file
Binary file not shown.
BIN
__pycache__/uitlezen_laadpaal.cpython-38.pyc
Normal file
BIN
__pycache__/uitlezen_laadpaal.cpython-38.pyc
Normal file
Binary file not shown.
@@ -48,31 +48,61 @@ def fetch_page(offset):
|
|||||||
def get_all_raw():
|
def get_all_raw():
|
||||||
"""Haalt alle transactiepagina's op via paginering.
|
"""Haalt alle transactiepagina's op via paginering.
|
||||||
Stopt zodra de record-nummers terugvallen (circulaire buffer bereikt).
|
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
|
debug = os.getenv("DEBUG") == "1"
|
||||||
spin = itertools.cycle('|/-\\')
|
MAX_GAP = 5000 # verst dat we een leeg/kapot record proberen te overspringen
|
||||||
|
|
||||||
all_raw = ""
|
all_raw = ""
|
||||||
offset = 0
|
offset = 0
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
#sys.stdout.write(next(spin) + '\b')
|
|
||||||
#sys.stdout.flush()
|
|
||||||
raw = fetch_page(offset)
|
raw = fetch_page(offset)
|
||||||
|
|
||||||
stripped = raw.strip().rstrip('}').strip()
|
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 not stripped or stripped in ('{"version":2,', '{"version":2'):
|
||||||
|
if debug:
|
||||||
|
print(f"[DEBUG] stop: lege/afgesloten pagina bij offset={offset}")
|
||||||
break
|
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
|
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
|
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 next_offset <= offset:
|
||||||
|
if debug:
|
||||||
|
print(f"[DEBUG] stop: next_offset({next_offset}) <= offset({offset}) — geen voortgang")
|
||||||
break
|
break
|
||||||
|
|
||||||
offset = next_offset
|
offset = next_offset
|
||||||
@@ -85,7 +115,8 @@ def get_all_raw():
|
|||||||
def parse_transactions(raw):
|
def parse_transactions(raw):
|
||||||
transactions = []
|
transactions = []
|
||||||
current_tx = None
|
current_tx = None
|
||||||
stop_parsing = False
|
last_offset = None
|
||||||
|
seen_starts = set()
|
||||||
|
|
||||||
# Verwijder pagina-headers eerst, zodat }{"version":2,305085_ correct wordt gesplitst
|
# Verwijder pagina-headers eerst, zodat }{"version":2,305085_ correct wordt gesplitst
|
||||||
raw = re.sub(r'\{"version":\d+,\s*', '', raw)
|
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)
|
raw = re.sub(r'\}\s*(\d+_)', r'\n\1', raw)
|
||||||
|
|
||||||
for line in raw.splitlines():
|
for line in raw.splitlines():
|
||||||
if stop_parsing:
|
|
||||||
break
|
|
||||||
|
|
||||||
line = line.strip().rstrip('}').strip()
|
line = line.strip().rstrip('}').strip()
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
@@ -105,7 +133,17 @@ def parse_transactions(raw):
|
|||||||
if not match:
|
if not match:
|
||||||
continue
|
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':
|
if record_type == 'txstart2':
|
||||||
m = re.match(
|
m = re.match(
|
||||||
@@ -116,11 +154,16 @@ def parse_transactions(raw):
|
|||||||
)
|
)
|
||||||
if m:
|
if m:
|
||||||
start_time = datetime.strptime(m.group(3), '%Y-%m-%d %H:%M:%S')
|
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
|
# Inhoudelijke dedup: de paal kan bij het overspringen van een gat
|
||||||
latest_start = max((tx['start_time'] for tx in transactions), default=None)
|
# (zie get_all_raw) oude data teruggeven onder een nieuw, oplopend
|
||||||
if latest_start and start_time < latest_start:
|
# 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
|
break
|
||||||
|
seen_starts.add(start_key)
|
||||||
|
|
||||||
current_tx = {
|
current_tx = {
|
||||||
'rfid': m.group(1),
|
'rfid': m.group(1),
|
||||||
'socket': int(m.group(2)),
|
'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'\{"version":\d+,\s*', '', raw)
|
||||||
raw = re.sub(r'\}\s*(\d+_)', r'\n\1', 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():
|
for line in raw.splitlines():
|
||||||
line = line.rstrip('}').strip()
|
line = line.rstrip('}').strip()
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
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:"
|
# Verwijder offset-nummers: "76_mv:" → "mv:", "0_txstart2:" → "txstart2:"
|
||||||
line = re.sub(r'^\d+_', '', line)
|
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:'):
|
if line.startswith('txstart2:'):
|
||||||
m = re.search(r'(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})', line)
|
payload = line[len('txstart2:'):].strip()
|
||||||
if m:
|
if payload in seen_starts:
|
||||||
start_time = datetime.strptime(m.group(1), '%Y-%m-%d %H:%M:%S')
|
break
|
||||||
if latest_start and start_time < latest_start:
|
seen_starts.add(payload)
|
||||||
break
|
|
||||||
latest_start = start_time
|
|
||||||
|
|
||||||
lines.append(line)
|
lines.append(line)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user