From ef84f9ee091f3d4675f491794681c953bc2a59ee Mon Sep 17 00:00:00 2001 From: bjbe Date: Mon, 22 Jul 2024 21:13:45 +0200 Subject: [PATCH 01/11] Customizable destination folder structure (WIP) --- pyproject.toml | 2 + pytr/api.py | 7 +- pytr/app_path.py | 10 + pytr/config/__init__.py | 0 .../file_destination_config__template.yaml | 387 ++++++++++++++++++ pytr/dl.py | 88 ++-- pytr/file_destination_provider.py | 185 +++++++++ pytr/main.py | 11 +- pytr/timeline.py | 14 +- 9 files changed, 618 insertions(+), 86 deletions(-) create mode 100644 pytr/app_path.py create mode 100644 pytr/config/__init__.py create mode 100644 pytr/config/file_destination_config__template.yaml create mode 100644 pytr/file_destination_provider.py diff --git a/pyproject.toml b/pyproject.toml index 7b53589e1..9c838921 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -32,6 +32,8 @@ dependencies = [ "shtab", "websockets>=10.1", "babel", + "PyYAML", + "importlib_resources" ] [project.scripts] diff --git a/pytr/api.py b/pytr/api.py index 20ae661d..b9879d07 100644 --- a/pytr/api.py +++ b/pytr/api.py @@ -32,18 +32,15 @@ import ssl import requests import websockets + from ecdsa import NIST256p, SigningKey from ecdsa.util import sigencode_der from http.cookiejar import MozillaCookieJar from pytr.utils import get_logger +from pytr.app_path import * -home = pathlib.Path.home() -BASE_DIR = home / '.pytr' -CREDENTIALS_FILE = BASE_DIR / 'credentials' -KEY_FILE = BASE_DIR / 'keyfile.pem' -COOKIES_FILE = BASE_DIR / 'cookies.txt' class TradeRepublicApi: diff --git a/pytr/app_path.py b/pytr/app_path.py new file mode 100644 index 00000000..01b34f0f --- /dev/null +++ b/pytr/app_path.py @@ -0,0 +1,10 @@ +import pathlib + +home = pathlib.Path.home() +BASE_DIR = home / '.pytr' + +CREDENTIALS_FILE = BASE_DIR / 'credentials' +KEY_FILE = BASE_DIR / 'keyfile.pem' +COOKIES_FILE = BASE_DIR / 'cookies.txt' + +DESTINATION_CONFIG_FILE = BASE_DIR / 'file_destination_config.yaml' \ No newline at end of file diff --git a/pytr/config/__init__.py b/pytr/config/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pytr/config/file_destination_config__template.yaml b/pytr/config/file_destination_config__template.yaml new file mode 100644 index 00000000..4a296a43 --- /dev/null +++ b/pytr/config/file_destination_config__template.yaml @@ -0,0 +1,387 @@ +destination: +# valid for all blocks without explicit filename + all: + filename: "{iso_date}.{iso_time} {event_title}" # {event_title} = Wertpapier-/ETF-/Produkt-Name + + +# if pattern not found, use this block + unknown: + path: "Unknown/{section_title}/" + filename: "{iso_date}.{iso_time} {event_type} - {event_subtitle} - {document_title} - {event_title}" + + +# Aktien + aktien_kauf_abrechnung: + pattern: [ + { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit gekauft + { event_type: "ORDER_EXECUTED", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit gekauft + { event_type: "ORDER_EXECUTED", event_subtitle: "Stop-Sell-Order", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit verkauft + { event_type: "TRADE_INVOICE", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit gekauft + { event_type: "ORDER_EXECUTED", event_subtitle: "Verkaufsorder", section_title: "Dokumente", document_title: "Abrechnung" }, # verkauft + { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", section_title: "Dokumente", document_title: "Abrechnung" }, # Aktiengeschenk + { event_type: "SHAREBOOKING", event_subtitle: "Verkauf", section_title: "Dokumente", document_title: "Abrechnung \\d" }, # Kapitalmassnahme + ] + path: "Wertpapiere/Abrechnung/{iso_date_year}/" + + + aktien_kauf_kosteninfo: + pattern: [ + { event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", section_title: "Dokumente", document_title: "Kosteninformation" }, # limit erstellt + { event_type: "ORDER_EXECUTED", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Kosteninformation" }, + { event_type: "ORDER_EXECUTED", event_subtitle: "Stop-Sell-Order", section_title: "Dokumente", document_title: "Kosteninformation" }, + { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Kosteninformation" }, # ohne limit gekauft + { event_type: "ORDER_EXECUTED", event_subtitle: "Verkaufsorder", section_title: "Dokumente", document_title: "Kosteninformation" }, # verkauft + { event_type: "TRADE_INVOICE", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Kosteninformation" }, + { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", section_title: "Dokumente", document_title: "Kosteninformation" }, # Aktiengeschenk + { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", section_title: "Dokumente", document_title: "Kosteninformation \\d" }, # Aktiengeschenk + { event_type: "EX_POST_COST_REPORT", event_subtitle: , event_title: "Ex-Post Kosteninformation", section_title: "Dokumente", document_title: "Ex-Post Kosteninformation \\d+" }, + ] + path: "Wertpapiere/Kosteninfo/{iso_date_year}/" + + + aktien_kauf_limit_erstellt: + pattern: [ + { event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, + { event_type: "ORDER_EXECUTED", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, + { event_type: "ORDER_EXECUTED", event_subtitle: "Stop-Sell-Order", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, + { event_type: "TRADE_INVOICE", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, + ] + path: "Wertpapiere/Order erstellt/{iso_date_year}/" + + + aktien_limit_storno: + pattern: [ + { event_type: "TRADE_CANCELED", event_subtitle: "Verkauf-Abrechnung storniert", section_title: "Dokumente", document_title: "Abrechnung \\d" }, + { event_type: "ORDER_CANCELED", event_subtitle: "Limit-Buy-Order storniert", section_title: "Dokumente", document_title: "Löschbestätigung" }, + { event_type: "SAVINGS_PLAN_CANCELED", event_subtitle: "Sparplan storniert", section_title: "Dokumente", document_title: "Stornierungsbestätigung" }, + ] + path: "Wertpapiere/Order storno/{iso_date_year}/" + + +# Kapitalmaßnahmen + info_anzeigen1: + pattern: [ + { event_type: "EXERCISE", event_subtitle: "Ausübung", section_title: "Dokumente", document_title: "Kundenanschreiben" }, + { event_type: "SHAREBOOKING", event_subtitle: "Split", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Fusion", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Verkauf", section_title: "Dokumente", document_title: "Ausführungsanzeige \\d" }, + { event_type: "SHAREBOOKING", event_subtitle: "Ausbuchung", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "ISIN Wechsel", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Entflechtung", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Reverse Split", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Titelumtausch", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Umtausch/Bezug", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Reorganisation", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Stockdividende", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Kapitalreduktion", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Titelgleichstellung", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Wertlose Ausbuchung", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Effektive Auslieferung", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Kapitalerhöhung gegen Bar", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING", event_subtitle: "Kapitalerhöhung aus Gesellschaftsmitteln", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + ] + path: "Wertpapiere/Anzeigen/{iso_date_year}/" + filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" + +# split for better readability + info_anzeigen2: + pattern: [ + { event_type: "CORPORATE_ACTION", event_subtitle: "Kapitalherabsetzung", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "SHAREBOOKING_CANCELED", event_subtitle: "Entflechtung storniert", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING_CANCELED", event_subtitle: "Stockdividende storniert", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "SHAREBOOKING_CANCELED", event_subtitle: "Kapitalerhöhung gegen Bar storniert", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + ] + path: "Wertpapiere/Anzeigen/{iso_date_year}/" + filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" + +# split for better readability + info_anzeigen3: + pattern: [ + { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Fusion", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Reinvestierung", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Umtausch/Bezug", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Zwangsübernahme", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Kapitalreduktion", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", section_title: "Dokumente", document_title: "Kundenanschreiben" }, #Kapitalerhöhung gegen Bar + { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", section_title: "Dokumente", document_title: "Kundenanschreiben \\d" }, #Kapitalerhöhung gegen Bar + ] + path: "Wertpapiere/Anzeigen/{iso_date_year}/" + filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" + +# split for better readability + info_anzeigen4: + pattern: [ + { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Spin-off", section_title: "Dokumente", document_title: "Dokumente" }, + { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Reverse Split", section_title: "Dokumente", document_title: "Dokumente" }, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Wechsel", section_title: "Dokumente", document_title: "Information" }, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Information", section_title: "Dokumente", document_title: "Information" }, + ] + path: "Wertpapiere/Anzeigen/{iso_date_year}/" + filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" + + + portfolio_abschluess: + pattern: [ + { event_type: "QUARTERLY_REPORT", event_subtitle: , event_title: "Q\\d/\\d+ Abschluss", section_title: "Dokumente", document_title: "Kontoauszug" }, + { event_type: "QUARTERLY_REPORT", event_subtitle: , event_title: "Q\\d/\\d+ Abschluss", section_title: "Dokumente", document_title: "Depotauszug" }, + { event_type: "QUARTERLY_REPORT", event_subtitle: , event_title: "Q\\d/\\d+ Abschluss", section_title: "Dokumente", document_title: "Cryptoauszug" }, + ] + path: "Wertpapiere/Abschluss/{iso_date_year}/" + filename: "{iso_date} {document_title} {event_title}" + + +# Dividenden + dividende_erhalten: + pattern: [ + { event_type: "CREDIT", event_subtitle: "Dividende", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "CREDIT", event_subtitle: "Ausschüttung", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Bardividende", section_title: "Dokumente", document_title: "Dokumente" }, + ] + path: "Dividenden/{iso_date_year}/" + + +# Dividenden Kapitalmaßnahme wahl + dividende_wahl: + pattern: [ + { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", section_title: "Dokumente", document_title: "Dividende Wahlweise" }, + { event_type: "ssp_dividend_option_customer_instruction", event_subtitle: "Cash oder Aktie", section_title: "Dokumente", document_title: "Dividende Wahlweise" }, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Dividende Wahlweise", section_title: "Dokumente", document_title: "Kapitalmaßnahmen" }, + ] + path: "Dividendenwahl/{iso_date_year}/" + + +# Hauptversammlungen + hauptversammlungen: + pattern: [ + { event_type: "GENERAL_MEETING", event_subtitle: "Hauptversammlung", section_title: "Dokumente", document_title: "Hauptversammlung" }, + { event_type: "GENERAL_MEETING", event_subtitle: "Hauptversammlung", section_title: "Dokumente", document_title: "Hauptversammlung \\d" }, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Jährliche Hauptversammlung", section_title: "Dokumente", document_title: "Information" }, + ] + path: "Wertpapiere/Hauptversammlungen/{iso_date_year}/" + + +# Sparplan + sparplan: + pattern: [ + { event_type: "SAVINGS_PLAN_INVOICE_CREATED", event_subtitle: "Sparplan ausgeführt", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, + { event_type: "SAVINGS_PLAN_EXECUTED", event_subtitle: "Sparplan ausgeführt", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, + ] + path: "Wertpapiere/Sparplan/{iso_date_year}/" + + +# Vorabpauschale + vorabpauschale: + pattern: [ + { event_type: "PRE_DETERMINED_TAX_BASE_EARNING", event_subtitle: "Vorabpauschale", section_title: "Dokumente", document_title: "Vorabpauschale" }, + ] + path: "Wertpapiere/Vorabpauschale/{iso_date_year}/" + + +# Anleihen + anleihe_tilgung: + pattern: [ + { event_type: "REPAYMENT", event_subtitle: "Tilgung", section_title: "Dokumente", document_title: "Abrechnung" }, + ] + path: "Anleihen/Tilgungen/{iso_date_year}/" + + anleihe_zinsen: + pattern: [ + { event_type: "COUPON_PAYMENT", event_subtitle: "Coupon Zahlung", section_title: "Dokumente", document_title: "Abrechnung" }, + ] + path: "Anleihen/Zinsen/{iso_date_year}/" + + +# Saveback + saveback_aktiviert: + pattern: [ + { event_type: "benefits_saveback_execution", event_subtitle: "Saveback", section_title: "Dokumente", document_title: "Aktivierung" }, + ] + path: "Saveback/aktiviert/" + + + saveback_abrechnung: + pattern: [ + { event_type: "benefits_saveback_execution", event_subtitle: "Saveback", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, + ] + path: "Saveback/Abrechnung/" + + + saveback_kosteninfo: + pattern: [ + { event_type: "benefits_saveback_execution", event_subtitle: "Saveback", section_title: "Dokumente", document_title: "Kosteninformation" }, + ] + path: "Saveback/Kosteninfo/" + + +# Round up + roundup_aktiviert: + pattern: [ + { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", section_title: "Dokumente", document_title: "Aktivierung" }, # same files - multiple times at once + ] + path: "Roundup/aktiviert/{iso_date_year}/" + + roundup_ausgefuert: + pattern: [ + { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, + ] + path: "Roundup/Abrechnung/{iso_date_year}/" + + roundup_kosteninfo: + pattern: [ + { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", section_title: "Dokumente", document_title: "Kosteninformation" }, + ] + path: "Roundup/Kosteninfo/{iso_date_year}/" + + +# Konto + zinsen: + pattern: [ + { event_type: "INTEREST_PAYOUT", event_subtitle: , event_title: "Zinsen", section_title: "Dokument", document_title: "Abrechnung" }, + { event_type: "INTEREST_PAYOUT_CREATED", event_subtitle: , event_title: "Zinsen", section_title: "Dokument", document_title: "Abrechnung" }, + ] + path: "Zinsen/" + filename: "{iso_date} Zinsabrechnung" + + + uberweisung_ankommend: + pattern: [ + { event_type: "INCOMING_TRANSFER", event_subtitle: "Erhalten", section_title: "Dokument", document_title: "Transaktionsbestätigung" }, + ] + path: "Konto/" + filename: "{iso_date}.{iso_time} {document_title}" # {event_title} = Personal name + + +# Tax + steuer_report: + pattern: [ + { event_type: "TAX_REFUND", event_subtitle: "Steuerbuchung", event_title: "Steuerabrechnung", section_title: "Dokument", document_title: "Steuerabrechnung" }, + { event_type: "YEAR_END_TAX_REPORT", event_subtitle: "Jahr \\d+", event_title: "Jährlicher Steuerreport", section_title: "Dokument", document_title: "Steuerreport \\d+" }, + ] + path: "Steuern/" + filename: "{iso_date} {document_title}" + + + steuer_korrektur: + pattern: [ + { event_type: "ssp_tax_correction_invoice", event_subtitle: , event_title: "Steuerkorrektur", section_title: "Dokument", document_title: "Steuerabrechnung" }, + ] + path: "Steuern/" + filename: "{iso_date} {event_title}" + + +# Allgemeine Infos + infos1_wertpapiere: + pattern: [ + { event_type: "CUSTOMER_CREATED", event_subtitle: "Erhalten", section_title: "Dokumente", document_title: "Basisinformationen über Wertpapiere" }, + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Hinweise zu den Handelsplätzen" }, # same file as below + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Hinweise zu den Handelsplätzen" }, # same file as above + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Kundenvereinbarung" }, + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Kundenvereinbarung" }, + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Datenschutzinformationen" }, + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Datenschutzinformationen" }, + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Info zur Einlagensicherung" }, + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Info zur Einlagensicherung" }, + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Preis- und Leistungsverzeichnis" }, + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Preis- und Leistungsverzeichnis" }, + ] + path: "Infos/" + filename: "{iso_date} {document_title}" + + + infos2_crypto: + pattern: [ + { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Kundenvereinbarung" }, + { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Customer Agreement" }, + { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Mistrade Regelungen" }, + { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Risikohinweise Crypto" }, + { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Crypto Verwahrbedingungen" }, + { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Vorvertragliche Informationen" }, + { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Widerrufsbelehrung Cryptoverwahrer" }, + { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Datenschutzinformationen Cryptoverwahrer" }, + ] + path: "Infos/" + filename: "{iso_date} {document_title}" + + + infos3_optionen_etfs: + pattern: [ + { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Basisinformationsblatt" }, # ohne limit gekauft + { event_type: "GESH_CORPORATE_ACTION", event_subtitle: "Unternehmensmeldung", section_title: "Dokumente", document_title: "Kundenanschreiben" }, + { event_type: "GESH_CORPORATE_ACTION_MULTIPLE_POSITIONS", event_subtitle: "Gesellschaftshinweis", section_title: "Dokumente", document_title: "Kundenanschreiben \\d" }, + ] + path: "Infos/" + filename: "{iso_date} {document_title} - {event_title}" + + karte_bestellt: + pattern: [ + { event_type: "card_order_billed", event_subtitle: , event_title: "Trade Republic Card", section_title: "Dokumente", document_title: "Bestellung Trade Republic Karte" }, + ] + path: "Infos/" + filename: "{iso_date} {document_title}" + + +# enthalten keine Dokumente, werden übersprungen: +# +# uberweisung_ankommend: +# pattern: [ +# { event_type: "PAYMENT_INBOUND", event_subtitle: , section_title: , document_title: }, +# { event_type: "INCOMING_TRANSFER", event_subtitle: "Erhalten", section_title: , document_title: }, +# ] +# path: "Girokonto/" +# +# uberweisung_ausgehend: +# pattern: [ +# { event_type: "PAYMENT_OUTBOUND", event_subtitle: , section_title: , document_title: }, +# ] +# path: "Girokonto/" +# +# karte_geld_erhalten: +# pattern: [ +# { event_type: "card_refund", event_subtitle: , section_title: , document_title: }, +# ] +# path: "Girokonto/" +# +# karte_bezahlt: +# pattern: [ +# { event_type: "card_successful_transaction", event_subtitle: , section_title: , document_title: }, +# ] +# path: "Girokonto/" +# +# karte_geld_abheben: +# pattern: [ +# { event_type: "card_successful_atm_withdrawal", event_subtitle: , event_title: "Abhebung", section_title: , document_title: }, +# ] +# path: "Girokonto/" +# +# +# karte_transaktions_fehler: +# pattern: [ +# { event_type: "card_failed_transaction", event_subtitle: "Abgebrochen", section_title: , document_title: }, +# ] +# path: "Girokonto/" +# +# karte_verifikation_erfolgreich: +# pattern: [ +# { event_type: "card_successful_verification", event_subtitle: , section_title: , document_title: }, +# ] +# path: "Girokonto/" +# +# neue_iban: +# pattern: [ +# { event_type: "new_tr_iban", event_subtitle: , section_title: , document_title: }, +# ] +# path: "Girokonto/" +# +# order_abgelehnt: +# pattern: [ +# { event_type: "ORDER_REJECTED", event_subtitle: "Kauforder abgelehnt", section_title: , document_title: }, +# ] +# path: "Wertpapiere/Order storno/{iso_date_year}/" +# +# +# order_abgelehnt: +# pattern: [ +# { event_type: "ORDER_EXPIRED", event_subtitle: "Kauforder abgelaufen", section_title: , document_title: }, +# ] +# path: "Wertpapiere/Order storno/{iso_date_year}/" +# \ No newline at end of file diff --git a/pytr/dl.py b/pytr/dl.py index 233afe6b..c6a09be4 100644 --- a/pytr/dl.py +++ b/pytr/dl.py @@ -1,22 +1,24 @@ -import re +import os from concurrent.futures import as_completed from pathlib import Path from requests_futures.sessions import FuturesSession from requests import session +from datetime import datetime from pathvalidate import sanitize_filepath from pytr.utils import preview, get_logger from pytr.api import TradeRepublicError from pytr.timeline import Timeline +from pytr.file_destination_provider import FileDestinationProvider class DL: def __init__( self, tr, output_path, - filename_fmt, + file_destination_provider:FileDestinationProvider, since_timestamp=0, history_file='pytr_history', max_workers=8, @@ -25,13 +27,13 @@ def __init__( ''' tr: api object output_path: name of the directory where the downloaded files are saved - filename_fmt: format string to customize the file names + file_destination_provider: The destination provider for the file path and file names based on the event type and other parameters. since_timestamp: downloaded files since this date (unix timestamp) ''' self.tr = tr self.output_path = Path(output_path) self.history_file = self.output_path / history_file - self.filename_fmt = filename_fmt + self.file_destination_provider = file_destination_provider self.since_timestamp = since_timestamp self.universal_filepath = universal_filepath @@ -83,75 +85,37 @@ async def dl_loop(self): else: self.log.warning(f"unmatched subscription of type '{subscription['type']}':\n{preview(response)}") - def dl_doc(self, doc, titleText, subtitleText, subfolder=None): + def dl_doc(self, doc, event_type: str, event_title: str, event_subtitle: str, section_title: str, timestamp: datetime): ''' send asynchronous request, append future with filepath to self.futures ''' doc_url = doc['action']['payload'] - if subtitleText is None: - subtitleText = '' - - try: - date = doc['detail'] - iso_date = '-'.join(date.split('.')[::-1]) - except KeyError: - date = '' - iso_date = '' - doc_id = doc['id'] - - # extract time from subtitleText - try: - time = re.findall('um (\\d+:\\d+) Uhr', subtitleText) - if time == []: - time = '' - else: - time = f' {time[0]}' - except TypeError: - time = '' - - if subfolder is not None: - directory = self.output_path / subfolder - else: - directory = self.output_path - - # If doc_type is something like 'Kosteninformation 2', then strip the 2 and save it in doc_type_num - doc_type = doc['title'].rsplit(' ') - if doc_type[-1].isnumeric() is True: - doc_type_num = f' {doc_type.pop()}' - else: - doc_type_num = '' - - doc_type = ' '.join(doc_type) - titleText = titleText.replace('\n', '').replace('/', '-') - subtitleText = subtitleText.replace('\n', '').replace('/', '-') - - filename = self.filename_fmt.format( - iso_date=iso_date, time=time, title=titleText, subtitle=subtitleText, doc_num=doc_type_num, id=doc_id - ) - - filename_with_doc_id = filename + f' ({doc_id})' - - if doc_type in ['Kontoauszug', 'Depotauszug']: - filepath = directory / 'Abschlüsse' / f'{filename}' / f'{doc_type}.pdf' - filepath_with_doc_id = directory / 'Abschlüsse' / f'{filename_with_doc_id}' / f'{doc_type}.pdf' - else: - filepath = directory / doc_type / f'{filename}.pdf' - filepath_with_doc_id = directory / doc_type / f'{filename_with_doc_id}.pdf' + document_title = doc.get('title', '') + + + variables = {} + variables['iso_date'] = timestamp.strftime('%Y-%m-%d') + variables['iso_date_year'] = timestamp.strftime('%Y') + variables['iso_date_month'] = timestamp.strftime('%m') + variables['iso_date_day'] = timestamp.strftime('%d') + variables['iso_time'] = timestamp.strftime('%H-%M') + + filepath = self.file_destination_provider.get_file_path(event_type, event_title, event_subtitle, section_title, document_title, variables) + if filepath.endswith('.pdf') is False: + filepath = f'{filepath}.pdf' + + filepath = Path(os.path.join( self.output_path , filepath)) if self.universal_filepath: filepath = sanitize_filepath(filepath, '_', 'universal') - filepath_with_doc_id = sanitize_filepath(filepath_with_doc_id, '_', 'universal') else: filepath = sanitize_filepath(filepath, '_', 'auto') - filepath_with_doc_id = sanitize_filepath(filepath_with_doc_id, '_', 'auto') + if filepath in self.filepaths: - self.log.debug(f'File {filepath} already in queue. Append document id {doc_id}...') - if filepath_with_doc_id in self.filepaths: - self.log.debug(f'File {filepath_with_doc_id} already in queue. Skipping...') - return - else: - filepath = filepath_with_doc_id + self.log.debug(f'File {filepath} already in queue. Skipping...') + return + doc['local filepath'] = str(filepath) self.filepaths.append(filepath) diff --git a/pytr/file_destination_provider.py b/pytr/file_destination_provider.py new file mode 100644 index 00000000..10e5287a --- /dev/null +++ b/pytr/file_destination_provider.py @@ -0,0 +1,185 @@ +import os +import re +import shutil +import pytr.config + +from importlib_resources import files +from yaml import safe_load +from pathlib import Path +from pytr.app_path import * +from pytr.utils import get_logger + +# ToDo Question if we want to use LibYAML which is faster than pure Python version but another dependency +try: + from yaml import CLoader as Loader, CDumper as Dumper +except ImportError: + from yaml import Loader, Dumper + + +ALL_CONFIG = "all" +UNKNOWN_CONFIG = "unknown" + +TEMPLATE_FILE_NAME ="file_destination_config__template.yaml" + + +class DefaultFormateValue(dict): + def __missing__(self, key): + return key.join("{}") + + +class DestinationConfig: + def __init__(self, config_name: str, filename: str, path: str = None, pattern: list = None): + self.config_name = config_name + self.filename = filename + self.path = path + self.pattern = pattern + + +class Pattern: + def __init__(self, event_type: str, event_subtitle: str, event_title: str, section_title: str, document_title: str): + self.event_type = event_type + self.event_subtitle = event_subtitle + self.event_title = event_title + self.section_title = section_title + self.document_title = document_title + + +class FileDestinationProvider: + + def __init__(self): + ''' + A provider for file path and file names based on the event type and other parameters. + ''' + self._log = get_logger(__name__) + + config_file_path = Path(DESTINATION_CONFIG_FILE) + if config_file_path.is_file() == False: + self.__create_default_config(config_file_path) + + config_file = open(config_file_path, "r", encoding="utf8") + destination_config = safe_load(config_file) + + self.__validate_config(destination_config) + + destinations = destination_config["destination"] + + self._destination_configs: list[DestinationConfig] = [] + + for config_name in destinations: + if config_name == ALL_CONFIG: + self._all_file_config = DestinationConfig( + ALL_CONFIG, destinations[ALL_CONFIG]["filename"]) + elif config_name == UNKNOWN_CONFIG: + self._unknown_file_config = DestinationConfig( + UNKNOWN_CONFIG, destinations[UNKNOWN_CONFIG]["filename"], destinations[UNKNOWN_CONFIG]["path"]) + else: + patterns = self.__extract_pattern( + destinations[config_name].get("pattern", None)) + self._destination_configs.append(DestinationConfig( + config_name, destinations[config_name].get("filename", None), destinations[config_name].get("path", None), patterns)) + + def get_file_path(self, event_type: str, event_title: str, event_subtitle: str, section_title: str, document_title: str, variables: dict) -> str: + ''' + Get the file path based on the event type and other parameters. + + Parameters: + event_type (str): The event type + event_title (str): The event title + event_subtitle (str): The event subtitle + section_title (str): The section title + document_title (str): The document title + variables (dict): The variables->value dict to be used in the file path and file name format. + ''' + + matching_configs = self._destination_configs.copy() + + # Maybe this can be improved looks like a lot of code duplication ... on the other hand using a + # dict for the parameters for example and iterate over it would make it harder to understand + if event_type is not None: + matching_configs = list(filter(lambda config: self.__is_matching_config( + config, "event_type", event_type), matching_configs)) + variables["event_type"] = event_type + + if event_title is not None: + matching_configs = list(filter(lambda config: self.__is_matching_config( + config, "event_title", event_title), matching_configs)) + variables["event_title"] = event_title + + if event_subtitle is not None: + matching_configs = list(filter(lambda config: self.__is_matching_config( + config, "event_subtitle", event_subtitle), matching_configs)) + variables["event_subtitle"] = event_subtitle + + if section_title is not None: + matching_configs = list(filter(lambda config: self.__is_matching_config( + config, "section_title", section_title), matching_configs)) + variables["section_title"] = section_title + + if document_title is not None: + matching_configs = list(filter(lambda config: self.__is_matching_config( + config, "document_title", document_title), matching_configs)) + variables["document_title"] = document_title + + if len(matching_configs) == 0: + self._log.debug( + f"No destination config found for the given parameters: event_type:{event_type}, event_title:{event_title},event_subtitle:{event_subtitle},section_title:{section_title},document_title:{document_title}") + return self.__create_file_path(self._unknown_file_config, variables) + + if len(matching_configs) > 1: + self._log.debug(f"Multiple Destination Patterns where found. Using 'unknown' config! Parameter: event_type:{event_type}, event_title:{event_title},event_subtitle:{event_subtitle},section_title:{section_title},document_title:{document_title}") + return self.__create_file_path(self._unknown_file_config, variables) + + return self.__create_file_path(matching_configs[0], variables) + + def __is_matching_config(self, config: DestinationConfig, key: str, value: str): + for pattern in config.pattern: + attribute = getattr(pattern, key) + if attribute is None or re.match(attribute, value): + return True + + return False + + def __create_file_path(self, config: DestinationConfig, variables: dict): + formate_variables = DefaultFormateValue(variables) + + path = config.path + filename = config.filename + if filename is None: + filename = self._all_file_config.filename + + return os.path.join(path, filename).format_map(formate_variables) + + def __extract_pattern(self, pattern_config: list) -> list: + patterns = [] + for pattern in pattern_config: + patterns.append(Pattern(pattern.get("event_type", None), + pattern.get("event_subtitle", None), + pattern.get("event_title", None), + pattern.get("section_title", None), + pattern.get("document_title", None))) + + return patterns + + def __validate_config(self, destination_config: dict): + if "destination" not in destination_config: + raise ValueError("'destination' key not found in config file") + + destinations = destination_config["destination"] + + # Check if default config is present + if ALL_CONFIG not in destinations or "filename" not in destinations[ALL_CONFIG]: + raise ValueError( + "'all' config not found or filename not not present in default config") + + if UNKNOWN_CONFIG not in destinations or "filename" not in destinations[UNKNOWN_CONFIG] or "path" not in destinations[UNKNOWN_CONFIG]: + raise ValueError( + "'unknown' config not found or filename/path not not present in unknown config") + + for config_name in destinations: + if config_name != ALL_CONFIG and "path" not in destinations[config_name]: + raise ValueError( + f"'{config_name}' has no path defined in destination config") + + def __create_default_config(self, config_file_path: Path): + path = files(pytr.config).joinpath(TEMPLATE_FILE_NAME) + shutil.copyfile(path, config_file_path) diff --git a/pytr/main.py b/pytr/main.py index b642f227..3c1c8c62 100644 --- a/pytr/main.py +++ b/pytr/main.py @@ -17,6 +17,7 @@ from pytr.portfolio import Portfolio from pytr.alarms import Alarms from pytr.details import Details +from pytr.file_destination_provider import FileDestinationProvider def get_main_parser(): @@ -80,12 +81,7 @@ def formatter(prog): ) parser_dl_docs.add_argument('output', help='Output directory', metavar='PATH', type=Path) - parser_dl_docs.add_argument( - '--format', - help='available variables:\tiso_date, time, title, doc_num, subtitle, id', - metavar='FORMAT_STRING', - default='{iso_date}{time} {title}{doc_num}', - ) + parser_dl_docs.add_argument( '--last_days', help='Number of last days to include (use 0 get all days)', metavar='DAYS', default=0, type=int ) @@ -200,10 +196,11 @@ def main(): since_timestamp = 0 else: since_timestamp = (datetime.now().astimezone() - timedelta(days=args.last_days)).timestamp() + dl = DL( login(phone_no=args.phone_no, pin=args.pin, web=not args.applogin), args.output, - args.format, + FileDestinationProvider(), since_timestamp=since_timestamp, max_workers=args.workers, universal_filepath=args.universal, diff --git a/pytr/timeline.py b/pytr/timeline.py index 250a28c3..f809023a 100644 --- a/pytr/timeline.py +++ b/pytr/timeline.py @@ -129,14 +129,7 @@ async def process_timelineDetail(self, response, dl): + f"{event['title']} -- {event['subtitle']} - {event['timestamp'][:19]}" ) - subfolder = { - 'benefits_saveback_execution': 'Saveback', - 'benefits_spare_change_execution': 'RoundUp', - 'ssp_corporate_action_invoice_cash': 'Dividende', - 'CREDIT': 'Dividende', - 'INTEREST_PAYOUT_CREATED': 'Zinsen', - "SAVINGS_PLAN_EXECUTED":'Sparplan' - }.get(event["eventType"]) + event['has_docs'] = False for section in response['sections']: @@ -149,10 +142,7 @@ async def process_timelineDetail(self, response, dl): except (ValueError, KeyError): timestamp = datetime.now().timestamp() if self.max_age_timestamp == 0 or self.max_age_timestamp < timestamp: - title = f"{doc['title']} - {event['title']}" - if event['eventType'] in ["ACCOUNT_TRANSFER_INCOMING", "ACCOUNT_TRANSFER_OUTGOING", "CREDIT"]: - title += f" - {event['subtitle']}" - dl.dl_doc(doc, title, doc.get('detail'), subfolder) + dl.dl_doc(doc, event['eventType'], event['title'], event['subtitle'], section['title'], datetime.fromisoformat(event['timestamp'][:19])) if event['has_docs']: self.events_with_docs.append(event) From 07dff413726f5497f56634e56f66ba81f71a633d Mon Sep 17 00:00:00 2001 From: bjbe Date: Fri, 26 Jul 2024 17:16:07 +0200 Subject: [PATCH 02/11] Improved destination provider - used new yaml structure - improved regex with full match - removed invalid chars from variables used for path/filename replacement - some smaller bugfixes --- README.md | 13 + pytr/api.py | 2 +- .../file_destination_config__template.yaml | 278 ++++++++++-------- pytr/dl.py | 42 ++- pytr/file_destination_provider.py | 63 ++-- pytr/main.py | 5 +- 6 files changed, 234 insertions(+), 169 deletions(-) diff --git a/README.md b/README.md index 1035c3e0..3dd36200 100644 --- a/README.md +++ b/README.md @@ -84,3 +84,16 @@ $ pytr login --phone_no +49123456789 --pin 1234 ``` If no arguments are supplied pytr will look for them in the file `~/.pytr/credentials` (the first line must contain the phone number, the second line the pin). If the file doesn't exist pytr will ask for for the phone number and pin. + +## Location and File names of the downloaded Documents +During the first run of the 'dl_docs' command a config file is created in the user home directory `/.pytr/file_destination_config.yaml`. +The file contains destination patterns which describe where the file should be located and how the file name should look like. If a event/document matches the defined pattern it will be located in that specific `path` with the specified `filename`. + +There are three mandatory patterns defined at the top: +* `default` - Defines only `filename` and is used for all other patterns if no "filename" is provided +* `unknown` - Defines `filename` and `path`, this is used when no match can be found for the event and the given document. +* `multiple_match` - If there are multiple matching patterns and the destination would be ambiguous, the document will be stored in the given `path` with the given `filename` + +The other pattern can be as you like but keep in mind that patterns `path` and `filenames` should result in unique document names. If you see something like this ` (some strange string)` the document path + name was not unique. + +> Its also possible to copy the configuration file from `~/pytr/config/file_destination_config__template.yaml` to `/.pytr/file_destination_config.yaml` and modify it before the first download to avoid that the download need to be performed a second time. \ No newline at end of file diff --git a/pytr/api.py b/pytr/api.py index b9879d07..4bfd84c8 100644 --- a/pytr/api.py +++ b/pytr/api.py @@ -38,7 +38,7 @@ from http.cookiejar import MozillaCookieJar from pytr.utils import get_logger -from pytr.app_path import * +from pytr.app_path import CREDENTIALS_FILE, COOKIES_FILE, KEY_FILE diff --git a/pytr/config/file_destination_config__template.yaml b/pytr/config/file_destination_config__template.yaml index 4a296a43..8c08d501 100644 --- a/pytr/config/file_destination_config__template.yaml +++ b/pytr/config/file_destination_config__template.yaml @@ -1,6 +1,6 @@ destination: # valid for all blocks without explicit filename - all: + default: filename: "{iso_date}.{iso_time} {event_title}" # {event_title} = Wertpapier-/ETF-/Produkt-Name @@ -10,8 +10,14 @@ destination: filename: "{iso_date}.{iso_time} {event_type} - {event_subtitle} - {document_title} - {event_title}" -# Aktien - aktien_kauf_abrechnung: +# if pattern found multiple times, use this block + multiple_match: + path: "MultipleMatchPattern/{section_title}/" + filename: "{iso_date}.{iso_time} {event_type} - {event_subtitle} - {document_title} - {event_title}" + + +# stocks + stock_order_settlement: pattern: [ { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit gekauft { event_type: "ORDER_EXECUTED", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit gekauft @@ -21,10 +27,10 @@ destination: { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", section_title: "Dokumente", document_title: "Abrechnung" }, # Aktiengeschenk { event_type: "SHAREBOOKING", event_subtitle: "Verkauf", section_title: "Dokumente", document_title: "Abrechnung \\d" }, # Kapitalmassnahme ] - path: "Wertpapiere/Abrechnung/{iso_date_year}/" + path: "Stocks/Settlement/{iso_date_year}/" - aktien_kauf_kosteninfo: + stock_order_cost_report: pattern: [ { event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", section_title: "Dokumente", document_title: "Kosteninformation" }, # limit erstellt { event_type: "ORDER_EXECUTED", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Kosteninformation" }, @@ -34,32 +40,32 @@ destination: { event_type: "TRADE_INVOICE", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Kosteninformation" }, { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", section_title: "Dokumente", document_title: "Kosteninformation" }, # Aktiengeschenk { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", section_title: "Dokumente", document_title: "Kosteninformation \\d" }, # Aktiengeschenk - { event_type: "EX_POST_COST_REPORT", event_subtitle: , event_title: "Ex-Post Kosteninformation", section_title: "Dokumente", document_title: "Ex-Post Kosteninformation \\d+" }, + { event_type: "EX_POST_COST_REPORT", event_title: "Ex-Post Kosteninformation", section_title: "Dokumente", document_title: "Ex-Post Kosteninformation \\d+" }, ] - path: "Wertpapiere/Kosteninfo/{iso_date_year}/" + path: "Stocks/Cost report/{iso_date_year}/" - aktien_kauf_limit_erstellt: + stock_order_created: pattern: [ + { event_type: "TRADE_INVOICE", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, { event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, { event_type: "ORDER_EXECUTED", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, { event_type: "ORDER_EXECUTED", event_subtitle: "Stop-Sell-Order", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, - { event_type: "TRADE_INVOICE", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, ] - path: "Wertpapiere/Order erstellt/{iso_date_year}/" + path: "Stocks/Order created/{iso_date_year}/" - aktien_limit_storno: + stock_order_canceled: pattern: [ - { event_type: "TRADE_CANCELED", event_subtitle: "Verkauf-Abrechnung storniert", section_title: "Dokumente", document_title: "Abrechnung \\d" }, { event_type: "ORDER_CANCELED", event_subtitle: "Limit-Buy-Order storniert", section_title: "Dokumente", document_title: "Löschbestätigung" }, + { event_type: "TRADE_CANCELED", event_subtitle: "Verkauf-Abrechnung storniert", section_title: "Dokumente", document_title: "Abrechnung \\d" }, { event_type: "SAVINGS_PLAN_CANCELED", event_subtitle: "Sparplan storniert", section_title: "Dokumente", document_title: "Stornierungsbestätigung" }, ] - path: "Wertpapiere/Order storno/{iso_date_year}/" + path: "Stocks/Order canceled/{iso_date_year}/" -# Kapitalmaßnahmen - info_anzeigen1: +# stocks Notice + stock_notice1: pattern: [ { event_type: "EXERCISE", event_subtitle: "Ausübung", section_title: "Dokumente", document_title: "Kundenanschreiben" }, { event_type: "SHAREBOOKING", event_subtitle: "Split", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, @@ -80,215 +86,227 @@ destination: { event_type: "SHAREBOOKING", event_subtitle: "Kapitalerhöhung gegen Bar", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, { event_type: "SHAREBOOKING", event_subtitle: "Kapitalerhöhung aus Gesellschaftsmitteln", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, ] - path: "Wertpapiere/Anzeigen/{iso_date_year}/" + path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" # split for better readability - info_anzeigen2: + stock_notice2: pattern: [ { event_type: "CORPORATE_ACTION", event_subtitle: "Kapitalherabsetzung", section_title: "Dokumente", document_title: "Abrechnung" }, { event_type: "SHAREBOOKING_CANCELED", event_subtitle: "Entflechtung storniert", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, { event_type: "SHAREBOOKING_CANCELED", event_subtitle: "Stockdividende storniert", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, { event_type: "SHAREBOOKING_CANCELED", event_subtitle: "Kapitalerhöhung gegen Bar storniert", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, ] - path: "Wertpapiere/Anzeigen/{iso_date_year}/" + path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" # split for better readability - info_anzeigen3: + stock_notice3: pattern: [ { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Fusion", section_title: "Dokumente", document_title: "Abrechnung" }, { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Reinvestierung", section_title: "Dokumente", document_title: "Abrechnung" }, { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Umtausch/Bezug", section_title: "Dokumente", document_title: "Abrechnung" }, { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Zwangsübernahme", section_title: "Dokumente", document_title: "Abrechnung" }, { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Kapitalreduktion", section_title: "Dokumente", document_title: "Abrechnung" }, - { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", section_title: "Dokumente", document_title: "Kundenanschreiben" }, #Kapitalerhöhung gegen Bar - { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", section_title: "Dokumente", document_title: "Kundenanschreiben \\d" }, #Kapitalerhöhung gegen Bar + # Capital increase in exchange for cash / Kapitalerhöhung gegen Bar + { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", section_title: "Dokumente", document_title: "Kundenanschreiben" }, + { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", section_title: "Dokumente", document_title: "Kundenanschreiben \\d" }, ] - path: "Wertpapiere/Anzeigen/{iso_date_year}/" + path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" # split for better readability - info_anzeigen4: + stock_notice4: pattern: [ - { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Spin-off", section_title: "Dokumente", document_title: "Dokumente" }, - { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Reverse Split", section_title: "Dokumente", document_title: "Dokumente" }, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Wechsel", section_title: "Dokumente", document_title: "Information" }, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Information", section_title: "Dokumente", document_title: "Information" }, + { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Spin-off", section_title: "Dokumente", document_title: "Dokumente" }, + { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Reverse Split", section_title: "Dokumente", document_title: "Dokumente" }, + # Recognition of subscription rights / Einbuchung Bezugsrechte + { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Zwischenvertrieb von Wertpapieren", section_title: "Dokumente", document_title: "Dokumente" }, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Wechsel", section_title: "Dokumente", document_title: "Information" }, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Information", section_title: "Dokumente", document_title: "Information" }, + # Repurchase of interim securities / Rückkauf von Zwischentiteln + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Aufruf von Zwischenpapieren", section_title: "Dokumente", document_title: "Kapitalmaßnahmen" }, ] - path: "Wertpapiere/Anzeigen/{iso_date_year}/" + path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" - portfolio_abschluess: + stock_report: pattern: [ - { event_type: "QUARTERLY_REPORT", event_subtitle: , event_title: "Q\\d/\\d+ Abschluss", section_title: "Dokumente", document_title: "Kontoauszug" }, - { event_type: "QUARTERLY_REPORT", event_subtitle: , event_title: "Q\\d/\\d+ Abschluss", section_title: "Dokumente", document_title: "Depotauszug" }, - { event_type: "QUARTERLY_REPORT", event_subtitle: , event_title: "Q\\d/\\d+ Abschluss", section_title: "Dokumente", document_title: "Cryptoauszug" }, + { event_type: "QUARTERLY_REPORT", event_title: "Q\\d/\\d+ Abschluss", section_title: "Dokumente", document_title: "Kontoauszug" }, + { event_type: "QUARTERLY_REPORT", event_title: "Q\\d/\\d+ Abschluss", section_title: "Dokumente", document_title: "Depotauszug" }, + { event_type: "QUARTERLY_REPORT", event_title: "Q\\d/\\d+ Abschluss", section_title: "Dokumente", document_title: "Cryptoauszug" }, ] - path: "Wertpapiere/Abschluss/{iso_date_year}/" + path: "Stocks/Report/{iso_date_year}/" filename: "{iso_date} {document_title} {event_title}" -# Dividenden - dividende_erhalten: +# General Meetings + stock_general_meetings: pattern: [ - { event_type: "CREDIT", event_subtitle: "Dividende", section_title: "Dokumente", document_title: "Abrechnung" }, - { event_type: "CREDIT", event_subtitle: "Ausschüttung", section_title: "Dokumente", document_title: "Abrechnung" }, - { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Bardividende", section_title: "Dokumente", document_title: "Dokumente" }, + { event_type: "GENERAL_MEETING", event_subtitle: "Hauptversammlung", section_title: "Dokumente", document_title: "Hauptversammlung" }, + { event_type: "GENERAL_MEETING", event_subtitle: "Hauptversammlung", section_title: "Dokumente", document_title: "Hauptversammlung \\d" }, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Jährliche Hauptversammlung", section_title: "Dokumente", document_title: "Information" }, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Außerordentliche oder spezielle Hauptversammlung", section_title: "Dokumente", document_title: "Information" }, ] - path: "Dividenden/{iso_date_year}/" + path: "Stocks/General Meetings/{iso_date_year}/" -# Dividenden Kapitalmaßnahme wahl - dividende_wahl: +# Savings plan + savings_plan: pattern: [ - { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", section_title: "Dokumente", document_title: "Dividende Wahlweise" }, - { event_type: "ssp_dividend_option_customer_instruction", event_subtitle: "Cash oder Aktie", section_title: "Dokumente", document_title: "Dividende Wahlweise" }, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Dividende Wahlweise", section_title: "Dokumente", document_title: "Kapitalmaßnahmen" }, + { event_type: "SAVINGS_PLAN_INVOICE_CREATED", event_subtitle: "Sparplan ausgeführt", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, + { event_type: "SAVINGS_PLAN_EXECUTED", event_subtitle: "Sparplan ausgeführt", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, ] - path: "Dividendenwahl/{iso_date_year}/" + path: "Stocks/Savings plan/{iso_date_year}/" -# Hauptversammlungen - hauptversammlungen: +# pre-determined tax base earning + stock_pre_earning_tax: pattern: [ - { event_type: "GENERAL_MEETING", event_subtitle: "Hauptversammlung", section_title: "Dokumente", document_title: "Hauptversammlung" }, - { event_type: "GENERAL_MEETING", event_subtitle: "Hauptversammlung", section_title: "Dokumente", document_title: "Hauptversammlung \\d" }, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Jährliche Hauptversammlung", section_title: "Dokumente", document_title: "Information" }, + { event_type: "PRE_DETERMINED_TAX_BASE_EARNING", event_subtitle: "Vorabpauschale", section_title: "Dokumente", document_title: "Vorabpauschale" }, ] - path: "Wertpapiere/Hauptversammlungen/{iso_date_year}/" + path: "Stocks/PreEarningTax/{iso_date_year}/" -# Sparplan - sparplan: +# Dividends + dividends_received: pattern: [ - { event_type: "SAVINGS_PLAN_INVOICE_CREATED", event_subtitle: "Sparplan ausgeführt", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, - { event_type: "SAVINGS_PLAN_EXECUTED", event_subtitle: "Sparplan ausgeführt", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, + { event_type: "CREDIT", event_subtitle: "Dividende", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "CREDIT", event_subtitle: "Ausschüttung", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Bardividende", section_title: "Dokumente", document_title: "Dokumente" }, + { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Dividende Wahlweise", section_title: "Dokumente", document_title: "Dokumente" }, + { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Bardividende korrigiert", section_title: "Dokumente", document_title: "Dokumente" }, + { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Reinvestition der Dividende", section_title: "Dokumente", document_title: "Dokumente" }, ] - path: "Wertpapiere/Sparplan/{iso_date_year}/" + path: "Dividends/{iso_date_year}/" -# Vorabpauschale - vorabpauschale: +# Dividends Corporate action election + dividends_election: pattern: [ - { event_type: "PRE_DETERMINED_TAX_BASE_EARNING", event_subtitle: "Vorabpauschale", section_title: "Dokumente", document_title: "Vorabpauschale" }, + { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", section_title: "Dokumente", document_title: "Dividende Wahlweise" }, + { event_type: "ssp_dividend_option_customer_instruction", event_subtitle: "Cash oder Aktie", section_title: "Dokumente", document_title: "Dividende Wahlweise" }, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Dividende Wahlweise", section_title: "Dokumente", document_title: "Kapitalmaßnahmen" }, ] - path: "Wertpapiere/Vorabpauschale/{iso_date_year}/" + path: "Dividendelection/{iso_date_year}/" -# Anleihen - anleihe_tilgung: +# bonds + bond_repayment: pattern: [ { event_type: "REPAYMENT", event_subtitle: "Tilgung", section_title: "Dokumente", document_title: "Abrechnung" }, ] - path: "Anleihen/Tilgungen/{iso_date_year}/" + path: "Bonds/{iso_date_year}/" + filename: "{iso_date}.{iso_time} Repayment {event_title}" - anleihe_zinsen: + bond_interest: pattern: [ { event_type: "COUPON_PAYMENT", event_subtitle: "Coupon Zahlung", section_title: "Dokumente", document_title: "Abrechnung" }, ] - path: "Anleihen/Zinsen/{iso_date_year}/" + path: "Bonds/{iso_date_year}/" + filename: "{iso_date}.{iso_time} Interest {event_title}" # Saveback - saveback_aktiviert: + saveback_enabled: pattern: [ { event_type: "benefits_saveback_execution", event_subtitle: "Saveback", section_title: "Dokumente", document_title: "Aktivierung" }, ] - path: "Saveback/aktiviert/" + path: "Saveback/Enabled/" - saveback_abrechnung: + saveback_executed: pattern: [ { event_type: "benefits_saveback_execution", event_subtitle: "Saveback", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, ] - path: "Saveback/Abrechnung/" + path: "Saveback/Report/" - saveback_kosteninfo: + saveback_cost_report: pattern: [ { event_type: "benefits_saveback_execution", event_subtitle: "Saveback", section_title: "Dokumente", document_title: "Kosteninformation" }, ] - path: "Saveback/Kosteninfo/" + path: "Saveback/Cost report/" # Round up - roundup_aktiviert: + roundup_enabled: pattern: [ - { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", section_title: "Dokumente", document_title: "Aktivierung" }, # same files - multiple times at once + # same files - multiple times at once + { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", section_title: "Dokumente", document_title: "Aktivierung" }, ] - path: "Roundup/aktiviert/{iso_date_year}/" + path: "Roundup/Enabled/" - roundup_ausgefuert: + roundup_executed: pattern: [ { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, ] - path: "Roundup/Abrechnung/{iso_date_year}/" + path: "Roundup/Report/{iso_date_year}/" - roundup_kosteninfo: + roundup_cost_report: pattern: [ { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", section_title: "Dokumente", document_title: "Kosteninformation" }, ] - path: "Roundup/Kosteninfo/{iso_date_year}/" + path: "Roundup/Cost report/{iso_date_year}/" -# Konto - zinsen: +# account + cash_interest: pattern: [ - { event_type: "INTEREST_PAYOUT", event_subtitle: , event_title: "Zinsen", section_title: "Dokument", document_title: "Abrechnung" }, - { event_type: "INTEREST_PAYOUT_CREATED", event_subtitle: , event_title: "Zinsen", section_title: "Dokument", document_title: "Abrechnung" }, + { event_type: "INTEREST_PAYOUT", event_title: "Zinsen", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "INTEREST_PAYOUT_CREATED", event_title: "Zinsen", section_title: "Dokumente", document_title: "Abrechnung" }, ] - path: "Zinsen/" + path: "Cash Interest/" filename: "{iso_date} Zinsabrechnung" - uberweisung_ankommend: + cash_transfer_report: pattern: [ { event_type: "INCOMING_TRANSFER", event_subtitle: "Erhalten", section_title: "Dokument", document_title: "Transaktionsbestätigung" }, ] - path: "Konto/" + path: "Cash Report/" filename: "{iso_date}.{iso_time} {document_title}" # {event_title} = Personal name -# Tax - steuer_report: +# annual tax report for account + account_tax_report: pattern: [ - { event_type: "TAX_REFUND", event_subtitle: "Steuerbuchung", event_title: "Steuerabrechnung", section_title: "Dokument", document_title: "Steuerabrechnung" }, - { event_type: "YEAR_END_TAX_REPORT", event_subtitle: "Jahr \\d+", event_title: "Jährlicher Steuerreport", section_title: "Dokument", document_title: "Steuerreport \\d+" }, + { event_type: "TAX_REFUND", event_subtitle: "Steuerbuchung", event_title: "Steuerabrechnung", section_title: "Dokumente", document_title: "Steuerabrechnung" }, + { event_type: "YEAR_END_TAX_REPORT", event_subtitle: "Jahr \\d+", event_title: "Jährlicher Steuerreport", section_title: "Dokument", document_title: "Steuerreport \\d+" }, ] - path: "Steuern/" + path: "Tax/" filename: "{iso_date} {document_title}" - steuer_korrektur: + account_tax_adjustment: pattern: [ - { event_type: "ssp_tax_correction_invoice", event_subtitle: , event_title: "Steuerkorrektur", section_title: "Dokument", document_title: "Steuerabrechnung" }, + { event_type: "ssp_tax_correction_invoice", event_title: "Steuerkorrektur", section_title: "Dokumente", document_title: "Steuerabrechnung" }, ] - path: "Steuern/" + path: "Tax/" filename: "{iso_date} {event_title}" -# Allgemeine Infos - infos1_wertpapiere: +# common informations + notice_stocks: pattern: [ { event_type: "CUSTOMER_CREATED", event_subtitle: "Erhalten", section_title: "Dokumente", document_title: "Basisinformationen über Wertpapiere" }, - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Hinweise zu den Handelsplätzen" }, # same file as below - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Hinweise zu den Handelsplätzen" }, # same file as above - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Kundenvereinbarung" }, - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Kundenvereinbarung" }, + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Kundenvereinbarung" }, # same file as below + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Kundenvereinbarung" }, # same file as above { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Datenschutzinformationen" }, { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Datenschutzinformationen" }, { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Info zur Einlagensicherung" }, { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Info zur Einlagensicherung" }, + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Hinweise zu den Handelsplätzen" }, + { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Hinweise zu den Handelsplätzen" }, { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Preis- und Leistungsverzeichnis" }, { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Preis- und Leistungsverzeichnis" }, ] - path: "Infos/" + path: "Notice/" filename: "{iso_date} {document_title}" - infos2_crypto: + notice_crypto: pattern: [ { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Kundenvereinbarung" }, { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Customer Agreement" }, @@ -299,89 +317,91 @@ destination: { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Widerrufsbelehrung Cryptoverwahrer" }, { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Datenschutzinformationen Cryptoverwahrer" }, ] - path: "Infos/" + path: "Notice/" filename: "{iso_date} {document_title}" - infos3_optionen_etfs: + notice_warrant_and_etfs: pattern: [ - { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Basisinformationsblatt" }, # ohne limit gekauft + # when you buy a product for the first time, you will receive information documents about this product + { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Basisinformationsblatt" }, { event_type: "GESH_CORPORATE_ACTION", event_subtitle: "Unternehmensmeldung", section_title: "Dokumente", document_title: "Kundenanschreiben" }, { event_type: "GESH_CORPORATE_ACTION_MULTIPLE_POSITIONS", event_subtitle: "Gesellschaftshinweis", section_title: "Dokumente", document_title: "Kundenanschreiben \\d" }, ] - path: "Infos/" + path: "Notice/" filename: "{iso_date} {document_title} - {event_title}" - karte_bestellt: + + card_ordered: pattern: [ - { event_type: "card_order_billed", event_subtitle: , event_title: "Trade Republic Card", section_title: "Dokumente", document_title: "Bestellung Trade Republic Karte" }, + { event_type: "card_order_billed", event_title: "Trade Republic Card", section_title: "Dokumente", document_title: "Bestellung Trade Republic Karte" }, ] - path: "Infos/" + path: "Notice/" filename: "{iso_date} {document_title}" -# enthalten keine Dokumente, werden übersprungen: +# The following events do not contain any documents, these nodes are skipped. # -# uberweisung_ankommend: +# payment_inbound: # pattern: [ # { event_type: "PAYMENT_INBOUND", event_subtitle: , section_title: , document_title: }, # { event_type: "INCOMING_TRANSFER", event_subtitle: "Erhalten", section_title: , document_title: }, # ] -# path: "Girokonto/" +# path: "Cash Report/" # -# uberweisung_ausgehend: +# payment_outbout: # pattern: [ # { event_type: "PAYMENT_OUTBOUND", event_subtitle: , section_title: , document_title: }, # ] -# path: "Girokonto/" +# path: "Cash Report/" # -# karte_geld_erhalten: +# card_refund: # pattern: [ # { event_type: "card_refund", event_subtitle: , section_title: , document_title: }, # ] -# path: "Girokonto/" +# path: "Cash Report/" # -# karte_bezahlt: +# card_successful_transaction: # pattern: [ # { event_type: "card_successful_transaction", event_subtitle: , section_title: , document_title: }, # ] -# path: "Girokonto/" +# path: "Cash Report/" # -# karte_geld_abheben: +# card_successful_atm_withdrawal: # pattern: [ # { event_type: "card_successful_atm_withdrawal", event_subtitle: , event_title: "Abhebung", section_title: , document_title: }, # ] -# path: "Girokonto/" +# path: "Cash Report/" # # -# karte_transaktions_fehler: +# card_failed_transaction: # pattern: [ # { event_type: "card_failed_transaction", event_subtitle: "Abgebrochen", section_title: , document_title: }, # ] -# path: "Girokonto/" +# path: "Cash Report/" # -# karte_verifikation_erfolgreich: +# card_successful_verification: # pattern: [ # { event_type: "card_successful_verification", event_subtitle: , section_title: , document_title: }, # ] -# path: "Girokonto/" +# path: "Cash Report/" # -# neue_iban: +# new_account_iban: # pattern: [ # { event_type: "new_tr_iban", event_subtitle: , section_title: , document_title: }, # ] -# path: "Girokonto/" +# path: "Cash Report/" # -# order_abgelehnt: +# stock_order_rejected: # pattern: [ # { event_type: "ORDER_REJECTED", event_subtitle: "Kauforder abgelehnt", section_title: , document_title: }, # ] -# path: "Wertpapiere/Order storno/{iso_date_year}/" +# path: "Stocks/Order canceled/{iso_date_year}/" # # -# order_abgelehnt: +# stock_order_expired: # pattern: [ # { event_type: "ORDER_EXPIRED", event_subtitle: "Kauforder abgelaufen", section_title: , document_title: }, # ] -# path: "Wertpapiere/Order storno/{iso_date_year}/" +# path: "Stocks/Order canceled/{iso_date_year}/" # \ No newline at end of file diff --git a/pytr/dl.py b/pytr/dl.py index c6a09be4..31708d06 100644 --- a/pytr/dl.py +++ b/pytr/dl.py @@ -18,7 +18,6 @@ def __init__( self, tr, output_path, - file_destination_provider:FileDestinationProvider, since_timestamp=0, history_file='pytr_history', max_workers=8, @@ -27,13 +26,12 @@ def __init__( ''' tr: api object output_path: name of the directory where the downloaded files are saved - file_destination_provider: The destination provider for the file path and file names based on the event type and other parameters. since_timestamp: downloaded files since this date (unix timestamp) ''' self.tr = tr self.output_path = Path(output_path) self.history_file = self.output_path / history_file - self.file_destination_provider = file_destination_provider + self.file_destination_provider = self.__get_file_destination_provider() self.since_timestamp = since_timestamp self.universal_filepath = universal_filepath @@ -90,31 +88,46 @@ def dl_doc(self, doc, event_type: str, event_title: str, event_subtitle: str, s send asynchronous request, append future with filepath to self.futures ''' doc_url = doc['action']['payload'] - document_title = doc.get('title', '') + document_title = doc.get('title', '') + doc_id = doc['id'] - variables = {} variables['iso_date'] = timestamp.strftime('%Y-%m-%d') variables['iso_date_year'] = timestamp.strftime('%Y') variables['iso_date_month'] = timestamp.strftime('%m') variables['iso_date_day'] = timestamp.strftime('%d') variables['iso_time'] = timestamp.strftime('%H-%M') - - filepath = self.file_destination_provider.get_file_path(event_type, event_title, event_subtitle, section_title, document_title, variables) - if filepath.endswith('.pdf') is False: - filepath = f'{filepath}.pdf' - filepath = Path(os.path.join( self.output_path , filepath)) + filepath = self.file_destination_provider.get_file_path( + event_type, event_title, event_subtitle, section_title, document_title, variables) + # Just in case someone defines file names with extension + if filepath.endswith('.pdf') is True: + filepath = filepath[:-4] + + filepath_with_doc_id = f'{filepath} ({doc_id})' + + filepath = f'{filepath}.pdf' + filepath_with_doc_id = f'{filepath_with_doc_id}.pdf' + + filepath = Path(os.path.join(self.output_path, filepath)) + filepath_with_doc_id = Path(os.path.join(self.output_path, filepath_with_doc_id)) if self.universal_filepath: filepath = sanitize_filepath(filepath, '_', 'universal') + filepath_with_doc_id = sanitize_filepath(filepath_with_doc_id, '_', 'universal') else: filepath = sanitize_filepath(filepath, '_', 'auto') - + filepath_with_doc_id = sanitize_filepath(filepath_with_doc_id, '_', 'auto') if filepath in self.filepaths: - self.log.debug(f'File {filepath} already in queue. Skipping...') - return + self.log.debug( + f'File {filepath} already in queue. Append document id {doc_id}...') + if filepath_with_doc_id in self.filepaths: + self.log.debug( + f'File {filepath_with_doc_id} already in queue. Skipping...') + return + else: + filepath = filepath_with_doc_id doc['local filepath'] = str(filepath) self.filepaths.append(filepath) @@ -168,3 +181,6 @@ def work_responses(self): if self.done == len(self.doc_urls): self.log.info('Done.') exit(0) + + def __get_file_destination_provider(self): + return FileDestinationProvider() diff --git a/pytr/file_destination_provider.py b/pytr/file_destination_provider.py index 10e5287a..0a86b97a 100644 --- a/pytr/file_destination_provider.py +++ b/pytr/file_destination_provider.py @@ -6,21 +6,29 @@ from importlib_resources import files from yaml import safe_load from pathlib import Path -from pytr.app_path import * +from pytr.app_path import DESTINATION_CONFIG_FILE from pytr.utils import get_logger -# ToDo Question if we want to use LibYAML which is faster than pure Python version but another dependency -try: - from yaml import CLoader as Loader, CDumper as Dumper -except ImportError: - from yaml import Loader, Dumper - -ALL_CONFIG = "all" +DEFAULT_CONFIG = "default" UNKNOWN_CONFIG = "unknown" +MULTIPLE_MATCH_CONFIG = "multiple_match" TEMPLATE_FILE_NAME ="file_destination_config__template.yaml" +# Invalid characters translation table, for cleaning up the variables before using them. +# This was done to avoid issues with for example 'event_subtitle: “Umtausch/Bezug”' which caused a directory which was unintentional. +INVALID_CHARS_TRANSLATION_TABLE = str.maketrans({ + '"': '', + '?': '', + '<': '', + '>': '', + '*': '', + '|': '-', + '/': '-', + '\\': '-' +}) + class DefaultFormateValue(dict): def __missing__(self, key): @@ -66,12 +74,15 @@ def __init__(self): self._destination_configs: list[DestinationConfig] = [] for config_name in destinations: - if config_name == ALL_CONFIG: - self._all_file_config = DestinationConfig( - ALL_CONFIG, destinations[ALL_CONFIG]["filename"]) + if config_name == DEFAULT_CONFIG: + self._default_file_config = DestinationConfig( + DEFAULT_CONFIG, destinations[DEFAULT_CONFIG]["filename"]) elif config_name == UNKNOWN_CONFIG: self._unknown_file_config = DestinationConfig( UNKNOWN_CONFIG, destinations[UNKNOWN_CONFIG]["filename"], destinations[UNKNOWN_CONFIG]["path"]) + elif config_name == MULTIPLE_MATCH_CONFIG: + self._multiple_match_file_config = DestinationConfig( + MULTIPLE_MATCH_CONFIG, destinations[MULTIPLE_MATCH_CONFIG]["filename"], destinations[MULTIPLE_MATCH_CONFIG]["path"]) else: patterns = self.__extract_pattern( destinations[config_name].get("pattern", None)) @@ -98,27 +109,27 @@ def get_file_path(self, event_type: str, event_title: str, event_subtitle: str, if event_type is not None: matching_configs = list(filter(lambda config: self.__is_matching_config( config, "event_type", event_type), matching_configs)) - variables["event_type"] = event_type + variables["event_type"] = event_type.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() if event_title is not None: matching_configs = list(filter(lambda config: self.__is_matching_config( config, "event_title", event_title), matching_configs)) - variables["event_title"] = event_title + variables["event_title"] = event_title.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() if event_subtitle is not None: matching_configs = list(filter(lambda config: self.__is_matching_config( config, "event_subtitle", event_subtitle), matching_configs)) - variables["event_subtitle"] = event_subtitle + variables["event_subtitle"] = event_subtitle.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() if section_title is not None: matching_configs = list(filter(lambda config: self.__is_matching_config( config, "section_title", section_title), matching_configs)) - variables["section_title"] = section_title + variables["section_title"] = section_title.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() if document_title is not None: matching_configs = list(filter(lambda config: self.__is_matching_config( config, "document_title", document_title), matching_configs)) - variables["document_title"] = document_title + variables["document_title"] = document_title.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() if len(matching_configs) == 0: self._log.debug( @@ -126,15 +137,15 @@ def get_file_path(self, event_type: str, event_title: str, event_subtitle: str, return self.__create_file_path(self._unknown_file_config, variables) if len(matching_configs) > 1: - self._log.debug(f"Multiple Destination Patterns where found. Using 'unknown' config! Parameter: event_type:{event_type}, event_title:{event_title},event_subtitle:{event_subtitle},section_title:{section_title},document_title:{document_title}") - return self.__create_file_path(self._unknown_file_config, variables) + self._log.debug(f"Multiple Destination Patterns where found. Using 'multiple_match' config! Parameter: event_type:{event_type}, event_title:{event_title},event_subtitle:{event_subtitle},section_title:{section_title},document_title:{document_title}") + return self.__create_file_path(self._multiple_match_file_config, variables) return self.__create_file_path(matching_configs[0], variables) def __is_matching_config(self, config: DestinationConfig, key: str, value: str): for pattern in config.pattern: attribute = getattr(pattern, key) - if attribute is None or re.match(attribute, value): + if attribute is None or re.fullmatch(attribute, value): return True return False @@ -145,7 +156,7 @@ def __create_file_path(self, config: DestinationConfig, variables: dict): path = config.path filename = config.filename if filename is None: - filename = self._all_file_config.filename + filename = self._default_file_config.filename return os.path.join(path, filename).format_map(formate_variables) @@ -167,16 +178,20 @@ def __validate_config(self, destination_config: dict): destinations = destination_config["destination"] # Check if default config is present - if ALL_CONFIG not in destinations or "filename" not in destinations[ALL_CONFIG]: + if DEFAULT_CONFIG not in destinations or "filename" not in destinations[DEFAULT_CONFIG]: raise ValueError( - "'all' config not found or filename not not present in default config") + "'default' config not found or filename is not present in 'default' config") if UNKNOWN_CONFIG not in destinations or "filename" not in destinations[UNKNOWN_CONFIG] or "path" not in destinations[UNKNOWN_CONFIG]: raise ValueError( - "'unknown' config not found or filename/path not not present in unknown config") + "'unknown' config not found or filename/path is not present in 'unknown' config") + + if MULTIPLE_MATCH_CONFIG not in destinations or "filename" not in destinations[MULTIPLE_MATCH_CONFIG] or "path" not in destinations[MULTIPLE_MATCH_CONFIG]: + raise ValueError( + "'multiple_match' config not found or filename/path is not present in 'multiple_match' config") for config_name in destinations: - if config_name != ALL_CONFIG and "path" not in destinations[config_name]: + if config_name != DEFAULT_CONFIG and "path" not in destinations[config_name]: raise ValueError( f"'{config_name}' has no path defined in destination config") diff --git a/pytr/main.py b/pytr/main.py index 3c1c8c62..f5f80cdb 100644 --- a/pytr/main.py +++ b/pytr/main.py @@ -17,7 +17,6 @@ from pytr.portfolio import Portfolio from pytr.alarms import Alarms from pytr.details import Details -from pytr.file_destination_provider import FileDestinationProvider def get_main_parser(): @@ -71,6 +70,9 @@ def formatter(prog): 'Download all pdf documents from the timeline and sort them into folders.' + ' Also export account transactions (account_transactions.csv)' + ' and JSON files with all events (events_with_documents.json and other_events.json' + + ' The file and folder where the structure is saved is defined in a config file located in your home' + + ' directory "/.pytr/file_destination_config.yaml". This is created during the first dl_docs run.' + + ' Its also possible to provide the config upfront by creating the file manually (copy from git repo).' ) parser_dl_docs = parser_cmd.add_parser( 'dl_docs', @@ -200,7 +202,6 @@ def main(): dl = DL( login(phone_no=args.phone_no, pin=args.pin, web=not args.applogin), args.output, - FileDestinationProvider(), since_timestamp=since_timestamp, max_workers=args.workers, universal_filepath=args.universal, From aabe6728b5a820bf5800674e0a099e484d8b1658 Mon Sep 17 00:00:00 2001 From: bjbe Date: Sat, 3 Aug 2024 10:32:26 +0200 Subject: [PATCH 03/11] added and fixed patterns --- pytr/config/file_destination_config__template.yaml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pytr/config/file_destination_config__template.yaml b/pytr/config/file_destination_config__template.yaml index 8c08d501..db30a848 100644 --- a/pytr/config/file_destination_config__template.yaml +++ b/pytr/config/file_destination_config__template.yaml @@ -19,10 +19,11 @@ destination: # stocks stock_order_settlement: pattern: [ - { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit gekauft + { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Abrechnung(.\\d)?" }, # mit limit gekauft { event_type: "ORDER_EXECUTED", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit gekauft { event_type: "ORDER_EXECUTED", event_subtitle: "Stop-Sell-Order", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit verkauft { event_type: "TRADE_INVOICE", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit gekauft + { event_type: "TRADE_INVOICE", event_subtitle: "Verkaufsorder", section_title: "Dokumente", document_title: "Abrechnung" }, { event_type: "ORDER_EXECUTED", event_subtitle: "Verkaufsorder", section_title: "Dokumente", document_title: "Abrechnung" }, # verkauft { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", section_title: "Dokumente", document_title: "Abrechnung" }, # Aktiengeschenk { event_type: "SHAREBOOKING", event_subtitle: "Verkauf", section_title: "Dokumente", document_title: "Abrechnung \\d" }, # Kapitalmassnahme @@ -35,9 +36,10 @@ destination: { event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", section_title: "Dokumente", document_title: "Kosteninformation" }, # limit erstellt { event_type: "ORDER_EXECUTED", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Kosteninformation" }, { event_type: "ORDER_EXECUTED", event_subtitle: "Stop-Sell-Order", section_title: "Dokumente", document_title: "Kosteninformation" }, - { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Kosteninformation" }, # ohne limit gekauft + { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Kosteninformation(.\\d)?" }, # ohne limit gekauft { event_type: "ORDER_EXECUTED", event_subtitle: "Verkaufsorder", section_title: "Dokumente", document_title: "Kosteninformation" }, # verkauft { event_type: "TRADE_INVOICE", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Kosteninformation" }, + { event_type: "TRADE_INVOICE", event_subtitle: "Verkaufsorder", section_title: "Dokumente", document_title: "Kosteninformation" }, { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", section_title: "Dokumente", document_title: "Kosteninformation" }, # Aktiengeschenk { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", section_title: "Dokumente", document_title: "Kosteninformation \\d" }, # Aktiengeschenk { event_type: "EX_POST_COST_REPORT", event_title: "Ex-Post Kosteninformation", section_title: "Dokumente", document_title: "Ex-Post Kosteninformation \\d+" }, @@ -58,6 +60,7 @@ destination: stock_order_canceled: pattern: [ { event_type: "ORDER_CANCELED", event_subtitle: "Limit-Buy-Order storniert", section_title: "Dokumente", document_title: "Löschbestätigung" }, + { event_type: "ORDER_CANCELED", event_subtitle: "Stop-Sell-Order storniert", section_title: "Dokumente", document_title: "Löschbestätigung" }, { event_type: "TRADE_CANCELED", event_subtitle: "Verkauf-Abrechnung storniert", section_title: "Dokumente", document_title: "Abrechnung \\d" }, { event_type: "SAVINGS_PLAN_CANCELED", event_subtitle: "Sparplan storniert", section_title: "Dokumente", document_title: "Stornierungsbestätigung" }, ] @@ -174,6 +177,7 @@ destination: pattern: [ { event_type: "CREDIT", event_subtitle: "Dividende", section_title: "Dokumente", document_title: "Abrechnung" }, { event_type: "CREDIT", event_subtitle: "Ausschüttung", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "CREDIT_CANCELED", event_subtitle: "Dividende", section_title: "Dokumente", document_title: "Abrechnung \\d" }, { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Bardividende", section_title: "Dokumente", document_title: "Dokumente" }, { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Dividende Wahlweise", section_title: "Dokumente", document_title: "Dokumente" }, { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Bardividende korrigiert", section_title: "Dokumente", document_title: "Dokumente" }, @@ -273,7 +277,8 @@ destination: account_tax_report: pattern: [ { event_type: "TAX_REFUND", event_subtitle: "Steuerbuchung", event_title: "Steuerabrechnung", section_title: "Dokumente", document_title: "Steuerabrechnung" }, - { event_type: "YEAR_END_TAX_REPORT", event_subtitle: "Jahr \\d+", event_title: "Jährlicher Steuerreport", section_title: "Dokument", document_title: "Steuerreport \\d+" }, + { event_type: "TAX_CORRECTION", event_subtitle: "Steuerbuchung", event_title: "Steuerabrechnung", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "YEAR_END_TAX_REPORT", event_subtitle: "Jahr \\d+", event_title: "Jährlicher Steuerreport", section_title: "Dokumente", document_title: "Steuerreport \\d+" }, ] path: "Tax/" filename: "{iso_date} {document_title}" @@ -324,7 +329,8 @@ destination: notice_warrant_and_etfs: pattern: [ # when you buy a product for the first time, you will receive information documents about this product - { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Basisinformationsblatt" }, + { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Basisinformationsblatt(.\\d)?" }, + { event_type: "ORDER_EXECUTED", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Basisinformationsblatt" }, { event_type: "GESH_CORPORATE_ACTION", event_subtitle: "Unternehmensmeldung", section_title: "Dokumente", document_title: "Kundenanschreiben" }, { event_type: "GESH_CORPORATE_ACTION_MULTIPLE_POSITIONS", event_subtitle: "Gesellschaftshinweis", section_title: "Dokumente", document_title: "Kundenanschreiben \\d" }, ] From 59c386c285c59cddd79477ffd69c2675162f51b8 Mon Sep 17 00:00:00 2001 From: bjbe Date: Sat, 3 Aug 2024 10:32:59 +0200 Subject: [PATCH 04/11] fixed matching logic for patterns --- pytr/file_destination_provider.py | 45 ++++++++++++++++++------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/pytr/file_destination_provider.py b/pytr/file_destination_provider.py index 0a86b97a..36088ddc 100644 --- a/pytr/file_destination_provider.py +++ b/pytr/file_destination_provider.py @@ -101,36 +101,32 @@ def get_file_path(self, event_type: str, event_title: str, event_subtitle: str, document_title (str): The document title variables (dict): The variables->value dict to be used in the file path and file name format. ''' - - matching_configs = self._destination_configs.copy() - - # Maybe this can be improved looks like a lot of code duplication ... on the other hand using a - # dict for the parameters for example and iterate over it would make it harder to understand + + parameters_to_match = {} + if event_type is not None: - matching_configs = list(filter(lambda config: self.__is_matching_config( - config, "event_type", event_type), matching_configs)) + parameters_to_match["event_type"] = event_type variables["event_type"] = event_type.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() if event_title is not None: - matching_configs = list(filter(lambda config: self.__is_matching_config( - config, "event_title", event_title), matching_configs)) + parameters_to_match["event_title"] = event_title variables["event_title"] = event_title.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() if event_subtitle is not None: - matching_configs = list(filter(lambda config: self.__is_matching_config( - config, "event_subtitle", event_subtitle), matching_configs)) + parameters_to_match["event_subtitle"] = event_subtitle variables["event_subtitle"] = event_subtitle.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() if section_title is not None: - matching_configs = list(filter(lambda config: self.__is_matching_config( - config, "section_title", section_title), matching_configs)) + parameters_to_match["section_title"] = section_title variables["section_title"] = section_title.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() if document_title is not None: - matching_configs = list(filter(lambda config: self.__is_matching_config( - config, "document_title", document_title), matching_configs)) + parameters_to_match["document_title"] = document_title variables["document_title"] = document_title.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() + matching_configs = list(filter(lambda config: self.__is_matching_config( + config, parameters_to_match), self._destination_configs)) + if len(matching_configs) == 0: self._log.debug( f"No destination config found for the given parameters: event_type:{event_type}, event_title:{event_title},event_subtitle:{event_subtitle},section_title:{section_title},document_title:{document_title}") @@ -142,12 +138,23 @@ def get_file_path(self, event_type: str, event_title: str, event_subtitle: str, return self.__create_file_path(matching_configs[0], variables) - def __is_matching_config(self, config: DestinationConfig, key: str, value: str): + def __is_matching_config(self, config: DestinationConfig, parameters_to_match: dict[str, str]): + # See: https://github.com/pytr-org/pytr/issues/98#issuecomment-2254675770 + # We need to perform a full match on all parameters for each pattern to avoid partial wrong matches for pattern in config.pattern: - attribute = getattr(pattern, key) - if attribute is None or re.fullmatch(attribute, value): + is_full_match = True + # Check if all the parameters match + for key, value in parameters_to_match.items(): + attribute = getattr(pattern, key) + if attribute is None or re.fullmatch(attribute, value): + continue #still matching + else: + is_full_match = False + break + + if is_full_match: return True - + return False def __create_file_path(self, config: DestinationConfig, variables: dict): From f9f98999a2255a136027ff4ae96a2a54c7f7d007 Mon Sep 17 00:00:00 2001 From: Katzmann1983 Date: Sun, 4 Aug 2024 12:29:11 +0200 Subject: [PATCH 05/11] Shorter yaml --- .../file_destination_config__template.yaml | 284 +++++------------- pytr/file_destination_provider.py | 82 +++-- 2 files changed, 117 insertions(+), 249 deletions(-) diff --git a/pytr/config/file_destination_config__template.yaml b/pytr/config/file_destination_config__template.yaml index 8c08d501..1a1e2325 100644 --- a/pytr/config/file_destination_config__template.yaml +++ b/pytr/config/file_destination_config__template.yaml @@ -19,98 +19,67 @@ destination: # stocks stock_order_settlement: pattern: [ - { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit gekauft - { event_type: "ORDER_EXECUTED", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit gekauft - { event_type: "ORDER_EXECUTED", event_subtitle: "Stop-Sell-Order", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit verkauft - { event_type: "TRADE_INVOICE", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Abrechnung" }, # mit limit gekauft - { event_type: "ORDER_EXECUTED", event_subtitle: "Verkaufsorder", section_title: "Dokumente", document_title: "Abrechnung" }, # verkauft - { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", section_title: "Dokumente", document_title: "Abrechnung" }, # Aktiengeschenk - { event_type: "SHAREBOOKING", event_subtitle: "Verkauf", section_title: "Dokumente", document_title: "Abrechnung \\d" }, # Kapitalmassnahme + { event_type: "ORDER_EXECUTED", document_title: "Abrechnung" }, # mit limit verkauft + { event_type: "TRADE_INVOICE", document_title: "Abrechnung" }, # mit limit gekauft + { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", document_title: "Abrechnung" }, # Aktiengeschenk + { event_type: "SHAREBOOKING", event_subtitle: "Verkauf", document_title: "Abrechnung \\d" }, # Kapitalmassnahme ] path: "Stocks/Settlement/{iso_date_year}/" stock_order_cost_report: pattern: [ - { event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", section_title: "Dokumente", document_title: "Kosteninformation" }, # limit erstellt - { event_type: "ORDER_EXECUTED", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Kosteninformation" }, - { event_type: "ORDER_EXECUTED", event_subtitle: "Stop-Sell-Order", section_title: "Dokumente", document_title: "Kosteninformation" }, - { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Kosteninformation" }, # ohne limit gekauft - { event_type: "ORDER_EXECUTED", event_subtitle: "Verkaufsorder", section_title: "Dokumente", document_title: "Kosteninformation" }, # verkauft - { event_type: "TRADE_INVOICE", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Kosteninformation" }, - { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", section_title: "Dokumente", document_title: "Kosteninformation" }, # Aktiengeschenk - { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", section_title: "Dokumente", document_title: "Kosteninformation \\d" }, # Aktiengeschenk - { event_type: "EX_POST_COST_REPORT", event_title: "Ex-Post Kosteninformation", section_title: "Dokumente", document_title: "Ex-Post Kosteninformation \\d+" }, + { event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", document_title: "Kosteninformation" }, # limit erstellt + { event_type: "ORDER_EXECUTED", document_title: "Kosteninformation" }, + { event_type: "TRADE_INVOICE", document_title: "Kosteninformation" }, + { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", document_title: "Kosteninformation" }, # Aktiengeschenk + { event_type: "EX_POST_COST_REPORT", event_subtitle: , event_title: "Ex-Post Kosteninformation", document_title: "Ex-Post Kosteninformation \\d+" }, ] path: "Stocks/Cost report/{iso_date_year}/" stock_order_created: pattern: [ - { event_type: "TRADE_INVOICE", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, - { event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, - { event_type: "ORDER_EXECUTED", event_subtitle: "Limit-Buy-Order", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, - { event_type: "ORDER_EXECUTED", event_subtitle: "Stop-Sell-Order", section_title: "Dokumente", document_title: "Auftragsbestätigung" }, + { event_type: "ORDER_CREATED", document_title: "Auftragsbestätigung" }, + { event_type: "ORDER_EXECUTED", document_title: "Auftragsbestätigung" }, + { event_type: "TRADE_INVOICE", document_title: "Auftragsbestätigung" }, ] path: "Stocks/Order created/{iso_date_year}/" stock_order_canceled: pattern: [ - { event_type: "ORDER_CANCELED", event_subtitle: "Limit-Buy-Order storniert", section_title: "Dokumente", document_title: "Löschbestätigung" }, - { event_type: "TRADE_CANCELED", event_subtitle: "Verkauf-Abrechnung storniert", section_title: "Dokumente", document_title: "Abrechnung \\d" }, - { event_type: "SAVINGS_PLAN_CANCELED", event_subtitle: "Sparplan storniert", section_title: "Dokumente", document_title: "Stornierungsbestätigung" }, - ] - path: "Stocks/Order canceled/{iso_date_year}/" + { event_type: "TRADE_CANCELED", document_title: "Abrechnung \\d" }, + { event_type: "ORDER_CANCELED", document_title: "Löschbestätigung" }, + { event_type: "SAVINGS_PLAN_CANCELED", section_title: "Dokumente", document_title: "Stornierungsbestätigung" }, + ] + path: "Wertpapiere/Order storno" -# stocks Notice - stock_notice1: +# Kapitalmaßnahmen + info_anzeigen1: pattern: [ - { event_type: "EXERCISE", event_subtitle: "Ausübung", section_title: "Dokumente", document_title: "Kundenanschreiben" }, - { event_type: "SHAREBOOKING", event_subtitle: "Split", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Fusion", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Verkauf", section_title: "Dokumente", document_title: "Ausführungsanzeige \\d" }, - { event_type: "SHAREBOOKING", event_subtitle: "Ausbuchung", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "ISIN Wechsel", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Entflechtung", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Reverse Split", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Titelumtausch", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Umtausch/Bezug", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Reorganisation", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Stockdividende", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Kapitalreduktion", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Titelgleichstellung", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Wertlose Ausbuchung", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Effektive Auslieferung", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Kapitalerhöhung gegen Bar", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING", event_subtitle: "Kapitalerhöhung aus Gesellschaftsmitteln", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - ] - path: "Stocks/Notice/{iso_date_year}/" + { event_type: "EXERCISE", event_subtitle: "Ausübung", document_title: "Kundenanschreiben" }, + { event_type: "SHAREBOOKING" }, + ] + path: "Wertpapiere/Anzeigen" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" # split for better readability - stock_notice2: + info_anzeigen2: pattern: [ - { event_type: "CORPORATE_ACTION", event_subtitle: "Kapitalherabsetzung", section_title: "Dokumente", document_title: "Abrechnung" }, - { event_type: "SHAREBOOKING_CANCELED", event_subtitle: "Entflechtung storniert", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING_CANCELED", event_subtitle: "Stockdividende storniert", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, - { event_type: "SHAREBOOKING_CANCELED", event_subtitle: "Kapitalerhöhung gegen Bar storniert", section_title: "Dokumente", document_title: "Ausführungsanzeige" }, + { event_type: "CORPORATE_ACTION", event_subtitle: "Kapitalherabsetzung", document_title: "Abrechnung" }, + { event_type: "SHAREBOOKING_CANCELED"}, ] - path: "Stocks/Notice/{iso_date_year}/" + path: "Wertpapiere/Anzeigen" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" # split for better readability - stock_notice3: + info_anzeigen3: pattern: [ - { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Fusion", section_title: "Dokumente", document_title: "Abrechnung" }, - { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Reinvestierung", section_title: "Dokumente", document_title: "Abrechnung" }, - { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Umtausch/Bezug", section_title: "Dokumente", document_title: "Abrechnung" }, - { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Zwangsübernahme", section_title: "Dokumente", document_title: "Abrechnung" }, - { event_type: "SHAREBOOKING_TRANSACTIONAL", event_subtitle: "Kapitalreduktion", section_title: "Dokumente", document_title: "Abrechnung" }, - # Capital increase in exchange for cash / Kapitalerhöhung gegen Bar - { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", section_title: "Dokumente", document_title: "Kundenanschreiben" }, - { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", section_title: "Dokumente", document_title: "Kundenanschreiben \\d" }, + { event_type: "SHAREBOOKING_TRANSACTIONAL", document_title: "Abrechnung" }, + { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", document_title: "Kundenanschreiben" }, #Kapitalerhöhung gegen Bar + { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", document_title: "Kundenanschreiben \\d" }, #Kapitalerhöhung gegen Bar ] path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" @@ -118,14 +87,10 @@ destination: # split for better readability stock_notice4: pattern: [ - { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Spin-off", section_title: "Dokumente", document_title: "Dokumente" }, - { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Reverse Split", section_title: "Dokumente", document_title: "Dokumente" }, - # Recognition of subscription rights / Einbuchung Bezugsrechte - { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Zwischenvertrieb von Wertpapieren", section_title: "Dokumente", document_title: "Dokumente" }, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Wechsel", section_title: "Dokumente", document_title: "Information" }, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Information", section_title: "Dokumente", document_title: "Information" }, - # Repurchase of interim securities / Rückkauf von Zwischentiteln - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Aufruf von Zwischenpapieren", section_title: "Dokumente", document_title: "Kapitalmaßnahmen" }, + { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Spin-off", document_title: "Dokumente" }, + { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Reverse Split", document_title: "Dokumente" }, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Wechsel", document_title: "Information" }, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Information", document_title: "Information" }, ] path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" @@ -133,9 +98,9 @@ destination: stock_report: pattern: [ - { event_type: "QUARTERLY_REPORT", event_title: "Q\\d/\\d+ Abschluss", section_title: "Dokumente", document_title: "Kontoauszug" }, - { event_type: "QUARTERLY_REPORT", event_title: "Q\\d/\\d+ Abschluss", section_title: "Dokumente", document_title: "Depotauszug" }, - { event_type: "QUARTERLY_REPORT", event_title: "Q\\d/\\d+ Abschluss", section_title: "Dokumente", document_title: "Cryptoauszug" }, + { event_type: "QUARTERLY_REPORT", event_subtitle: , event_title: "Q\\d/\\d+ Abschluss", document_title: "Kontoauszug" }, + { event_type: "QUARTERLY_REPORT", event_subtitle: , event_title: "Q\\d/\\d+ Abschluss", document_title: "Depotauszug" }, + { event_type: "QUARTERLY_REPORT", event_subtitle: , event_title: "Q\\d/\\d+ Abschluss", document_title: "Cryptoauszug" }, ] path: "Stocks/Report/{iso_date_year}/" filename: "{iso_date} {document_title} {event_title}" @@ -144,10 +109,8 @@ destination: # General Meetings stock_general_meetings: pattern: [ - { event_type: "GENERAL_MEETING", event_subtitle: "Hauptversammlung", section_title: "Dokumente", document_title: "Hauptversammlung" }, - { event_type: "GENERAL_MEETING", event_subtitle: "Hauptversammlung", section_title: "Dokumente", document_title: "Hauptversammlung \\d" }, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Jährliche Hauptversammlung", section_title: "Dokumente", document_title: "Information" }, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Außerordentliche oder spezielle Hauptversammlung", section_title: "Dokumente", document_title: "Information" }, + { event_type: "GENERAL_MEETING"}, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Jährliche Hauptversammlung" }, ] path: "Stocks/General Meetings/{iso_date_year}/" @@ -155,8 +118,9 @@ destination: # Savings plan savings_plan: pattern: [ - { event_type: "SAVINGS_PLAN_INVOICE_CREATED", event_subtitle: "Sparplan ausgeführt", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, - { event_type: "SAVINGS_PLAN_EXECUTED", event_subtitle: "Sparplan ausgeführt", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, + { event_type: "SAVINGS_PLAN_INVOICE_CREATED"}, + { event_type: "SAVINGS_PLAN_EXECUTED"}, + { event_type: "SAVINGS_PLAN_CANCELED"}, ] path: "Stocks/Savings plan/{iso_date_year}/" @@ -164,7 +128,7 @@ destination: # pre-determined tax base earning stock_pre_earning_tax: pattern: [ - { event_type: "PRE_DETERMINED_TAX_BASE_EARNING", event_subtitle: "Vorabpauschale", section_title: "Dokumente", document_title: "Vorabpauschale" }, + { event_type: "PRE_DETERMINED_TAX_BASE_EARNING", event_subtitle: "Vorabpauschale", document_title: "Vorabpauschale" }, ] path: "Stocks/PreEarningTax/{iso_date_year}/" @@ -172,12 +136,9 @@ destination: # Dividends dividends_received: pattern: [ - { event_type: "CREDIT", event_subtitle: "Dividende", section_title: "Dokumente", document_title: "Abrechnung" }, - { event_type: "CREDIT", event_subtitle: "Ausschüttung", section_title: "Dokumente", document_title: "Abrechnung" }, - { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Bardividende", section_title: "Dokumente", document_title: "Dokumente" }, - { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Dividende Wahlweise", section_title: "Dokumente", document_title: "Dokumente" }, - { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Bardividende korrigiert", section_title: "Dokumente", document_title: "Dokumente" }, - { event_type: "ssp_corporate_action_invoice_cash", event_subtitle: "Reinvestition der Dividende", section_title: "Dokumente", document_title: "Dokumente" }, + { event_type: "CREDIT", event_subtitle: "Dividende", document_title: "Abrechnung" }, + { event_type: "CREDIT", event_subtitle: "Ausschüttung", document_title: "Abrechnung" }, + { event_type: "ssp_corporate_action_invoice_cash"}, ] path: "Dividends/{iso_date_year}/" @@ -185,9 +146,9 @@ destination: # Dividends Corporate action election dividends_election: pattern: [ - { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", section_title: "Dokumente", document_title: "Dividende Wahlweise" }, - { event_type: "ssp_dividend_option_customer_instruction", event_subtitle: "Cash oder Aktie", section_title: "Dokumente", document_title: "Dividende Wahlweise" }, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Dividende Wahlweise", section_title: "Dokumente", document_title: "Kapitalmaßnahmen" }, + { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", document_title: "Dividende Wahlweise" }, + { event_type: "ssp_dividend_option_customer_instruction", event_subtitle: "Cash oder Aktie", document_title: "Dividende Wahlweise" }, + { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Dividende Wahlweise", document_title: "Kapitalmaßnahmen" }, ] path: "Dividendelection/{iso_date_year}/" @@ -195,14 +156,14 @@ destination: # bonds bond_repayment: pattern: [ - { event_type: "REPAYMENT", event_subtitle: "Tilgung", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "REPAYMENT", event_subtitle: "Tilgung", document_title: "Abrechnung" }, ] path: "Bonds/{iso_date_year}/" filename: "{iso_date}.{iso_time} Repayment {event_title}" bond_interest: pattern: [ - { event_type: "COUPON_PAYMENT", event_subtitle: "Coupon Zahlung", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "COUPON_PAYMENT", event_subtitle: "Coupon Zahlung", document_title: "Abrechnung" }, ] path: "Bonds/{iso_date_year}/" filename: "{iso_date}.{iso_time} Interest {event_title}" @@ -211,21 +172,21 @@ destination: # Saveback saveback_enabled: pattern: [ - { event_type: "benefits_saveback_execution", event_subtitle: "Saveback", section_title: "Dokumente", document_title: "Aktivierung" }, + { event_type: "benefits_saveback_execution", document_title: "Aktivierung" }, ] path: "Saveback/Enabled/" saveback_executed: pattern: [ - { event_type: "benefits_saveback_execution", event_subtitle: "Saveback", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, + { event_type: "benefits_saveback_execution", document_title: "Abrechnung Ausführung" }, ] path: "Saveback/Report/" saveback_cost_report: pattern: [ - { event_type: "benefits_saveback_execution", event_subtitle: "Saveback", section_title: "Dokumente", document_title: "Kosteninformation" }, + { event_type: "benefits_saveback_execution", document_title: "Kosteninformation" }, ] path: "Saveback/Cost report/" @@ -233,20 +194,19 @@ destination: # Round up roundup_enabled: pattern: [ - # same files - multiple times at once - { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", section_title: "Dokumente", document_title: "Aktivierung" }, + { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", document_title: "Aktivierung" }, # same files - multiple times at once ] path: "Roundup/Enabled/" roundup_executed: pattern: [ - { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", section_title: "Dokumente", document_title: "Abrechnung Ausführung" }, + { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", document_title: "Abrechnung Ausführung" }, ] path: "Roundup/Report/{iso_date_year}/" roundup_cost_report: pattern: [ - { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", section_title: "Dokumente", document_title: "Kosteninformation" }, + { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", document_title: "Kosteninformation" }, ] path: "Roundup/Cost report/{iso_date_year}/" @@ -254,8 +214,7 @@ destination: # account cash_interest: pattern: [ - { event_type: "INTEREST_PAYOUT", event_title: "Zinsen", section_title: "Dokumente", document_title: "Abrechnung" }, - { event_type: "INTEREST_PAYOUT_CREATED", event_title: "Zinsen", section_title: "Dokumente", document_title: "Abrechnung" }, + { event_type: "INTEREST_PAYOUT", event_title: "Zinsen", document_title: "Abrechnung" } ] path: "Cash Interest/" filename: "{iso_date} Zinsabrechnung" @@ -263,7 +222,7 @@ destination: cash_transfer_report: pattern: [ - { event_type: "INCOMING_TRANSFER", event_subtitle: "Erhalten", section_title: "Dokument", document_title: "Transaktionsbestätigung" }, + { event_type: "INCOMING_TRANSFER", event_subtitle: "Erhalten", document_title: "Transaktionsbestätigung" }, ] path: "Cash Report/" filename: "{iso_date}.{iso_time} {document_title}" # {event_title} = Personal name @@ -272,8 +231,10 @@ destination: # annual tax report for account account_tax_report: pattern: [ - { event_type: "TAX_REFUND", event_subtitle: "Steuerbuchung", event_title: "Steuerabrechnung", section_title: "Dokumente", document_title: "Steuerabrechnung" }, - { event_type: "YEAR_END_TAX_REPORT", event_subtitle: "Jahr \\d+", event_title: "Jährlicher Steuerreport", section_title: "Dokument", document_title: "Steuerreport \\d+" }, + { event_type: "TAX_REFUND"}, + { event_type: "TAX_ENGINE_ANNUAL_REPORT"}, + { event_type: "YEAR_END_TAX_REPORT", document_title: "Steuerreport \\d+" }, + { event_type: "ssp_tax_correction_invoice", event_title: "Steuerkorrektur",document_title: "Steuerabrechnung" }, ] path: "Tax/" filename: "{iso_date} {document_title}" @@ -290,17 +251,8 @@ destination: # common informations notice_stocks: pattern: [ - { event_type: "CUSTOMER_CREATED", event_subtitle: "Erhalten", section_title: "Dokumente", document_title: "Basisinformationen über Wertpapiere" }, - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Kundenvereinbarung" }, # same file as below - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Kundenvereinbarung" }, # same file as above - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Datenschutzinformationen" }, - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Datenschutzinformationen" }, - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Info zur Einlagensicherung" }, - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Info zur Einlagensicherung" }, - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Hinweise zu den Handelsplätzen" }, - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Hinweise zu den Handelsplätzen" }, - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Dokumente", document_title: "Preis- und Leistungsverzeichnis" }, - { event_type: "DOCUMENTS_CHANGED", event_subtitle: "Geändert", section_title: "Aktualisierte Dokumente", document_title: "Preis- und Leistungsverzeichnis" }, + { event_type: "CUSTOMER_CREATED", event_subtitle: "Erhalten", document_title: "Basisinformationen über Wertpapiere" }, + { event_type: "DOCUMENTS_CHANGED" }, # same file as below ] path: "Notice/" filename: "{iso_date} {document_title}" @@ -308,100 +260,28 @@ destination: notice_crypto: pattern: [ - { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Kundenvereinbarung" }, - { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Customer Agreement" }, - { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Mistrade Regelungen" }, - { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Risikohinweise Crypto" }, - { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Crypto Verwahrbedingungen" }, - { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Vorvertragliche Informationen" }, - { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Widerrufsbelehrung Cryptoverwahrer" }, - { event_type: "DOCUMENTS_ACCEPTED", event_subtitle: "Angenommen", section_title: "Dokumente", document_title: "Datenschutzinformationen Cryptoverwahrer" }, + { event_type: "TRADE_INVOICE", document_title: "Basisinformationsblatt" }, + { event_type: "ORDER_EXECUTED", document_title: "Basisinformationsblatt" }, + { event_type: "GESH_CORPORATE_ACTION", event_subtitle: "Unternehmensmeldung", document_title: "Kundenanschreiben" }, + { event_type: "GESH_CORPORATE_ACTION_MULTIPLE_POSITIONS", event_subtitle: "Gesellschaftshinweis", document_title: "Kundenanschreiben \\d" }, ] path: "Notice/" filename: "{iso_date} {document_title}" - - notice_warrant_and_etfs: + + contract_documents: pattern: [ - # when you buy a product for the first time, you will receive information documents about this product - { event_type: "ORDER_EXECUTED", event_subtitle: "Kauforder", section_title: "Dokumente", document_title: "Basisinformationsblatt" }, - { event_type: "GESH_CORPORATE_ACTION", event_subtitle: "Unternehmensmeldung", section_title: "Dokumente", document_title: "Kundenanschreiben" }, - { event_type: "GESH_CORPORATE_ACTION_MULTIPLE_POSITIONS", event_subtitle: "Gesellschaftshinweis", section_title: "Dokumente", document_title: "Kundenanschreiben \\d" }, + { event_type: "card_order_billed" }, + { event_type: "DOCUMENTS_CREATED" }, + { event_type: "DOCUMENTS_ACCEPTED" }, ] - path: "Notice/" - filename: "{iso_date} {document_title} - {event_title}" - - - card_ordered: + path: "Contract/" + filename: "{iso_date} {document_title}" + + + order_expired: pattern: [ - { event_type: "card_order_billed", event_title: "Trade Republic Card", section_title: "Dokumente", document_title: "Bestellung Trade Republic Karte" }, + { event_type: "ORDER_EXPIRED"}, ] - path: "Notice/" + path: "Stocks/Order canceled/{iso_date_year}/" filename: "{iso_date} {document_title}" - - -# The following events do not contain any documents, these nodes are skipped. -# -# payment_inbound: -# pattern: [ -# { event_type: "PAYMENT_INBOUND", event_subtitle: , section_title: , document_title: }, -# { event_type: "INCOMING_TRANSFER", event_subtitle: "Erhalten", section_title: , document_title: }, -# ] -# path: "Cash Report/" -# -# payment_outbout: -# pattern: [ -# { event_type: "PAYMENT_OUTBOUND", event_subtitle: , section_title: , document_title: }, -# ] -# path: "Cash Report/" -# -# card_refund: -# pattern: [ -# { event_type: "card_refund", event_subtitle: , section_title: , document_title: }, -# ] -# path: "Cash Report/" -# -# card_successful_transaction: -# pattern: [ -# { event_type: "card_successful_transaction", event_subtitle: , section_title: , document_title: }, -# ] -# path: "Cash Report/" -# -# card_successful_atm_withdrawal: -# pattern: [ -# { event_type: "card_successful_atm_withdrawal", event_subtitle: , event_title: "Abhebung", section_title: , document_title: }, -# ] -# path: "Cash Report/" -# -# -# card_failed_transaction: -# pattern: [ -# { event_type: "card_failed_transaction", event_subtitle: "Abgebrochen", section_title: , document_title: }, -# ] -# path: "Cash Report/" -# -# card_successful_verification: -# pattern: [ -# { event_type: "card_successful_verification", event_subtitle: , section_title: , document_title: }, -# ] -# path: "Cash Report/" -# -# new_account_iban: -# pattern: [ -# { event_type: "new_tr_iban", event_subtitle: , section_title: , document_title: }, -# ] -# path: "Cash Report/" -# -# stock_order_rejected: -# pattern: [ -# { event_type: "ORDER_REJECTED", event_subtitle: "Kauforder abgelehnt", section_title: , document_title: }, -# ] -# path: "Stocks/Order canceled/{iso_date_year}/" -# -# -# stock_order_expired: -# pattern: [ -# { event_type: "ORDER_EXPIRED", event_subtitle: "Kauforder abgelaufen", section_title: , document_title: }, -# ] -# path: "Stocks/Order canceled/{iso_date_year}/" -# \ No newline at end of file diff --git a/pytr/file_destination_provider.py b/pytr/file_destination_provider.py index 0a86b97a..072a3623 100644 --- a/pytr/file_destination_provider.py +++ b/pytr/file_destination_provider.py @@ -4,6 +4,9 @@ import pytr.config from importlib_resources import files + +from dataclasses import dataclass, fields +from typing import Optional from yaml import safe_load from pathlib import Path from pytr.app_path import DESTINATION_CONFIG_FILE @@ -35,21 +38,22 @@ def __missing__(self, key): return key.join("{}") +@dataclass class DestinationConfig: - def __init__(self, config_name: str, filename: str, path: str = None, pattern: list = None): - self.config_name = config_name - self.filename = filename - self.path = path - self.pattern = pattern + config_name: str + filename: str + path: Optional[str] = None + pattern: Optional[list] = None +@dataclass class Pattern: - def __init__(self, event_type: str, event_subtitle: str, event_title: str, section_title: str, document_title: str): - self.event_type = event_type - self.event_subtitle = event_subtitle - self.event_title = event_title - self.section_title = section_title - self.document_title = document_title + event_type: Optional[str] = None + event_title: Optional[str] = None + event_subtitle: Optional[str] = None + section_title: Optional[str] = None + document_title: Optional[str] = None + timestamp: Optional[float] = None class FileDestinationProvider: @@ -86,8 +90,9 @@ def __init__(self): else: patterns = self.__extract_pattern( destinations[config_name].get("pattern", None)) - self._destination_configs.append(DestinationConfig( - config_name, destinations[config_name].get("filename", None), destinations[config_name].get("path", None), patterns)) + for pattern in patterns: + self._destination_configs.append(DestinationConfig( + config_name, destinations[config_name].get("filename", None), destinations[config_name].get("path", None), pattern)) def get_file_path(self, event_type: str, event_title: str, event_subtitle: str, section_title: str, document_title: str, variables: dict) -> str: ''' @@ -101,35 +106,18 @@ def get_file_path(self, event_type: str, event_title: str, event_subtitle: str, document_title (str): The document title variables (dict): The variables->value dict to be used in the file path and file name format. ''' + doc = Pattern(event_type, event_title, event_subtitle, section_title, document_title) matching_configs = self._destination_configs.copy() + # create a dictionary that maps the field names to their values in the pattern instance + pattern_dict = {field.name: getattr(doc, field.name) for field in fields(Pattern)} + # variables = doc.get_variables() - # Maybe this can be improved looks like a lot of code duplication ... on the other hand using a - # dict for the parameters for example and iterate over it would make it harder to understand - if event_type is not None: - matching_configs = list(filter(lambda config: self.__is_matching_config( - config, "event_type", event_type), matching_configs)) - variables["event_type"] = event_type.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() - - if event_title is not None: - matching_configs = list(filter(lambda config: self.__is_matching_config( - config, "event_title", event_title), matching_configs)) - variables["event_title"] = event_title.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() - - if event_subtitle is not None: - matching_configs = list(filter(lambda config: self.__is_matching_config( - config, "event_subtitle", event_subtitle), matching_configs)) - variables["event_subtitle"] = event_subtitle.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() - - if section_title is not None: - matching_configs = list(filter(lambda config: self.__is_matching_config( - config, "section_title", section_title), matching_configs)) - variables["section_title"] = section_title.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() - - if document_title is not None: - matching_configs = list(filter(lambda config: self.__is_matching_config( - config, "document_title", document_title), matching_configs)) - variables["document_title"] = document_title.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() + # iterate over the dictionary to filter the matching_configs list and update the variables dictionary + for field_name, search_pattern in pattern_dict.items(): + if search_pattern is not None: + matching_configs = list(filter(lambda config: self.__is_matching_config(config, field_name, search_pattern), matching_configs)) + variables[field_name] = search_pattern if len(matching_configs) == 0: self._log.debug( @@ -142,13 +130,13 @@ def get_file_path(self, event_type: str, event_title: str, event_subtitle: str, return self.__create_file_path(matching_configs[0], variables) - def __is_matching_config(self, config: DestinationConfig, key: str, value: str): - for pattern in config.pattern: - attribute = getattr(pattern, key) - if attribute is None or re.fullmatch(attribute, value): - return True - - return False + @staticmethod + def __is_matching_config(config: DestinationConfig, field_name: str, search_pattern: str) -> bool: + pattern = config.pattern + return ( + getattr(pattern, field_name, None) is None + or re.match(getattr(pattern, field_name, None), search_pattern) + ) def __create_file_path(self, config: DestinationConfig, variables: dict): formate_variables = DefaultFormateValue(variables) @@ -164,8 +152,8 @@ def __extract_pattern(self, pattern_config: list) -> list: patterns = [] for pattern in pattern_config: patterns.append(Pattern(pattern.get("event_type", None), - pattern.get("event_subtitle", None), pattern.get("event_title", None), + pattern.get("event_subtitle", None), pattern.get("section_title", None), pattern.get("document_title", None))) From 5bcccfaf9aaa15d3f1336c412624f76fc551791e Mon Sep 17 00:00:00 2001 From: bjbe Date: Sun, 4 Aug 2024 18:15:25 +0200 Subject: [PATCH 06/11] fixed an issues added by the PR --- pytr/config/file_destination_config__template.yaml | 1 + pytr/file_destination_provider.py | 5 +---- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pytr/config/file_destination_config__template.yaml b/pytr/config/file_destination_config__template.yaml index ed00838f..85f874b1 100644 --- a/pytr/config/file_destination_config__template.yaml +++ b/pytr/config/file_destination_config__template.yaml @@ -244,6 +244,7 @@ destination: account_tax_adjustment: pattern: [ { event_type: "ssp_tax_correction_invoice", event_title: "Steuerkorrektur", section_title: "Dokumente", document_title: "Steuerabrechnung" }, + { event_type: "TAX_CORRECTION", event_subtitle: "Steuerbuchung", event_title: "Steuerabrechnung", section_title: "Dokumente", document_title: "Abrechnung" }, ] path: "Tax/" filename: "{iso_date} {event_title}" diff --git a/pytr/file_destination_provider.py b/pytr/file_destination_provider.py index 968aa0cd..19306216 100644 --- a/pytr/file_destination_provider.py +++ b/pytr/file_destination_provider.py @@ -53,7 +53,6 @@ class Pattern: event_subtitle: Optional[str] = None section_title: Optional[str] = None document_title: Optional[str] = None - timestamp: Optional[float] = None class FileDestinationProvider: @@ -119,8 +118,6 @@ def get_file_path(self, event_type: str, event_title: str, event_subtitle: str, matching_configs = list(filter(lambda config: self.__is_matching_config(config, field_name, search_pattern), matching_configs)) variables[field_name] = search_pattern.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() - matching_configs = list(filter(lambda config: self.__is_matching_config( - config, parameters_to_match), self._destination_configs)) if len(matching_configs) == 0: self._log.debug( @@ -138,7 +135,7 @@ def __is_matching_config(config: DestinationConfig, field_name: str, search_patt pattern = config.pattern return ( getattr(pattern, field_name, None) is None - or re.match(getattr(pattern, field_name, None), search_pattern) + or re.fullmatch(getattr(pattern, field_name, None), search_pattern) ) def __create_file_path(self, config: DestinationConfig, variables: dict): From ecda739d95be0a63a99cdb3ad79febea9aff3074 Mon Sep 17 00:00:00 2001 From: bjbe Date: Sun, 4 Aug 2024 18:33:54 +0200 Subject: [PATCH 07/11] added and fixed some patterns --- pytr/config/file_destination_config__template.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pytr/config/file_destination_config__template.yaml b/pytr/config/file_destination_config__template.yaml index 85f874b1..2ef47f4a 100644 --- a/pytr/config/file_destination_config__template.yaml +++ b/pytr/config/file_destination_config__template.yaml @@ -19,7 +19,7 @@ destination: # stocks stock_order_settlement: pattern: [ - { event_type: "ORDER_EXECUTED", document_title: "Abrechnung" }, # mit limit verkauft + { event_type: "ORDER_EXECUTED", document_title: "Abrechnung(.\\d)?" }, # mit limit verkauft { event_type: "TRADE_INVOICE", document_title: "Abrechnung" }, # mit limit gekauft { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", document_title: "Abrechnung" }, # Aktiengeschenk { event_type: "SHAREBOOKING", event_subtitle: "Verkauf", document_title: "Abrechnung \\d" }, # Kapitalmassnahme @@ -30,7 +30,7 @@ destination: stock_order_cost_report: pattern: [ { event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", document_title: "Kosteninformation" }, # limit erstellt - { event_type: "ORDER_EXECUTED", document_title: "Kosteninformation" }, + { event_type: "ORDER_EXECUTED", document_title: "Kosteninformation(.\\d)?" }, { event_type: "TRADE_INVOICE", document_title: "Kosteninformation" }, { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", document_title: "Kosteninformation" }, # Aktiengeschenk { event_type: "EX_POST_COST_REPORT", event_subtitle: , event_title: "Ex-Post Kosteninformation", document_title: "Ex-Post Kosteninformation \\d+" }, @@ -139,6 +139,7 @@ destination: pattern: [ { event_type: "CREDIT", event_subtitle: "Dividende", document_title: "Abrechnung" }, { event_type: "CREDIT", event_subtitle: "Ausschüttung", document_title: "Abrechnung" }, + { event_type: "CREDIT_CANCELED", event_subtitle: "Dividende", document_title: "Abrechnung \\d" }, { event_type: "ssp_corporate_action_invoice_cash"}, ] path: "Dividends/{iso_date_year}/" @@ -215,7 +216,8 @@ destination: # account cash_interest: pattern: [ - { event_type: "INTEREST_PAYOUT", event_title: "Zinsen", document_title: "Abrechnung" } + { event_type: "INTEREST_PAYOUT", event_title: "Zinsen", document_title: "Abrechnung" }, + { event_type: "INTEREST_PAYOUT_CREATED", event_title: "Zinsen", document_title: "Abrechnung" } ] path: "Cash Interest/" filename: "{iso_date} Zinsabrechnung" @@ -263,7 +265,7 @@ destination: notice_crypto: pattern: [ { event_type: "TRADE_INVOICE", document_title: "Basisinformationsblatt" }, - { event_type: "ORDER_EXECUTED", document_title: "Basisinformationsblatt" }, + { event_type: "ORDER_EXECUTED", document_title: "Basisinformationsblatt(.\\d)?" }, { event_type: "GESH_CORPORATE_ACTION", event_subtitle: "Unternehmensmeldung", document_title: "Kundenanschreiben" }, { event_type: "GESH_CORPORATE_ACTION_MULTIPLE_POSITIONS", event_subtitle: "Gesellschaftshinweis", document_title: "Kundenanschreiben \\d" }, ] From 966e9cf382576cf7ef54fcaffb28dfbfbc47d8e5 Mon Sep 17 00:00:00 2001 From: bjbe Date: Sun, 18 Aug 2024 18:42:12 +0200 Subject: [PATCH 08/11] patched patterns and formatted yaml file --- .../file_destination_config__template.yaml | 296 +++++++----------- 1 file changed, 112 insertions(+), 184 deletions(-) diff --git a/pytr/config/file_destination_config__template.yaml b/pytr/config/file_destination_config__template.yaml index 2ef47f4a..b63d1b4d 100644 --- a/pytr/config/file_destination_config__template.yaml +++ b/pytr/config/file_destination_config__template.yaml @@ -1,291 +1,219 @@ destination: -# valid for all blocks without explicit filename + ################################################################################################ + ## Default and fallback patterns used for the destination of a downloaded document. + ################################################################################################ + # valid for all blocks without explicit filename default: filename: "{iso_date}.{iso_time} {event_title}" # {event_title} = Wertpapier-/ETF-/Produkt-Name - -# if pattern not found, use this block + # if pattern not found, use this block unknown: path: "Unknown/{section_title}/" filename: "{iso_date}.{iso_time} {event_type} - {event_subtitle} - {document_title} - {event_title}" - -# if pattern found multiple times, use this block + # if pattern found multiple times, use this block multiple_match: path: "MultipleMatchPattern/{section_title}/" filename: "{iso_date}.{iso_time} {event_type} - {event_subtitle} - {document_title} - {event_title}" + ################################################################################################ - -# stocks + ################################################################################################ + ## Specific patterns for the destination of a downloaded document. + ################################################################################################ + # stocks stock_order_settlement: pattern: [ - { event_type: "ORDER_EXECUTED", document_title: "Abrechnung(.\\d)?" }, # mit limit verkauft - { event_type: "TRADE_INVOICE", document_title: "Abrechnung" }, # mit limit gekauft - { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", document_title: "Abrechnung" }, # Aktiengeschenk - { event_type: "SHAREBOOKING", event_subtitle: "Verkauf", document_title: "Abrechnung \\d" }, # Kapitalmassnahme - ] + {event_type: "ORDER_EXECUTED", document_title: "Abrechnung(.\\d+)?"}, # mit limit verkauft + {event_type: "TRADE_INVOICE", document_title: "Abrechnung(.\\d+)?"}, # mit limit gekauft + {event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", document_title: "Abrechnung(.\\d+)?"}, # Aktiengeschenk + {event_type: "SHAREBOOKING", event_subtitle: "Verkauf", document_title: "Abrechnung(.\\d+)?"}, # Kapitalmassnahme + ] path: "Stocks/Settlement/{iso_date_year}/" - stock_order_cost_report: pattern: [ - { event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", document_title: "Kosteninformation" }, # limit erstellt - { event_type: "ORDER_EXECUTED", document_title: "Kosteninformation(.\\d)?" }, - { event_type: "TRADE_INVOICE", document_title: "Kosteninformation" }, - { event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", document_title: "Kosteninformation" }, # Aktiengeschenk - { event_type: "EX_POST_COST_REPORT", event_subtitle: , event_title: "Ex-Post Kosteninformation", document_title: "Ex-Post Kosteninformation \\d+" }, - ] + {event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", document_title: "Kosteninformation(.\\d+)?"}, # limit erstellt + {event_type: "ORDER_EXECUTED", document_title: "Kosteninformation(.\\d+)?"}, + {event_type: "TRADE_INVOICE", document_title: "Kosteninformation(.\\d+)?"}, + {event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", document_title: "Kosteninformation(.\\d+)?"}, # Aktiengeschenk + {event_type: "EX_POST_COST_REPORT", event_title: "Ex-Post Kosteninformation", document_title: "Ex-Post Kosteninformation(.\\d+)?"}, + ] path: "Stocks/Cost report/{iso_date_year}/" - stock_order_created: - pattern: [ - { event_type: "ORDER_CREATED", document_title: "Auftragsbestätigung" }, - { event_type: "ORDER_EXECUTED", document_title: "Auftragsbestätigung" }, - { event_type: "TRADE_INVOICE", document_title: "Auftragsbestätigung" }, - ] + pattern: [{event_type: "ORDER_CREATED", document_title: "Auftragsbestätigung(.\\d+)?"}, {event_type: "ORDER_EXECUTED", document_title: "Auftragsbestätigung(.\\d+)?"}, {event_type: "TRADE_INVOICE", document_title: "Auftragsbestätigung(.\\d+)?"}] path: "Stocks/Order created/{iso_date_year}/" - stock_order_canceled: - pattern: [ - { event_type: "TRADE_CANCELED", document_title: "Abrechnung \\d" }, - { event_type: "ORDER_CANCELED", document_title: "Löschbestätigung" }, - { event_type: "SAVINGS_PLAN_CANCELED", section_title: "Dokumente", document_title: "Stornierungsbestätigung" }, - ] + pattern: [{event_type: "TRADE_CANCELED", document_title: "Abrechnung(.\\d+)?"}, {event_type: "ORDER_CANCELED", document_title: "Löschbestätigung(.\\d+)?"}] path: "Stocks/Order canceled/{iso_date_year}/" - - -# Kapitalmaßnahmen - info_anzeigen1: - pattern: [ - { event_type: "EXERCISE", event_subtitle: "Ausübung", document_title: "Kundenanschreiben" }, - { event_type: "SHAREBOOKING" }, - ] - path: "Wertpapiere/Anzeigen" + # Kapitalmaßnahmen + stock_notice_1: + pattern: [{event_type: "EXERCISE", event_subtitle: "Ausübung", document_title: "Kundenanschreiben(.\\d+)?"}, {event_type: "SHAREBOOKING", document_title: "Ausführungsanzeige(.\\d+)?"}] + path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" -# split for better readability - info_anzeigen2: - pattern: [ - { event_type: "CORPORATE_ACTION", event_subtitle: "Kapitalherabsetzung", document_title: "Abrechnung" }, - { event_type: "SHAREBOOKING_CANCELED"}, - ] - path: "Wertpapiere/Anzeigen" + # split for better readability + stock_notice_2: + pattern: [{event_type: "CORPORATE_ACTION", event_subtitle: "Kapitalherabsetzung", document_title: "Abrechnung(.\\d+)?"}, {event_type: "SHAREBOOKING_CANCELED"}] + path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" -# split for better readability - info_anzeigen3: + # split for better readability + stock_notice_3: pattern: [ - { event_type: "SHAREBOOKING_TRANSACTIONAL", document_title: "Abrechnung" }, - { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", document_title: "Kundenanschreiben" }, #Kapitalerhöhung gegen Bar - { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", document_title: "Kundenanschreiben \\d" }, #Kapitalerhöhung gegen Bar - ] + {event_type: "SHAREBOOKING_TRANSACTIONAL", document_title: "Abrechnung(.\\d+)?"}, + {event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", document_title: "Kundenanschreiben(.\\d+)?"}, #Kapitalerhöhung gegen Bar + ] path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" -# split for better readability - stock_notice4: - pattern: [ - { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Spin-off", document_title: "Dokumente" }, - { event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Reverse Split", document_title: "Dokumente" }, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Wechsel", document_title: "Information" }, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Information", document_title: "Information" }, - ] + # split for better readability + stock_notice_4: + pattern: + [ + {event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Spin-off", document_title: "Dokumente(.\\d+)?"}, + {event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Reverse Split", document_title: "Dokumente(.\\d+)?"}, + {event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Aktiendividende", document_title: "Dokumente(.\\d+)?"}, + {event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Zwischenvertrieb von Wertpapieren", document_title: "Dokumente(.\\d+)?"}, + {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Wechsel", document_title: "Information(.\\d+)?"}, + {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Information", document_title: "Information(.\\d+)?"}, + {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Außerordentliche oder spezielle Hauptversammlung", document_title: "Information(.\\d+)?"}, + {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Aufruf von Zwischenpapieren", document_title: "Kapitalmaßnahmen(.\\d+)?"}, + ] path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" - stock_report: - pattern: [ - { event_type: "QUARTERLY_REPORT", event_subtitle: , event_title: "Q\\d/\\d+ Abschluss", document_title: "Kontoauszug" }, - { event_type: "QUARTERLY_REPORT", event_subtitle: , event_title: "Q\\d/\\d+ Abschluss", document_title: "Depotauszug" }, - { event_type: "QUARTERLY_REPORT", event_subtitle: , event_title: "Q\\d/\\d+ Abschluss", document_title: "Cryptoauszug" }, - ] + pattern: [{event_type: "QUARTERLY_REPORT", event_title: "Q\\d/\\d+ Abschluss", document_title: "Kontoauszug(.\\d+)?"}, {event_type: "QUARTERLY_REPORT", event_title: "Q\\d/\\d+ Abschluss", document_title: "Depotauszug(.\\d+)?"}, {event_type: "QUARTERLY_REPORT", event_title: "Q\\d/\\d+ Abschluss", document_title: "Cryptoauszug(.\\d+)?"}] path: "Stocks/Report/{iso_date_year}/" filename: "{iso_date} {document_title} {event_title}" - -# General Meetings + # General Meetings stock_general_meetings: - pattern: [ - { event_type: "GENERAL_MEETING"}, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Jährliche Hauptversammlung" }, - ] + pattern: [{event_type: "GENERAL_MEETING"}, {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Jährliche Hauptversammlung"}] path: "Stocks/General Meetings/{iso_date_year}/" - -# Savings plan + # Savings plan savings_plan: - pattern: [ - { event_type: "SAVINGS_PLAN_INVOICE_CREATED"}, - { event_type: "SAVINGS_PLAN_EXECUTED"}, - { event_type: "SAVINGS_PLAN_CANCELED"}, - ] + pattern: [{event_type: "SAVINGS_PLAN_INVOICE_CREATED"}, {event_type: "SAVINGS_PLAN_EXECUTED"}, {event_type: "SAVINGS_PLAN_CANCELED", event_subtitle: "Sparplan storniert"}] path: "Stocks/Savings plan/{iso_date_year}/" - -# pre-determined tax base earning + # pre-determined tax base earning stock_pre_earning_tax: - pattern: [ - { event_type: "PRE_DETERMINED_TAX_BASE_EARNING", event_subtitle: "Vorabpauschale", document_title: "Vorabpauschale" }, - ] + pattern: [{event_type: "PRE_DETERMINED_TAX_BASE_EARNING", event_subtitle: "Vorabpauschale", document_title: "Vorabpauschale(.\\d+)?"}] path: "Stocks/PreEarningTax/{iso_date_year}/" - -# Dividends + # Dividends dividends_received: - pattern: [ - { event_type: "CREDIT", event_subtitle: "Dividende", document_title: "Abrechnung" }, - { event_type: "CREDIT", event_subtitle: "Ausschüttung", document_title: "Abrechnung" }, - { event_type: "CREDIT_CANCELED", event_subtitle: "Dividende", document_title: "Abrechnung \\d" }, - { event_type: "ssp_corporate_action_invoice_cash"}, - ] + pattern: [{event_type: "CREDIT", event_subtitle: "Dividende", document_title: "Abrechnung(.\\d+)?"}, {event_type: "CREDIT", event_subtitle: "Ausschüttung", document_title: "Abrechnung(.\\d+)?"}, {event_type: "CREDIT_CANCELED", event_subtitle: "Dividende", document_title: "Abrechnung(.\\d+)?"}, {event_type: "ssp_corporate_action_invoice_cash"}] path: "Dividends/{iso_date_year}/" - -# Dividends Corporate action election + # Dividends Corporate action election dividends_election: - pattern: [ - { event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", document_title: "Dividende Wahlweise" }, - { event_type: "ssp_dividend_option_customer_instruction", event_subtitle: "Cash oder Aktie", document_title: "Dividende Wahlweise" }, - { event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Dividende Wahlweise", document_title: "Kapitalmaßnahmen" }, - ] + pattern: + [ + {event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", document_title: "Dividende Wahlweise(.\\d+)?"}, + {event_type: "ssp_dividend_option_customer_instruction", event_subtitle: "Cash oder Aktie", document_title: "Dividende Wahlweise(.\\d+)?"}, + {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Dividende Wahlweise", document_title: "Kapitalmaßnahmen(.\\d+)?"}, + ] path: "Dividendelection/{iso_date_year}/" - -# bonds + # bonds bond_repayment: - pattern: [ - { event_type: "REPAYMENT", event_subtitle: "Tilgung", document_title: "Abrechnung" }, - ] + pattern: [{event_type: "REPAYMENT", event_subtitle: "Tilgung", document_title: "Abrechnung(.\\d+)?"}] path: "Bonds/{iso_date_year}/" filename: "{iso_date}.{iso_time} Repayment {event_title}" bond_interest: - pattern: [ - { event_type: "COUPON_PAYMENT", event_subtitle: "Coupon Zahlung", document_title: "Abrechnung" }, - ] + pattern: [{event_type: "COUPON_PAYMENT", event_subtitle: "Coupon Zahlung", document_title: "Abrechnung(.\\d+)?"}] path: "Bonds/{iso_date_year}/" filename: "{iso_date}.{iso_time} Interest {event_title}" - -# Saveback + # Saveback saveback_enabled: - pattern: [ - { event_type: "benefits_saveback_execution", document_title: "Aktivierung" }, - ] - path: "Saveback/Enabled/" - + pattern: [{event_type: "benefits_saveback_execution", document_title: "Aktivierung(.\\d+)?"}] + path: "Saveback/{iso_date_year}/" + filename: "{iso_date}.{iso_time} Enabled {event_title}" saveback_executed: - pattern: [ - { event_type: "benefits_saveback_execution", document_title: "Abrechnung Ausführung" }, - ] - path: "Saveback/Report/" - + pattern: [{event_type: "benefits_saveback_execution", document_title: "Abrechnung Ausführung(.\\d+)?"}] + path: "Saveback/{iso_date_year}/" + filename: "{iso_date}.{iso_time} Report {event_title}" saveback_cost_report: - pattern: [ - { event_type: "benefits_saveback_execution", document_title: "Kosteninformation" }, - ] - path: "Saveback/Cost report/" + pattern: [{event_type: "benefits_saveback_execution", document_title: "Kosteninformation(.\\d+)?"}] + path: "Saveback/{iso_date_year}/" + filename: "{iso_date}.{iso_time} Cost report {event_title}" - -# Round up + # Round up roundup_enabled: pattern: [ - { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", document_title: "Aktivierung" }, # same files - multiple times at once - ] - path: "Roundup/Enabled/" + {event_type: "benefits_spare_change_execution", event_subtitle: "Round up", document_title: "Aktivierung(.\\d+)?"}, # same files - multiple times at once + ] + path: "Roundup/{iso_date_year}/" + filename: "{iso_date}.{iso_time} Enabled {event_title}" roundup_executed: - pattern: [ - { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", document_title: "Abrechnung Ausführung" }, - ] - path: "Roundup/Report/{iso_date_year}/" + pattern: [{event_type: "benefits_spare_change_execution", event_subtitle: "Round up", document_title: "Abrechnung Ausführung(.\\d+)?"}] + path: "Roundup/{iso_date_year}/" + filename: "{iso_date}.{iso_time} Report {event_title}" roundup_cost_report: - pattern: [ - { event_type: "benefits_spare_change_execution", event_subtitle: "Round up", document_title: "Kosteninformation" }, - ] - path: "Roundup/Cost report/{iso_date_year}/" + pattern: [{event_type: "benefits_spare_change_execution", event_subtitle: "Round up", document_title: "Kosteninformation(.\\d+)?"}] + path: "Roundup/{iso_date_year}/" + filename: "{iso_date}.{iso_time} Cost report {event_title}" - -# account + # account cash_interest: - pattern: [ - { event_type: "INTEREST_PAYOUT", event_title: "Zinsen", document_title: "Abrechnung" }, - { event_type: "INTEREST_PAYOUT_CREATED", event_title: "Zinsen", document_title: "Abrechnung" } - ] + pattern: [{event_type: "INTEREST_PAYOUT", event_title: "Zinsen", document_title: "Abrechnung(.\\d+)?"}, {event_type: "INTEREST_PAYOUT_CREATED", event_title: "Zinsen", document_title: "Abrechnung(.\\d+)?"}] path: "Cash Interest/" filename: "{iso_date} Zinsabrechnung" - cash_transfer_report: - pattern: [ - { event_type: "INCOMING_TRANSFER", event_subtitle: "Erhalten", document_title: "Transaktionsbestätigung" }, - ] + pattern: [{event_type: "INCOMING_TRANSFER", document_title: "Transaktionsbestätigung(.\\d+)?"}] path: "Cash Report/" filename: "{iso_date}.{iso_time} {document_title}" # {event_title} = Personal name - -# annual tax report for account + # annual tax report for account account_tax_report: - pattern: [ - { event_type: "TAX_REFUND"}, - { event_type: "TAX_ENGINE_ANNUAL_REPORT"}, - { event_type: "YEAR_END_TAX_REPORT", document_title: "Steuerreport \\d+" }, - { event_type: "ssp_tax_correction_invoice", event_title: "Steuerkorrektur",document_title: "Steuerabrechnung" }, - ] + pattern: [{event_type: "TAX_REFUND"}, {event_type: "TAX_ENGINE_ANNUAL_REPORT"}, {event_type: "YEAR_END_TAX_REPORT", document_title: "Steuerreport(.\\d+)?"}] path: "Tax/" filename: "{iso_date} {document_title}" - account_tax_adjustment: - pattern: [ - { event_type: "ssp_tax_correction_invoice", event_title: "Steuerkorrektur", section_title: "Dokumente", document_title: "Steuerabrechnung" }, - { event_type: "TAX_CORRECTION", event_subtitle: "Steuerbuchung", event_title: "Steuerabrechnung", section_title: "Dokumente", document_title: "Abrechnung" }, - ] + pattern: [{event_type: "ssp_tax_correction_invoice", event_title: "Steuerkorrektur", document_title: "Steuerabrechnung(.\\d+)?"}, {event_type: "TAX_CORRECTION", event_subtitle: "Steuerbuchung", event_title: "Steuerabrechnung", section_title: "Dokumente", document_title: "Abrechnung(.\\d+)?"}] path: "Tax/" filename: "{iso_date} {event_title}" - -# common informations + # common informations notice_stocks: pattern: [ - { event_type: "CUSTOMER_CREATED", event_subtitle: "Erhalten", document_title: "Basisinformationen über Wertpapiere" }, - { event_type: "DOCUMENTS_CHANGED" }, # same file as below - ] + {event_type: "CUSTOMER_CREATED", event_subtitle: "Erhalten", document_title: "Basisinformationen über Wertpapiere(.\\d+)?"}, + {event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", document_title: "Basisinformationsblatt(.\\d+)?"}, + {event_type: "DOCUMENTS_CHANGED"}, # same file as below + ] path: "Notice/" filename: "{iso_date} {document_title}" - notice_crypto: - pattern: [ - { event_type: "TRADE_INVOICE", document_title: "Basisinformationsblatt" }, - { event_type: "ORDER_EXECUTED", document_title: "Basisinformationsblatt(.\\d)?" }, - { event_type: "GESH_CORPORATE_ACTION", event_subtitle: "Unternehmensmeldung", document_title: "Kundenanschreiben" }, - { event_type: "GESH_CORPORATE_ACTION_MULTIPLE_POSITIONS", event_subtitle: "Gesellschaftshinweis", document_title: "Kundenanschreiben \\d" }, - ] + pattern: + [ + {event_type: "TRADE_INVOICE", document_title: "Basisinformationsblatt(.\\d+)?"}, + {event_type: "ORDER_EXECUTED", document_title: "Basisinformationsblatt(.\\d+)?"}, + {event_type: "GESH_CORPORATE_ACTION", event_subtitle: "Unternehmensmeldung", document_title: "Kundenanschreiben(.\\d+)?"}, + {event_type: "GESH_CORPORATE_ACTION_MULTIPLE_POSITIONS", event_subtitle: "Gesellschaftshinweis", document_title: "Kundenanschreiben(.\\d+)?"}, + ] path: "Notice/" filename: "{iso_date} {document_title}" - contract_documents: - pattern: [ - { event_type: "card_order_billed" }, - { event_type: "DOCUMENTS_CREATED" }, - { event_type: "DOCUMENTS_ACCEPTED" }, - ] + pattern: [{event_type: "card_order_billed"}, {event_type: "DOCUMENTS_CREATED"}, {event_type: "DOCUMENTS_ACCEPTED"}] path: "Contract/" - filename: "{iso_date} {document_title}" - - + filename: "{iso_date} {document_title} - {event_title}" + order_expired: - pattern: [ - { event_type: "ORDER_EXPIRED"}, - ] + pattern: [{event_type: "ORDER_EXPIRED"}] path: "Stocks/Order canceled/{iso_date_year}/" filename: "{iso_date} {document_title}" From 0f8b7f7a7fa2862ebb7a29843180673d3e21178c Mon Sep 17 00:00:00 2001 From: bjbe Date: Sun, 1 Sep 2024 11:24:48 +0200 Subject: [PATCH 09/11] updated destination config --- .../file_destination_config__template.yaml | 149 ++++++++++-------- 1 file changed, 83 insertions(+), 66 deletions(-) diff --git a/pytr/config/file_destination_config__template.yaml b/pytr/config/file_destination_config__template.yaml index b63d1b4d..344ef826 100644 --- a/pytr/config/file_destination_config__template.yaml +++ b/pytr/config/file_destination_config__template.yaml @@ -13,7 +13,7 @@ destination: # if pattern found multiple times, use this block multiple_match: - path: "MultipleMatchPattern/{section_title}/" + path: "MultipleMatch/{section_title}/" filename: "{iso_date}.{iso_time} {event_type} - {event_subtitle} - {document_title} - {event_title}" ################################################################################################ @@ -25,18 +25,18 @@ destination: pattern: [ {event_type: "ORDER_EXECUTED", document_title: "Abrechnung(.\\d+)?"}, # mit limit verkauft {event_type: "TRADE_INVOICE", document_title: "Abrechnung(.\\d+)?"}, # mit limit gekauft - {event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", document_title: "Abrechnung(.\\d+)?"}, # Aktiengeschenk - {event_type: "SHAREBOOKING", event_subtitle: "Verkauf", document_title: "Abrechnung(.\\d+)?"}, # Kapitalmassnahme + {event_type: "STOCK_PERK_REFUNDED", document_title: "Abrechnung(.\\d+)?"}, # Aktiengeschenk + {event_type: "SHAREBOOKING", document_title: "Abrechnung(.\\d+)?"}, # Kapitalmassnahme ] path: "Stocks/Settlement/{iso_date_year}/" stock_order_cost_report: pattern: [ - {event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", document_title: "Kosteninformation(.\\d+)?"}, # limit erstellt + {event_type: "ORDER_CREATED", document_title: "Kosteninformation(.\\d+)?"}, # limit erstellt {event_type: "ORDER_EXECUTED", document_title: "Kosteninformation(.\\d+)?"}, {event_type: "TRADE_INVOICE", document_title: "Kosteninformation(.\\d+)?"}, - {event_type: "STOCK_PERK_REFUNDED", event_subtitle: "Eingelöst", document_title: "Kosteninformation(.\\d+)?"}, # Aktiengeschenk - {event_type: "EX_POST_COST_REPORT", event_title: "Ex-Post Kosteninformation", document_title: "Ex-Post Kosteninformation(.\\d+)?"}, + {event_type: "STOCK_PERK_REFUNDED", document_title: "Kosteninformation(.\\d+)?"}, # Aktiengeschenk + {event_type: "EX_POST_COST_REPORT"}, ] path: "Stocks/Cost report/{iso_date_year}/" @@ -45,26 +45,31 @@ destination: path: "Stocks/Order created/{iso_date_year}/" stock_order_canceled: - pattern: [{event_type: "TRADE_CANCELED", document_title: "Abrechnung(.\\d+)?"}, {event_type: "ORDER_CANCELED", document_title: "Löschbestätigung(.\\d+)?"}] + pattern: [{event_type: "TRADE_CANCELED"}, {event_type: "ORDER_CANCELED"}] path: "Stocks/Order canceled/{iso_date_year}/" + order_expired: + pattern: [{event_type: "ORDER_EXPIRED"}] + path: "Stocks/Order canceled/{iso_date_year}/" + filename: "{iso_date} {document_title}" + # Kapitalmaßnahmen stock_notice_1: - pattern: [{event_type: "EXERCISE", event_subtitle: "Ausübung", document_title: "Kundenanschreiben(.\\d+)?"}, {event_type: "SHAREBOOKING", document_title: "Ausführungsanzeige(.\\d+)?"}] + pattern: [{event_type: "EXERCISE"}, {event_type: "SHAREBOOKING", document_title: "Ausführungsanzeige(.\\d+)?"}] path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" # split for better readability stock_notice_2: - pattern: [{event_type: "CORPORATE_ACTION", event_subtitle: "Kapitalherabsetzung", document_title: "Abrechnung(.\\d+)?"}, {event_type: "SHAREBOOKING_CANCELED"}] + pattern: [{event_type: "CORPORATE_ACTION"}, {event_type: "SHAREBOOKING_CANCELED"}] path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" # split for better readability stock_notice_3: pattern: [ - {event_type: "SHAREBOOKING_TRANSACTIONAL", document_title: "Abrechnung(.\\d+)?"}, - {event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", document_title: "Kundenanschreiben(.\\d+)?"}, #Kapitalerhöhung gegen Bar + {event_type: "SHAREBOOKING_TRANSACTIONAL"}, + {event_type: "INSTRUCTION_CORPORATE_ACTION", document_title: "Kundenanschreiben(.\\d+)?"}, #Kapitalerhöhung gegen Bar ] path: "Stocks/Notice/{iso_date_year}/" filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" @@ -73,67 +78,71 @@ destination: stock_notice_4: pattern: [ - {event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Spin-off", document_title: "Dokumente(.\\d+)?"}, - {event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Reverse Split", document_title: "Dokumente(.\\d+)?"}, - {event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Aktiendividende", document_title: "Dokumente(.\\d+)?"}, - {event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Zwischenvertrieb von Wertpapieren", document_title: "Dokumente(.\\d+)?"}, - {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Wechsel", document_title: "Information(.\\d+)?"}, - {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Information", document_title: "Information(.\\d+)?"}, - {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Außerordentliche oder spezielle Hauptversammlung", document_title: "Information(.\\d+)?"}, - {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Aufruf von Zwischenpapieren", document_title: "Kapitalmaßnahmen(.\\d+)?"}, + {event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Spin-off"}, + {event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Reverse Split"}, + {event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Aktiendividende"}, + {event_type: "ssp_corporate_action_invoice_shares", event_subtitle: "Zwischenvertrieb von Wertpapieren"}, + {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Wechsel"}, + {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Information"}, + {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Aufruf von Zwischenpapieren"}, ] path: "Stocks/Notice/{iso_date_year}/" - filename: "{iso_date}.{iso_time} {event_subtitle} {event_title}" + filename: "{iso_date}.{iso_time} {event_subtitle} - {event_title}" stock_report: - pattern: [{event_type: "QUARTERLY_REPORT", event_title: "Q\\d/\\d+ Abschluss", document_title: "Kontoauszug(.\\d+)?"}, {event_type: "QUARTERLY_REPORT", event_title: "Q\\d/\\d+ Abschluss", document_title: "Depotauszug(.\\d+)?"}, {event_type: "QUARTERLY_REPORT", event_title: "Q\\d/\\d+ Abschluss", document_title: "Cryptoauszug(.\\d+)?"}] + pattern: [{event_type: "QUARTERLY_REPORT", document_title: "Kontoauszug(.\\d+)?"}, {event_type: "QUARTERLY_REPORT", document_title: "Depotauszug(.\\d+)?"}, {event_type: "QUARTERLY_REPORT", document_title: "Cryptoauszug(.\\d+)?"}] path: "Stocks/Report/{iso_date_year}/" filename: "{iso_date} {document_title} {event_title}" # General Meetings stock_general_meetings: - pattern: [{event_type: "GENERAL_MEETING"}, {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Jährliche Hauptversammlung"}] + pattern: [{event_type: "GENERAL_MEETING", document_title: "Hauptversammlung"}, {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Jährliche Hauptversammlung"}] + path: "Stocks/General Meetings/{iso_date_year}/" + + stock_general_meetings_multiple_files: + pattern: [{event_type: "GENERAL_MEETING", document_title: "Hauptversammlung \\d+"}] path: "Stocks/General Meetings/{iso_date_year}/" + filename: "{iso_date}.{iso_time} {event_title} - {document_title}" + + stock_special_meetings: + pattern: [{event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Außerordentliche oder spezielle Hauptversammlung"}] + path: "Stocks/General Meetings/{iso_date_year}/" + filename: "{iso_date}.{iso_time} {event_subtitle}" # Savings plan savings_plan: - pattern: [{event_type: "SAVINGS_PLAN_INVOICE_CREATED"}, {event_type: "SAVINGS_PLAN_EXECUTED"}, {event_type: "SAVINGS_PLAN_CANCELED", event_subtitle: "Sparplan storniert"}] + pattern: [{event_type: "SAVINGS_PLAN_INVOICE_CREATED"}, {event_type: "SAVINGS_PLAN_EXECUTED"}, {event_type: "SAVINGS_PLAN_CANCELED"}] path: "Stocks/Savings plan/{iso_date_year}/" # pre-determined tax base earning stock_pre_earning_tax: - pattern: [{event_type: "PRE_DETERMINED_TAX_BASE_EARNING", event_subtitle: "Vorabpauschale", document_title: "Vorabpauschale(.\\d+)?"}] + pattern: [{event_type: "PRE_DETERMINED_TAX_BASE_EARNING"}] path: "Stocks/PreEarningTax/{iso_date_year}/" # Dividends dividends_received: - pattern: [{event_type: "CREDIT", event_subtitle: "Dividende", document_title: "Abrechnung(.\\d+)?"}, {event_type: "CREDIT", event_subtitle: "Ausschüttung", document_title: "Abrechnung(.\\d+)?"}, {event_type: "CREDIT_CANCELED", event_subtitle: "Dividende", document_title: "Abrechnung(.\\d+)?"}, {event_type: "ssp_corporate_action_invoice_cash"}] + pattern: [{event_type: "CREDIT", event_subtitle: "Dividende"}, {event_type: "CREDIT", event_subtitle: "Ausschüttung"}, {event_type: "CREDIT_CANCELED"}, {event_type: "ssp_corporate_action_invoice_cash"}] path: "Dividends/{iso_date_year}/" # Dividends Corporate action election dividends_election: - pattern: - [ - {event_type: "INSTRUCTION_CORPORATE_ACTION", event_subtitle: "Weisung zur Aktiendividende", document_title: "Dividende Wahlweise(.\\d+)?"}, - {event_type: "ssp_dividend_option_customer_instruction", event_subtitle: "Cash oder Aktie", document_title: "Dividende Wahlweise(.\\d+)?"}, - {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Dividende Wahlweise", document_title: "Kapitalmaßnahmen(.\\d+)?"}, - ] + pattern: [{event_type: "INSTRUCTION_CORPORATE_ACTION", document_title: "Dividende Wahlweise(.\\d+)?"}, {event_type: "ssp_dividend_option_customer_instruction", event_subtitle: "Cash oder Aktie"}, {event_type: "ssp_corporate_action_informative_notification", event_subtitle: "Dividende Wahlweise"}] path: "Dividendelection/{iso_date_year}/" # bonds bond_repayment: - pattern: [{event_type: "REPAYMENT", event_subtitle: "Tilgung", document_title: "Abrechnung(.\\d+)?"}] + pattern: [{event_type: "REPAYMENT"}] path: "Bonds/{iso_date_year}/" filename: "{iso_date}.{iso_time} Repayment {event_title}" bond_interest: - pattern: [{event_type: "COUPON_PAYMENT", event_subtitle: "Coupon Zahlung", document_title: "Abrechnung(.\\d+)?"}] + pattern: [{event_type: "COUPON_PAYMENT"}] path: "Bonds/{iso_date_year}/" filename: "{iso_date}.{iso_time} Interest {event_title}" # Saveback saveback_enabled: - pattern: [{event_type: "benefits_saveback_execution", document_title: "Aktivierung(.\\d+)?"}] + pattern: [{event_type: "benefits_saveback_execution", document_title: "Enabled(.\\d+)?"}] path: "Saveback/{iso_date_year}/" filename: "{iso_date}.{iso_time} Enabled {event_title}" @@ -149,71 +158,79 @@ destination: # Round up roundup_enabled: - pattern: [ - {event_type: "benefits_spare_change_execution", event_subtitle: "Round up", document_title: "Aktivierung(.\\d+)?"}, # same files - multiple times at once - ] + pattern: [{event_type: "benefits_spare_change_execution", document_title: "Enabled(.\\d+)?"}] # same files - multiple times at once path: "Roundup/{iso_date_year}/" filename: "{iso_date}.{iso_time} Enabled {event_title}" roundup_executed: - pattern: [{event_type: "benefits_spare_change_execution", event_subtitle: "Round up", document_title: "Abrechnung Ausführung(.\\d+)?"}] + pattern: [{event_type: "benefits_spare_change_execution", document_title: "Abrechnung Ausführung(.\\d+)?"}] path: "Roundup/{iso_date_year}/" filename: "{iso_date}.{iso_time} Report {event_title}" roundup_cost_report: - pattern: [{event_type: "benefits_spare_change_execution", event_subtitle: "Round up", document_title: "Kosteninformation(.\\d+)?"}] + pattern: [{event_type: "benefits_spare_change_execution", document_title: "Kosteninformation(.\\d+)?"}] path: "Roundup/{iso_date_year}/" filename: "{iso_date}.{iso_time} Cost report {event_title}" # account cash_interest: - pattern: [{event_type: "INTEREST_PAYOUT", event_title: "Zinsen", document_title: "Abrechnung(.\\d+)?"}, {event_type: "INTEREST_PAYOUT_CREATED", event_title: "Zinsen", document_title: "Abrechnung(.\\d+)?"}] + pattern: [{event_type: "INTEREST_PAYOUT"}, {event_type: "INTEREST_PAYOUT_CREATED"}] path: "Cash Interest/" - filename: "{iso_date} Zinsabrechnung" + filename: "{iso_date} Report" cash_transfer_report: - pattern: [{event_type: "INCOMING_TRANSFER", document_title: "Transaktionsbestätigung(.\\d+)?"}] + pattern: [{event_type: "INCOMING_TRANSFER"}] path: "Cash Report/" filename: "{iso_date}.{iso_time} {document_title}" # {event_title} = Personal name # annual tax report for account account_tax_report: - pattern: [{event_type: "TAX_REFUND"}, {event_type: "TAX_ENGINE_ANNUAL_REPORT"}, {event_type: "YEAR_END_TAX_REPORT", document_title: "Steuerreport(.\\d+)?"}] + pattern: [{event_type: "TAX_REFUND"}, {event_type: "TAX_ENGINE_ANNUAL_REPORT"}, {event_type: "YEAR_END_TAX_REPORT"}] path: "Tax/" filename: "{iso_date} {document_title}" account_tax_adjustment: - pattern: [{event_type: "ssp_tax_correction_invoice", event_title: "Steuerkorrektur", document_title: "Steuerabrechnung(.\\d+)?"}, {event_type: "TAX_CORRECTION", event_subtitle: "Steuerbuchung", event_title: "Steuerabrechnung", section_title: "Dokumente", document_title: "Abrechnung(.\\d+)?"}] + pattern: [{event_type: "ssp_tax_correction_invoice"}, {event_type: "TAX_CORRECTION"}] path: "Tax/" filename: "{iso_date} {event_title}" # common informations notice_stocks: - pattern: [ - {event_type: "CUSTOMER_CREATED", event_subtitle: "Erhalten", document_title: "Basisinformationen über Wertpapiere(.\\d+)?"}, - {event_type: "ORDER_CREATED", event_subtitle: "Limit-Buy-Order erstellt", document_title: "Basisinformationsblatt(.\\d+)?"}, - {event_type: "DOCUMENTS_CHANGED"}, # same file as below - ] - path: "Notice/" - filename: "{iso_date} {document_title}" + pattern: [{event_type: "ORDER_CREATED", document_title: "Basisinformationsblatt(.\\d+)?"}] + path: "Notice/{iso_date_year}/" + filename: "{iso_date} {document_title} - {event_title}" - notice_crypto: - pattern: - [ - {event_type: "TRADE_INVOICE", document_title: "Basisinformationsblatt(.\\d+)?"}, - {event_type: "ORDER_EXECUTED", document_title: "Basisinformationsblatt(.\\d+)?"}, - {event_type: "GESH_CORPORATE_ACTION", event_subtitle: "Unternehmensmeldung", document_title: "Kundenanschreiben(.\\d+)?"}, - {event_type: "GESH_CORPORATE_ACTION_MULTIPLE_POSITIONS", event_subtitle: "Gesellschaftshinweis", document_title: "Kundenanschreiben(.\\d+)?"}, - ] - path: "Notice/" + notice_stocks2: + pattern: [{event_type: "TRADE_INVOICE", document_title: "Basisinformationsblatt(.\\d+)?"}, {event_type: "GESH_CORPORATE_ACTION", event_subtitle: "Unternehmensmeldung"}] + path: "Notice/{iso_date_year}/" + filename: "{iso_date} {event_subtitle} - {event_title}" + + notice_stocks3: + pattern: [{event_type: "CUSTOMER_CREATED"}] + path: "Notice/{iso_date_year}/" filename: "{iso_date} {document_title}" + notice_option_contract: + pattern: [{event_type: "ORDER_EXECUTED", document_title: "Basisinformationsblatt(.\\d+)?"}] + path: "Notice/{iso_date_year}/" + filename: "{iso_date} {document_title} Option" + + notice_multiple_documents: + pattern: [{event_type: "GESH_CORPORATE_ACTION_MULTIPLE_POSITIONS"}] #event_subtitle: Gesellschaftshinweis + path: "Notice/{iso_date_year}/" + filename: "{iso_date} {event_subtitle} - {event_title} - {document_title}" + contract_documents: - pattern: [{event_type: "card_order_billed"}, {event_type: "DOCUMENTS_CREATED"}, {event_type: "DOCUMENTS_ACCEPTED"}] + pattern: [ + {event_type: "card_order_billed"}, # Bestellung Trade Republic Karte + {event_type: "DOCUMENTS_CREATED"}, # Basisinformationen über Wertpapiere + {event_type: "DOCUMENTS_ACCEPTED"}, # Rechtliche Dokumente: Kundenvereinbarung / Vorvertragliche Informationen / Datenschutzinformationen* / Widerrufsbelehrung* / *Crypto* / Risikohinweise + {event_type: "DOCUMENTS_CHANGED", section_title: "Dokumente"}, # Rechtliche Dokumente: Kundenvereinbarung + ] path: "Contract/" - filename: "{iso_date} {document_title} - {event_title}" + filename: "{iso_date} {event_title} - {document_title}" - order_expired: - pattern: [{event_type: "ORDER_EXPIRED"}] - path: "Stocks/Order canceled/{iso_date_year}/" - filename: "{iso_date} {document_title}" + contract_documents_updated: + pattern: [{event_type: "DOCUMENTS_CHANGED", section_title: "Aktualisierte Dokumente"}] # aktualisierte Rechtliche Dokumente: Kundenvereinbarung + path: "Contract/" + filename: "{iso_date} {event_title} - {document_title} updated" From c05a4e341229bed78bfad403078bf2fe75b4411e Mon Sep 17 00:00:00 2001 From: bjbe Date: Sun, 1 Sep 2024 11:47:29 +0200 Subject: [PATCH 10/11] added missing event types --- pytr/config/file_destination_config__template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pytr/config/file_destination_config__template.yaml b/pytr/config/file_destination_config__template.yaml index 344ef826..29551f0e 100644 --- a/pytr/config/file_destination_config__template.yaml +++ b/pytr/config/file_destination_config__template.yaml @@ -179,7 +179,7 @@ destination: filename: "{iso_date} Report" cash_transfer_report: - pattern: [{event_type: "INCOMING_TRANSFER"}] + pattern: [{event_type: "INCOMING_TRANSFER"}, {event_type: "PAYMENT_INBOUND_GOOGLE_PAY"}, {event_type: "PAYMENT_INBOUND_CREDIT_CARD"}] path: "Cash Report/" filename: "{iso_date}.{iso_time} {document_title}" # {event_title} = Personal name From 382301fa516ed9f13cc1ac296231276939df875c Mon Sep 17 00:00:00 2001 From: bjbe Date: Sat, 30 Nov 2024 10:35:15 +0100 Subject: [PATCH 11/11] fixed formatting --- pytr/app_path.py | 10 +- pytr/file_destination_provider.py | 161 ++++++++++++++++++++---------- 2 files changed, 112 insertions(+), 59 deletions(-) diff --git a/pytr/app_path.py b/pytr/app_path.py index 01b34f0f..4a8b8ebb 100644 --- a/pytr/app_path.py +++ b/pytr/app_path.py @@ -1,10 +1,10 @@ import pathlib home = pathlib.Path.home() -BASE_DIR = home / '.pytr' +BASE_DIR = home / ".pytr" -CREDENTIALS_FILE = BASE_DIR / 'credentials' -KEY_FILE = BASE_DIR / 'keyfile.pem' -COOKIES_FILE = BASE_DIR / 'cookies.txt' +CREDENTIALS_FILE = BASE_DIR / "credentials" +KEY_FILE = BASE_DIR / "keyfile.pem" +COOKIES_FILE = BASE_DIR / "cookies.txt" -DESTINATION_CONFIG_FILE = BASE_DIR / 'file_destination_config.yaml' \ No newline at end of file +DESTINATION_CONFIG_FILE = BASE_DIR / "file_destination_config.yaml" diff --git a/pytr/file_destination_provider.py b/pytr/file_destination_provider.py index 19306216..b0e5cb65 100644 --- a/pytr/file_destination_provider.py +++ b/pytr/file_destination_provider.py @@ -10,27 +10,20 @@ from yaml import safe_load from pathlib import Path from pytr.app_path import DESTINATION_CONFIG_FILE -from pytr.utils import get_logger +from pytr.utils import get_logger DEFAULT_CONFIG = "default" UNKNOWN_CONFIG = "unknown" MULTIPLE_MATCH_CONFIG = "multiple_match" -TEMPLATE_FILE_NAME ="file_destination_config__template.yaml" +TEMPLATE_FILE_NAME = "file_destination_config__template.yaml" # Invalid characters translation table, for cleaning up the variables before using them. # This was done to avoid issues with for example 'event_subtitle: “Umtausch/Bezug”' which caused a directory which was unintentional. -INVALID_CHARS_TRANSLATION_TABLE = str.maketrans({ - '"': '', - '?': '', - '<': '', - '>': '', - '*': '', - '|': '-', - '/': '-', - '\\': '-' -}) +INVALID_CHARS_TRANSLATION_TABLE = str.maketrans( + {'"': "", "?": "", "<": "", ">": "", "*": "", "|": "-", "/": "-", "\\": "-"} +) class DefaultFormateValue(dict): @@ -58,11 +51,11 @@ class Pattern: class FileDestinationProvider: def __init__(self): - ''' + """ A provider for file path and file names based on the event type and other parameters. - ''' + """ self._log = get_logger(__name__) - + config_file_path = Path(DESTINATION_CONFIG_FILE) if config_file_path.is_file() == False: self.__create_default_config(config_file_path) @@ -73,28 +66,50 @@ def __init__(self): self.__validate_config(destination_config) destinations = destination_config["destination"] - + self._destination_configs: list[DestinationConfig] = [] for config_name in destinations: if config_name == DEFAULT_CONFIG: self._default_file_config = DestinationConfig( - DEFAULT_CONFIG, destinations[DEFAULT_CONFIG]["filename"]) + DEFAULT_CONFIG, destinations[DEFAULT_CONFIG]["filename"] + ) elif config_name == UNKNOWN_CONFIG: self._unknown_file_config = DestinationConfig( - UNKNOWN_CONFIG, destinations[UNKNOWN_CONFIG]["filename"], destinations[UNKNOWN_CONFIG]["path"]) + UNKNOWN_CONFIG, + destinations[UNKNOWN_CONFIG]["filename"], + destinations[UNKNOWN_CONFIG]["path"], + ) elif config_name == MULTIPLE_MATCH_CONFIG: - self._multiple_match_file_config = DestinationConfig( - MULTIPLE_MATCH_CONFIG, destinations[MULTIPLE_MATCH_CONFIG]["filename"], destinations[MULTIPLE_MATCH_CONFIG]["path"]) + self._multiple_match_file_config = DestinationConfig( + MULTIPLE_MATCH_CONFIG, + destinations[MULTIPLE_MATCH_CONFIG]["filename"], + destinations[MULTIPLE_MATCH_CONFIG]["path"], + ) else: patterns = self.__extract_pattern( - destinations[config_name].get("pattern", None)) + destinations[config_name].get("pattern", None) + ) for pattern in patterns: - self._destination_configs.append(DestinationConfig( - config_name, destinations[config_name].get("filename", None), destinations[config_name].get("path", None), pattern)) - - def get_file_path(self, event_type: str, event_title: str, event_subtitle: str, section_title: str, document_title: str, variables: dict) -> str: - ''' + self._destination_configs.append( + DestinationConfig( + config_name, + destinations[config_name].get("filename", None), + destinations[config_name].get("path", None), + pattern, + ) + ) + + def get_file_path( + self, + event_type: str, + event_title: str, + event_subtitle: str, + section_title: str, + document_title: str, + variables: dict, + ) -> str: + """ Get the file path based on the event type and other parameters. Parameters: @@ -104,38 +119,54 @@ def get_file_path(self, event_type: str, event_title: str, event_subtitle: str, section_title (str): The section title document_title (str): The document title variables (dict): The variables->value dict to be used in the file path and file name format. - ''' - - doc = Pattern(event_type, event_title, event_subtitle, section_title, document_title) + """ + + doc = Pattern( + event_type, event_title, event_subtitle, section_title, document_title + ) matching_configs = self._destination_configs.copy() # create a dictionary that maps the field names to their values in the pattern instance - pattern_dict = {field.name: getattr(doc, field.name) for field in fields(Pattern)} + pattern_dict = { + field.name: getattr(doc, field.name) for field in fields(Pattern) + } # iterate over the dictionary to filter the matching_configs list and update the variables dictionary for field_name, search_pattern in pattern_dict.items(): if search_pattern is not None: - matching_configs = list(filter(lambda config: self.__is_matching_config(config, field_name, search_pattern), matching_configs)) - variables[field_name] = search_pattern.translate(INVALID_CHARS_TRANSLATION_TABLE).strip() - + matching_configs = list( + filter( + lambda config: self.__is_matching_config( + config, field_name, search_pattern + ), + matching_configs, + ) + ) + variables[field_name] = search_pattern.translate( + INVALID_CHARS_TRANSLATION_TABLE + ).strip() if len(matching_configs) == 0: self._log.debug( - f"No destination config found for the given parameters: event_type:{event_type}, event_title:{event_title},event_subtitle:{event_subtitle},section_title:{section_title},document_title:{document_title}") + f"No destination config found for the given parameters: event_type:{event_type}, event_title:{event_title},event_subtitle:{event_subtitle},section_title:{section_title},document_title:{document_title}" + ) return self.__create_file_path(self._unknown_file_config, variables) if len(matching_configs) > 1: - self._log.debug(f"Multiple Destination Patterns where found. Using 'multiple_match' config! Parameter: event_type:{event_type}, event_title:{event_title},event_subtitle:{event_subtitle},section_title:{section_title},document_title:{document_title}") + self._log.debug( + f"Multiple Destination Patterns where found. Using 'multiple_match' config! Parameter: event_type:{event_type}, event_title:{event_title},event_subtitle:{event_subtitle},section_title:{section_title},document_title:{document_title}" + ) return self.__create_file_path(self._multiple_match_file_config, variables) return self.__create_file_path(matching_configs[0], variables) @staticmethod - def __is_matching_config(config: DestinationConfig, field_name: str, search_pattern: str) -> bool: + def __is_matching_config( + config: DestinationConfig, field_name: str, search_pattern: str + ) -> bool: pattern = config.pattern - return ( - getattr(pattern, field_name, None) is None - or re.fullmatch(getattr(pattern, field_name, None), search_pattern) + return getattr(pattern, field_name, None) is None or re.fullmatch( + getattr(pattern, field_name, None), search_pattern ) def __create_file_path(self, config: DestinationConfig, variables: dict): @@ -151,11 +182,15 @@ def __create_file_path(self, config: DestinationConfig, variables: dict): def __extract_pattern(self, pattern_config: list) -> list: patterns = [] for pattern in pattern_config: - patterns.append(Pattern(pattern.get("event_type", None), - pattern.get("event_title", None), - pattern.get("event_subtitle", None), - pattern.get("section_title", None), - pattern.get("document_title", None))) + patterns.append( + Pattern( + pattern.get("event_type", None), + pattern.get("event_title", None), + pattern.get("event_subtitle", None), + pattern.get("section_title", None), + pattern.get("document_title", None), + ) + ) return patterns @@ -166,22 +201,40 @@ def __validate_config(self, destination_config: dict): destinations = destination_config["destination"] # Check if default config is present - if DEFAULT_CONFIG not in destinations or "filename" not in destinations[DEFAULT_CONFIG]: + if ( + DEFAULT_CONFIG not in destinations + or "filename" not in destinations[DEFAULT_CONFIG] + ): raise ValueError( - "'default' config not found or filename is not present in 'default' config") - - if UNKNOWN_CONFIG not in destinations or "filename" not in destinations[UNKNOWN_CONFIG] or "path" not in destinations[UNKNOWN_CONFIG]: + "'default' config not found or filename is not present in 'default' config" + ) + + if ( + UNKNOWN_CONFIG not in destinations + or "filename" not in destinations[UNKNOWN_CONFIG] + or "path" not in destinations[UNKNOWN_CONFIG] + ): raise ValueError( - "'unknown' config not found or filename/path is not present in 'unknown' config") - - if MULTIPLE_MATCH_CONFIG not in destinations or "filename" not in destinations[MULTIPLE_MATCH_CONFIG] or "path" not in destinations[MULTIPLE_MATCH_CONFIG]: + "'unknown' config not found or filename/path is not present in 'unknown' config" + ) + + if ( + MULTIPLE_MATCH_CONFIG not in destinations + or "filename" not in destinations[MULTIPLE_MATCH_CONFIG] + or "path" not in destinations[MULTIPLE_MATCH_CONFIG] + ): raise ValueError( - "'multiple_match' config not found or filename/path is not present in 'multiple_match' config") + "'multiple_match' config not found or filename/path is not present in 'multiple_match' config" + ) for config_name in destinations: - if config_name != DEFAULT_CONFIG and "path" not in destinations[config_name]: + if ( + config_name != DEFAULT_CONFIG + and "path" not in destinations[config_name] + ): raise ValueError( - f"'{config_name}' has no path defined in destination config") + f"'{config_name}' has no path defined in destination config" + ) def __create_default_config(self, config_file_path: Path): path = files(pytr.config).joinpath(TEMPLATE_FILE_NAME)