Skip to content

Commit 0b65918

Browse files
authored
Fix for events with docs (#89)
* clean up of Timeline class
1 parent 5e5106b commit 0b65918

File tree

4 files changed

+47
-49
lines changed

4 files changed

+47
-49
lines changed

Diff for: pytr/dl.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ async def dl_loop(self):
7979
elif subscription['type'] == 'timelineActivityLog':
8080
await self.tl.get_next_timeline_activity_log(response)
8181
elif subscription['type'] == 'timelineDetailV2':
82-
await self.tl.timelineDetail(response, self)
82+
await self.tl.process_timelineDetail(response, self)
8383
else:
8484
self.log.warning(f"unmatched subscription of type '{subscription['type']}':\n{preview(response)}")
8585

Diff for: pytr/main.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from importlib.metadata import version
1111
from pathlib import Path
12-
from datetime import datetime
12+
from datetime import datetime, timedelta
1313

1414
from pytr.utils import get_logger, check_version, export_transactions
1515
from pytr.dl import DL
@@ -199,7 +199,7 @@ def main():
199199
if args.last_days == 0:
200200
since_timestamp = 0
201201
else:
202-
since_timestamp = datetime.now().timestamp() - (24 * 3600 * args.last_days)
202+
since_timestamp = (datetime.now().astimezone() - timedelta(days=args.last_days)).timestamp()
203203
dl = DL(
204204
login(phone_no=args.phone_no, pin=args.pin, web=not args.applogin),
205205
args.output,

Diff for: pytr/utils.py

+43-45
Original file line numberDiff line numberDiff line change
@@ -308,12 +308,10 @@ def __init__(self, tr, max_age_timestamp):
308308
self.log = get_logger(__name__)
309309
self.received_detail = 0
310310
self.requested_detail = 0
311-
self.num_timeline_details = 0
312311
self.events_without_docs = []
313312
self.events_with_docs = []
314313
self.num_timelines = 0
315314
self.timeline_events = {}
316-
self.timeline_events_iter = None
317315
self.max_age_timestamp = max_age_timestamp
318316

319317
async def get_next_timeline_transactions(self, response=None):
@@ -329,24 +327,24 @@ async def get_next_timeline_transactions(self, response=None):
329327
await self.tr.timeline_transactions()
330328
else:
331329
self.num_timelines += 1
332-
# print(json.dumps(response))
333-
self.num_timeline_details += len(response['items'])
330+
added_last_event = True
334331
for event in response['items']:
335-
event['source'] = "timelineTransaction"
336-
self.timeline_events[event['id']] = event
332+
if self.max_age_timestamp == 0 or datetime.fromisoformat(event['timestamp'][:19]).timestamp() >= self.max_age_timestamp:
333+
event['source'] = "timelineTransaction"
334+
self.timeline_events[event['id']] = event
335+
else:
336+
added_last_event = False
337+
break
337338

338-
after = response['cursors'].get('after')
339-
340-
last_transaction_time = response['items'][-1]['timestamp'][:19]
341-
timestamp = datetime.fromisoformat(last_transaction_time).timestamp()
342339
self.log.info(
343-
f'Received #{self.num_timelines:<2} timeline transactions until {last_transaction_time}'
340+
f'Received #{self.num_timelines:<2} timeline transactions'
344341
)
345-
if (after is not None) and ((self.max_age_timestamp == 0) or (timestamp >= self.max_age_timestamp)):
342+
after = response['cursors'].get('after')
343+
if (after is not None) and added_last_event:
346344
self.log.info(
347345
f'Subscribing #{self.num_timelines+1:<2} timeline transactions'
348346
)
349-
await self.tr.timeline_transactions(after)
347+
await self.tr.timeline_transactions(after)
350348
else:
351349
# last timeline is reached
352350
self.log.info('Received last relevant timeline transaction')
@@ -365,19 +363,21 @@ async def get_next_timeline_activity_log(self, response=None):
365363
self.num_timelines = 0
366364
await self.tr.timeline_activity_log()
367365
else:
368-
last_transaction_time = response['items'][-1]['timestamp'][:19]
369-
timestamp = datetime.fromisoformat(last_transaction_time).timestamp()
370366
self.num_timelines += 1
371-
# print(json.dumps(response))
372-
self.num_timeline_details += len(response['items'])
367+
added_last_event = True
373368
for event in response['items']:
374-
if event['id'] not in self.timeline_events:
369+
if self.max_age_timestamp == 0 or datetime.fromisoformat(event['timestamp'][:19]).timestamp() >= self.max_age_timestamp:
370+
if event['id'] in self.timeline_events:
371+
self.log.warning(f"Received duplicate event {event['id'] }")
375372
event['source'] = "timelineActivity"
376373
self.timeline_events[event['id']] = event
374+
else:
375+
added_last_event = False
376+
break
377377

378+
self.log.info(f'Received #{self.num_timelines:<2} timeline activity log')
378379
after = response['cursors'].get('after')
379-
self.log.info(f'Received #{self.num_timelines:<2} timeline activity log unitl {last_transaction_time}')
380-
if (after is not None) and ((self.max_age_timestamp == 0) or (timestamp >= self.max_age_timestamp)):
380+
if (after is not None) and added_last_event:
381381
self.log.info(
382382
f'Subscribing #{self.num_timelines+1:<2} timeline activity log'
383383
)
@@ -393,62 +393,55 @@ async def _get_timeline_details(self):
393393
for event in self.timeline_events.values():
394394
action = event.get('action')
395395
msg = ''
396-
timestamp_field = datetime.fromisoformat(event['timestamp'][:19]).timestamp()
397-
if self.max_age_timestamp != 0 and (timestamp_field < self.max_age_timestamp):
398-
msg += 'Skip: too old'
399-
elif action is None:
396+
if action is None:
400397
if event.get('actionLabel') is None:
401398
msg += 'Skip: no action'
402399
elif action.get('type') != 'timelineDetail':
403400
msg += f"Skip: action type unmatched ({action['type']})"
404401
elif action.get('payload') != event['id']:
405402
msg += f"Skip: payload unmatched ({action['payload']})"
406403

407-
if msg == '':
408-
self.events_with_docs.append(event)
409-
else:
404+
if msg != '':
410405
self.events_without_docs.append(event)
411-
self.log.debug(f"{msg} {event['title']}: {event.get('body')} {json.dumps(event)}")
412-
self.num_timeline_details -= 1
413-
continue
414-
415-
self.requested_detail += 1
416-
await self.tr.timeline_detail_v2(event['id'])
406+
self.log.debug(f"{msg} {event['title']}: {event.get('body')} ")
407+
else:
408+
self.requested_detail += 1
409+
await self.tr.timeline_detail_v2(event['id'])
417410
self.log.info('All timeline details requested')
418411
return False
419412

420-
async def timelineDetail(self, response, dl):
413+
async def process_timelineDetail(self, response, dl):
421414
'''
422-
process timeline response and request timelines
415+
process timeline details response
416+
download any associated docs
417+
create other_events.json, events_with_documents.json and account_transactions.csv
423418
'''
424419

425420
self.received_detail += 1
426421
event = self.timeline_events[response['id']]
427422
event['details'] = response
428423

429-
is_savings_plan = (event["eventType"] == "SAVINGS_PLAN_EXECUTED")
430-
431-
max_details_digits = len(str(self.num_timeline_details))
424+
max_details_digits = len(str(self.requested_detail))
432425
self.log.info(
433-
f"{self.received_detail:>{max_details_digits}}/{self.num_timeline_details}: "
434-
+ f"{event['title']} -- {event['subtitle']}"
426+
f"{self.received_detail:>{max_details_digits}}/{self.requested_detail}: "
427+
+ f"{event['title']} -- {event['subtitle']} - {event['timestamp'][:19]}"
435428
)
436429

437-
if is_savings_plan:
438-
subfolder = 'Sparplan'
439-
else:
440-
subfolder = {
430+
subfolder = {
441431
'benefits_saveback_execution': 'Saveback',
442432
'benefits_spare_change_execution': 'RoundUp',
443433
'ssp_corporate_action_invoice_cash': 'Dividende',
444434
'CREDIT': 'Dividende',
445435
'INTEREST_PAYOUT_CREATED': 'Zinsen',
436+
"SAVINGS_PLAN_EXECUTED":'Sparplan'
446437
}.get(event["eventType"])
447438

439+
event['has_docs'] = False
448440
for section in response['sections']:
449441
if section['type'] != 'documents':
450442
continue
451443
for doc in section['data']:
444+
event['has_docs'] = True
452445
try:
453446
timestamp = datetime.strptime(doc['detail'], '%d.%m.%Y').timestamp()
454447
except (ValueError, KeyError):
@@ -459,7 +452,12 @@ async def timelineDetail(self, response, dl):
459452
title += f" - {event['subtitle']}"
460453
dl.dl_doc(doc, title, doc.get('detail'), subfolder)
461454

462-
if self.received_detail == self.num_timeline_details:
455+
if event['has_docs']:
456+
self.events_with_docs.append(event)
457+
else:
458+
self.events_without_docs.append(event)
459+
460+
if self.received_detail == self.requested_detail:
463461
self.log.info('Received all details')
464462
dl.output_path.mkdir(parents=True, exist_ok=True)
465463
with open(dl.output_path / 'other_events.json', 'w', encoding='utf-8') as f:

Diff for: setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def readme():
1010

1111
setup(
1212
name='pytr',
13-
version='0.2.0',
13+
version='0.2.1',
1414
description='Use TradeRepublic in terminal',
1515
long_description=readme(),
1616
long_description_content_type='text/markdown',

0 commit comments

Comments
 (0)