Archive for the ‘Programmierung’ Category.
2025-08-23, 16:33
Excel 365 aus Office365 hat eine Reihe neuer Funktionen, die ich auch noch nicht kannte. Hier eine Liste, die mir ChatGPT ausgespuckt hat.
| Name (Englisch) |
Erläuterung |
| TEXTKETTE (TEXTJOIN) |
Verbindet Text aus mehreren Zellen mit einem Trennzeichen. Unterstützt auch Zellbereiche. |
| TEXTVERKETTEN (CONCAT) |
Ähnlich wie VERKETTEN, aber flexibler und moderner. |
| WENNS (IFS) |
Ersetzt verschachtelte WENN-Funktionen durch eine klarere Syntax. |
| MAXWENNS (MAXIFS) |
Gibt den größten Wert zurück, der bestimmte Kriterien erfüllt. |
| MINWENNS (MINIFS) |
Gibt den kleinsten Wert zurück, der bestimmte Kriterien erfüllt. |
| ERSTERWERT (SWITCH) |
Gibt einen Wert basierend auf dem ersten zutreffenden Vergleich zurück. |
| FILTER |
Filtert Daten dynamisch basierend auf Bedingungen. |
| SORTIEREN (SORT) |
Sortiert Daten dynamisch. |
| SORTIERENNACH (SORTBY) |
Sortiert Daten basierend auf einem anderen Bereich. |
| EINDEUTIG (UNIQUE) |
Gibt eindeutige Werte aus einem Bereich zurück. |
| SEQUENZ (SEQUENCE) |
Erstellt eine Liste von Zahlen in einer Sequenz. |
| WAHL.ZEILE (XLOOKUP) |
Moderner Ersatz für SVERWEIS und WVERWEIS. |
| WAHL.BEREICH (XMATCH) |
Gibt die Position eines Werts in einem Bereich zurück. |
| LET |
Ermöglicht das Definieren von Variablen innerhalb einer Formel. |
| LAMBDA |
Erstellt benutzerdefinierte Funktionen direkt in Excel. |
| TEXTSPLIT |
Teilt Text anhand eines Trennzeichens in mehrere Zellen auf. |
| TEXTBEFORE / TEXTAFTER |
Gibt Text vor oder nach einem bestimmten Zeichen zurück. |
| VSTACK / HSTACK |
Stapelt Zellbereiche vertikal oder horizontal. |
| WRAPROWS / WRAPCOLS |
Wandelt eine Liste in ein Array mit mehreren Zeilen oder Spalten um. |
| TAKE / DROP |
Gibt die ersten/letzten Elemente eines Bereichs zurück oder entfernt sie. |
| EXPAND |
Erweitert einen Bereich auf eine bestimmte Größe. |
| TOCOL / TOROW |
Wandelt einen Bereich in eine Spalte oder Zeile um. |
Category:
MS Office & VBA |
Kommentare deaktiviert für Neue Excel Office365 Funktionen
2025-07-30, 22:21
Schauen wir uns das Forward Filling an. Je nach Version des SQL Servers funktioniert eine Version oder leider nicht. 🙂
Mit SQL Server 2022 kann man den folgenden Code nutzen, um einen weiteren CTE zu bauen:
Filled AS (
SELECT
ContNo,
MonthEnd,
Amount,
last_value(Amount) IGNORE NULLS
OVER (
PARTITION BY ContNo
ORDER BY MonthEnd
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS ForwardFilledAmount
FROM Combined
)
SELECT * FROM Filled |
Filled AS (
SELECT
ContNo,
MonthEnd,
Amount,
last_value(Amount) ignore nulls
OVER (
PARTITION BY ContNo
ORDER BY MonthEnd
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
) AS ForwardFilledAmount
FROM Combined
)
select * from Filled
Versionen von SQL Server vor 2022 unterstützen leider „ignore nulls“ nicht, hier kann man dann folgenden Code nutzen:
Filled AS (
SELECT
c.ContNo,
c.MonthEnd,
c.Amount,
ca.ForwardFilledAmount
FROM Combined c
OUTER APPLY (
SELECT TOP 1 Amount AS ForwardFilledAmount
FROM Combined c2
WHERE c2.ContNo = c.ContNo
AND c2.MonthEnd <= c.MonthEnd
AND c2.Amount IS NOT NULL
ORDER BY c2.MonthEnd DESC
) ca
)
SELECT * FROM Filled |
Filled AS (
SELECT
c.ContNo,
c.MonthEnd,
c.Amount,
ca.ForwardFilledAmount
FROM Combined c
OUTER APPLY (
SELECT TOP 1 Amount AS ForwardFilledAmount
FROM Combined c2
WHERE c2.ContNo = c.ContNo
AND c2.MonthEnd <= c.MonthEnd
AND c2.Amount IS NOT NULL
ORDER BY c2.MonthEnd DESC
) ca
)
select * from Filled
OUTER APPLY funktioniert dabei so ähnlich wie ein LEFT JOIN. Der Unterschied besteht darin, dass beim OUTER APPLY die rechte Seite von der linken Seite abhängt. In einem weiteren Artikel zum Thema werde ich noch ein paar Beispiele dazu zeigen. CROSS APPLY ist ähnlich, hier ist es aber kein Äquivalent zum LEFT JOIN, sondern zum INNER JOIN. Auch dazu mehr in einem der weiteren Artikel.
Category:
SQL |
Kommentare deaktiviert für Outer Apply und Cross Apply in SQL nutzen – NULL-Werte auffüllen
2025-07-28, 08:15
Wir kennen jetzt das Problem, nun können wir uns an die Lösung wagen! In diesem Blogbeitrag werden wir alles vorbereiten, was wir für das anschließende „Forward Filling“ der Daten brauchen.
Im ersten Schritt bestimmen wir für die einzelnen Verträge die Monatsendwerte von Start des jeweiligen Vertrags bis zu dessen Ende. Dazu nutze ich den den folgenden rekursiven CTE (Common Table Expression)
WITH MonthSequence AS (
SELECT
c.ContNo,
EOMONTH(c.StartDate) AS MonthEnd,
c.EndDate
FROM Contracts c
UNION ALL
SELECT
ms.ContNo,
EOMONTH(DATEADD(MONTH, 1, ms.MonthEnd)),
ms.EndDate
FROM MonthSequence ms
WHERE EOMONTH(DATEADD(MONTH, 1, ms.MonthEnd)) <= ms.EndDate
) |
WITH MonthSequence AS (
SELECT
c.ContNo,
EOMONTH(c.StartDate) AS MonthEnd,
c.EndDate
FROM Contracts c
UNION ALL
SELECT
ms.ContNo,
EOMONTH(DATEADD(MONTH, 1, ms.MonthEnd)),
ms.EndDate
FROM MonthSequence ms
WHERE EOMONTH(DATEADD(MONTH, 1, ms.MonthEnd)) <= ms.EndDate
)
Das erzeugt uns die folgende Sequenz:
| ContNo |
MonthEnd |
EndDate |
| 123 |
2025-01-31 |
2025-12-31 |
| 123 |
2025-02-28 |
2025-12-31 |
| 123 |
2025-03-31 |
2025-12-31 |
| 123 |
2025-04-30 |
2025-12-31 |
| 123 |
2025-05-31 |
2025-12-31 |
| 123 |
2025-06-30 |
2025-12-31 |
| 123 |
2025-07-31 |
2025-12-31 |
| 123 |
2025-08-31 |
2025-12-31 |
| 123 |
2025-09-30 |
2025-12-31 |
| 123 |
2025-10-31 |
2025-12-31 |
| 123 |
2025-11-30 |
2025-12-31 |
| 123 |
2025-12-31 |
2025-12-31 |
| 456 |
2024-01-31 |
2024-12-31 |
| 456 |
2024-02-29 |
2024-12-31 |
| 456 |
2024-03-31 |
2024-12-31 |
| 456 |
2024-04-30 |
2024-12-31 |
| 456 |
2024-05-31 |
2024-12-31 |
| 456 |
2024-06-30 |
2024-12-31 |
| 456 |
2024-07-31 |
2024-12-31 |
| 456 |
2024-08-31 |
2024-12-31 |
| 456 |
2024-09-30 |
2024-12-31 |
| 456 |
2024-10-31 |
2024-12-31 |
| 456 |
2024-11-30 |
2024-12-31 |
| 456 |
2024-12-31 |
2024-12-31 |
Als nächstes suchen wir uns die End-of-Month der Cashflows raus, die wir im nächsten Schritt mit den End-of-Month aus Schritt 1 kombinieren werden.
WITH CashflowBuckets AS (
SELECT
ContNo,
EOMONTH(CashflowDate) AS MonthEnd,
Amount
FROM Cashflows
)
SELECT * FROM CashflowBuckets |
with CashflowBuckets AS (
SELECT
ContNo,
EOMONTH(CashflowDate) AS MonthEnd,
Amount
FROM Cashflows
)
select * from CashflowBuckets
| ContNo |
MonthEnd |
Amount |
| 123 |
2025-01-31 |
100.00 |
| 123 |
2025-04-30 |
110.00 |
| 123 |
2025-07-31 |
105.00 |
| 123 |
2025-12-31 |
120.00 |
| 456 |
2024-01-31 |
100.00 |
| 456 |
2024-06-30 |
130.00 |
| 456 |
2024-12-31 |
101.00 |
Die Kombination ist dann recht einfach und gibt uns die Liste aller End-of-Months aus mit den dazugehörigen Cashflows.
WITH MonthSequence AS (
SELECT
c.ContNo,
EOMONTH(c.StartDate) AS MonthEnd,
c.EndDate
FROM Contracts c
UNION ALL
SELECT
ms.ContNo,
EOMONTH(DATEADD(MONTH, 1, ms.MonthEnd)),
ms.EndDate
FROM MonthSequence ms
WHERE EOMONTH(DATEADD(MONTH, 1, ms.MonthEnd)) <= ms.EndDate
)
,CashflowBuckets AS (
SELECT
ContNo,
EOMONTH(CashflowDate) AS MonthEnd,
Amount
FROM Cashflows
)
,Combined AS (
SELECT
ms.ContNo,
ms.MonthEnd,
cb.Amount
FROM MonthSequence ms
LEFT JOIN CashflowBuckets cb
ON ms.ContNo = cb.ContNo
AND ms.MonthEnd = cb.MonthEnd
)
SELECT * FROM Combined |
WITH MonthSequence AS (
SELECT
c.ContNo,
EOMONTH(c.StartDate) AS MonthEnd,
c.EndDate
FROM Contracts c
UNION ALL
SELECT
ms.ContNo,
EOMONTH(DATEADD(MONTH, 1, ms.MonthEnd)),
ms.EndDate
FROM MonthSequence ms
WHERE EOMONTH(DATEADD(MONTH, 1, ms.MonthEnd)) <= ms.EndDate
)
,CashflowBuckets AS (
SELECT
ContNo,
EOMONTH(CashflowDate) AS MonthEnd,
Amount
FROM Cashflows
)
,Combined AS (
SELECT
ms.ContNo,
ms.MonthEnd,
cb.Amount
FROM MonthSequence ms
LEFT JOIN CashflowBuckets cb
ON ms.ContNo = cb.ContNo
AND ms.MonthEnd = cb.MonthEnd
)
select * from Combined
In gelb sind die Einträge markiert, die wir im finalen Schritt mit den jeweils letzten gültigen Cashflow-Werten befüllen müssen.
| ContNo |
MonthEnd |
Amount |
| 123 |
2025-01-31 |
100.00 |
| 456 |
2024-01-31 |
100.00 |
| 456 |
2024-02-29 |
NULL |
| 456 |
2024-03-31 |
NULL |
| 456 |
2024-04-30 |
NULL |
| 456 |
2024-05-31 |
NULL |
| 456 |
2024-06-30 |
130.00 |
| 456 |
2024-07-31 |
NULL |
| 456 |
2024-08-31 |
NULL |
| 456 |
2024-09-30 |
NULL |
| 456 |
2024-10-31 |
NULL |
| 456 |
2024-11-30 |
NULL |
| 456 |
2024-12-31 |
101.00 |
| 123 |
2025-02-28 |
NULL |
| 123 |
2025-03-31 |
NULL |
| 123 |
2025-04-30 |
110.00 |
| 123 |
2025-05-31 |
NULL |
| 123 |
2025-06-30 |
NULL |
| 123 |
2025-07-31 |
105.00 |
| 123 |
2025-08-31 |
NULL |
| 123 |
2025-09-30 |
NULL |
| 123 |
2025-10-31 |
NULL |
| 123 |
2025-11-30 |
NULL |
| 123 |
2025-12-31 |
120.00 |
Schlagwörter:
SQL,
T-SQL Category:
Allgemein,
SQL |
Kommentare deaktiviert für Outer Apply und Cross Apply in SQL nutzen – Die Datenmenge bauen
2025-07-27, 20:38
Ich halte mich schon recht erfahren im Umgang SQL, kürzlich bin ich aber an einer Ecke von SQL vorbeigekommen, die ich auch noch nicht kannte. Dabei handelt es sind um die „OUTER APPLY“ bzw. „CROSS APPLY“ Operatoren.
Um die Lösung herzuleiten betrachten wir zuerst das Problem:
Gegeben seien Kreditverträge mit verschiedenen Zahlplänen. Ein Vertrag zahlt vielleicht monatlich, einer zahlt quartalsweise. Ein Vertrag hat dabei ein Startdatum und ein Enddatum sowie verschiedene Cashflows.
Hier ein paar Testdaten mit den entsprechenden Tabellen, die dazugehörigen SQL-Statements folgen später.
Cashflows
| ContNo |
StartDate |
EndDate |
| 123 |
2025-01-01 |
2025-12-31 |
| 456 |
2024-01-01 |
2024-12-31 |
Cashflows
| ContNo |
CashflowDate |
Amount |
| 123 |
2025-01-05 |
100.0 |
| 123 |
2025-04-07 |
110.0 |
| 123 |
2025-07-06 |
105.0 |
| 123 |
2025-12-16 |
120.0 |
| 456 |
2024-01-05 |
100.0 |
| 456 |
2024-06-12 |
130.0 |
| 456 |
2025-12-22 |
101.0 |
Das Problem ist jetzt, wie kann man eine monatliche Übersicht pro Vertrag bekommen, bei der in den Monaten, wo es kein Cashflow gab, einfach der letzte Cashflow angezeigt wird? Das Endergebnis soll wie folgt aussehen:
| ContNo |
CashflowDate |
Amount |
| 123 |
2025-01-31 |
100,00 |
| 123 |
2025-02-28 |
100,00 |
| 123 |
2025-03-31 |
100,00 |
| 123 |
2025-04-30 |
110,00 |
| 123 |
2025-05-31 |
110,00 |
| 123 |
2025-06-30 |
110,00 |
| 123 |
2025-07-31 |
105,00 |
| 123 |
2025-08-31 |
105,00 |
| 123 |
2025-09-30 |
105,00 |
| 123 |
2025-10-31 |
105,00 |
| 123 |
2025-11-30 |
105,00 |
| 123 |
2025-12-31 |
120,00 |
| 456 |
2024-01-31 |
100,00 |
| 456 |
2024-02-29 |
100,00 |
| 456 |
2024-03-31 |
100,00 |
| 456 |
2024-04-30 |
100,00 |
| 456 |
2024-05-31 |
100,00 |
| 456 |
2024-06-30 |
130,00 |
| 456 |
2024-07-31 |
130,00 |
| 456 |
2024-08-31 |
130,00 |
| 456 |
2024-09-30 |
130,00 |
| 456 |
2024-10-31 |
130,00 |
| 456 |
2024-11-30 |
130,00 |
| 456 |
2024-12-31 |
101,00 |
Im nächsten Teil fangen wir dann mit den SQL Statements an.
Category:
Allgemein,
SQL |
Kommentare deaktiviert für Outer Apply und Cross Apply in SQL nutzen – Einleitung
2025-06-21, 22:10
Für ein kleines Projekt brauchte ich die Möglichkeit, flexible Zahlungspläne zu erzeugen. Der folgende Python-Code tut genau das und erzeugt auch gleich passende INSERT Statements für Postgres.
Das CREATE TABLE ist wie folgt:
CREATE TABLE paymentplan (
contract INTEGER NOT NULL,
paymentdate INTEGER NOT NULL,
paymenttype INTEGER NOT NULL,
amount NUMERIC(12, 2) NOT NULL
); |
CREATE TABLE paymentplan (
contract INTEGER NOT NULL,
paymentdate INTEGER NOT NULL,
paymenttype INTEGER NOT NULL,
amount NUMERIC(12, 2) NOT NULL
);
import pandas as pd
from datetime import datetime
from dateutil.relativedelta import relativedelta
def genPaymentPlan(contract, principal, paymentsperyear, interestrate, startdate, years):
interest = interestrate/100*principal/paymentsperyear
date = datetime.strptime(startdate, '%Y%m%d').date()
output = pd.DataFrame(columns=['Contract', 'Paymentdate', 'Paymenttype', 'Amount'])
# Add a row to the DataFrame
start = {'Contract': contract, 'Paymentdate': startdate, 'Paymenttype': 1, 'Amount' : principal}
output = pd.concat([output, pd.DataFrame([start])], ignore_index=True)
for payment in range(paymentsperyear*years):
new_date = (date + relativedelta(months=12/paymentsperyear))
date = new_date
interest_line = {'Contract': contract, 'Paymentdate': new_date.strftime('%Y%m%d'),'Paymenttype': 2, 'Amount' : -1*interest}
output = pd.concat([output, pd.DataFrame([interest_line])], ignore_index=True)
end = {'Contract': contract, 'Paymentdate': date.strftime('%Y%m%d'), 'Paymenttype': 3, 'Amount' : -principal}
output = pd.concat([output, pd.DataFrame([end])], ignore_index=True)
return output
test = genPaymentPlan(123458, 2000, 2, 12, '20260101', 4)
#print(test)
# Generate INSERT statements
print("Generated SQL INSERT statements:\n")
for index, row in test.iterrows():
contract = int(row['Contract'])
date = row['Paymentdate'] # already string in format YYYY-MM-DD
type = row['Paymenttype']
amount = float(row['Amount'])
insert = f"INSERT INTO paymentplan (contract, paymentdate,paymenttype, amount) VALUES ({contract}, '{date}', {type} , {amount});"
print(insert) |
import pandas as pd
from datetime import datetime
from dateutil.relativedelta import relativedelta
def genPaymentPlan(contract, principal, paymentsperyear, interestrate, startdate, years):
interest = interestrate/100*principal/paymentsperyear
date = datetime.strptime(startdate, '%Y%m%d').date()
output = pd.DataFrame(columns=['Contract', 'Paymentdate', 'Paymenttype', 'Amount'])
# Add a row to the DataFrame
start = {'Contract': contract, 'Paymentdate': startdate, 'Paymenttype': 1, 'Amount' : principal}
output = pd.concat([output, pd.DataFrame([start])], ignore_index=True)
for payment in range(paymentsperyear*years):
new_date = (date + relativedelta(months=12/paymentsperyear))
date = new_date
interest_line = {'Contract': contract, 'Paymentdate': new_date.strftime('%Y%m%d'),'Paymenttype': 2, 'Amount' : -1*interest}
output = pd.concat([output, pd.DataFrame([interest_line])], ignore_index=True)
end = {'Contract': contract, 'Paymentdate': date.strftime('%Y%m%d'), 'Paymenttype': 3, 'Amount' : -principal}
output = pd.concat([output, pd.DataFrame([end])], ignore_index=True)
return output
test = genPaymentPlan(123458, 2000, 2, 12, '20260101', 4)
#print(test)
# Generate INSERT statements
print("Generated SQL INSERT statements:\n")
for index, row in test.iterrows():
contract = int(row['Contract'])
date = row['Paymentdate'] # already string in format YYYY-MM-DD
type = row['Paymenttype']
amount = float(row['Amount'])
insert = f"INSERT INTO paymentplan (contract, paymentdate,paymenttype, amount) VALUES ({contract}, '{date}', {type} , {amount});"
print(insert)
2025-03-06, 21:24
Hier ein einfacher Code-Schnipsel, um aus CSV-Dateien Excel-Dateien zu machen.
Es empfiehlt sich auch, die Engine explizit zu setzen, mit der die Excel-Datei geschrieben wird. Standardmäßig nutzt pandas openpyxl, xlsxwriter scheint nach meinen Tests aber um ca. 30% schneller zu sein.
import pandas as pd
from openpyxl import Workbook
from openpyxl.utils.dataframe import dataframe_to_rows
# output control
files = ['']
wb = Workbook()
wb.remove(wb['Sheet'])
for index, file in enumerate(files,0):
temp = pd.read_csv(file + '.csv',sep='\t')
ws1 = wb.create_sheet()
ws1.title = file
rows = dataframe_to_rows(temp, index=False, header=True)
for r_idx, row in enumerate(rows, 1):
for c_idx, value in enumerate(row, 1):
ws1.cell(row=r_idx, column=c_idx, value=value)
wb.save('files45646.xlsx')
wb.close() |
import pandas as pd
from openpyxl import Workbook
from openpyxl.utils.dataframe import dataframe_to_rows
# output control
files = ['']
wb = Workbook()
wb.remove(wb['Sheet'])
for index, file in enumerate(files,0):
temp = pd.read_csv(file + '.csv',sep='\t')
ws1 = wb.create_sheet()
ws1.title = file
rows = dataframe_to_rows(temp, index=False, header=True)
for r_idx, row in enumerate(rows, 1):
for c_idx, value in enumerate(row, 1):
ws1.cell(row=r_idx, column=c_idx, value=value)
wb.save('files45646.xlsx')
wb.close()
2025-02-27, 21:52
Aus aktuellem Anlass hier ein einfaches Beispiel, wie man Werte in pandas Dataframes ersetzen kann:
import pandas as pd
# initialize data of lists.
data = {'Hersteller': ['VW', 'BMW', 'VW', 'Porsche'],
'Modell': ['Golf', '1er', 'Polo', '911']}
df = pd.DataFrame(data)
print(df,'\n')
df['Hersteller'] = df['Hersteller'].replace(
{"VW": "Volkswagen", "Horch": "Audi"})
print(df) |
import pandas as pd
# initialize data of lists.
data = {'Hersteller': ['VW', 'BMW', 'VW', 'Porsche'],
'Modell': ['Golf', '1er', 'Polo', '911']}
df = pd.DataFrame(data)
print(df,'\n')
df['Hersteller'] = df['Hersteller'].replace(
{"VW": "Volkswagen", "Horch": "Audi"})
print(df)
2025-02-08, 18:29
Hier ein Code-Beispiel für die wichtigsten Funktionen von DuckDB.
import duckdb as ddb
import pandas as pd
con = ddb.connect(':memory:')
con_p = ddb.connect('my_database.db')
con_p.execute('CREATE OR REPLACE TABLE telefonnummern(fullname VARCHAR,phone VARCHAR);')
con_p.execute("INSERT INTO telefonnummern VALUES ('Max Mustermann', '0123-4567890')")
print(con_p.sql('SHOW ALL TABLES'))
print(con_p.sql('SELECT * FROM telefonnummern;'))
ddb_object = con_p.sql('SELECT * FROM telefonnummern;')
df = ddb_object.to_df()
ddb_tuple = ddb_object.fetchall()
print(df)
print(ddb_tuple) |
import duckdb as ddb
import pandas as pd
con = ddb.connect(':memory:')
con_p = ddb.connect('my_database.db')
con_p.execute('CREATE OR REPLACE TABLE telefonnummern(fullname VARCHAR,phone VARCHAR);')
con_p.execute("INSERT INTO telefonnummern VALUES ('Max Mustermann', '0123-4567890')")
print(con_p.sql('SHOW ALL TABLES'))
print(con_p.sql('SELECT * FROM telefonnummern;'))
ddb_object = con_p.sql('SELECT * FROM telefonnummern;')
df = ddb_object.to_df()
ddb_tuple = ddb_object.fetchall()
print(df)
print(ddb_tuple)
2024-12-14, 11:18
Hier ein paar Beispiele, wie man mit icecream print() Ausgaben ersetzen kann.
"""
icecream examples
"""
from icecream import ic
# define some function
def addiere(x, y):
return x + y
# call ice
ic(addiere(1, 2))
# Output:
# ic| addiere(1, 2): 3
d = {'i': 2, 'j': 3, 'k': 4711}
ic(d['k'])
struct = {
"hersteller": "VW",
"modell": "Golf",
"Farben": ["gelb", "rot"]
}
ic(struct)
ic.disable()
ic(struct) # no output
ic.enable()
def logstuff(text):
# log to output file
print(text)
ic.configureOutput(prefix="Hallo| ", outputFunction=logstuff)
ic(addiere(7, 7))
ic.configureOutput(prefix="Welt| ", outputFunction=logstuff)
ic(addiere(7, 7)) |
"""
icecream examples
"""
from icecream import ic
# define some function
def addiere(x, y):
return x + y
# call ice
ic(addiere(1, 2))
# Output:
# ic| addiere(1, 2): 3
d = {'i': 2, 'j': 3, 'k': 4711}
ic(d['k'])
struct = {
"hersteller": "VW",
"modell": "Golf",
"Farben": ["gelb", "rot"]
}
ic(struct)
ic.disable()
ic(struct) # no output
ic.enable()
def logstuff(text):
# log to output file
print(text)
ic.configureOutput(prefix="Hallo| ", outputFunction=logstuff)
ic(addiere(7, 7))
ic.configureOutput(prefix="Welt| ", outputFunction=logstuff)
ic(addiere(7, 7))
2024-09-07, 09:23
Für Dante e.V. bestand die Notwendigkeit, aus MT940 Dateien moderne CAMT.053 zu erzeugen, dank Python wurde das eine lösbare Aufgabe.
Schritt 1
Die MT940 Datei parsen und die Transaktionen in einen pandas DataFrame überführen.
import mt940
import pprint
import pandas as pd
df = pd.DataFrame()
transactions = mt940.parse('Umsaetze_2310007_22.07.2024.mta')
print('Transactions:')
pprint.pprint(transactions.data)
for transaction in transactions:
print('Transaction: ', transaction)
pprint.pprint(transaction.data)
t = transaction.data
tt = pd.DataFrame(t, index=[0])
df = pd.concat([df,tt],ignore_index=True)
df.to_excel('AllBookings.xlsx',index=False)
Schritt 2
Aus dem DataFrame das XML befüllen, die für den Kopf der XML-Datei notwendigen Kontostandsinformationen holen ich dazu aus der MT940 Datei.
import pandas as pd # data wrangling
import jinja2 # template engine
import os # for file-related stuff
import mt940
from datetime import datetime
today = datetime.today()
now = today.strftime("%Y-%m-%d")
transactions = mt940.parse('Umsaetze_2310007_22.07.2024.mta')
opening = transactions.data['final_opening_balance']
openingamount = str(opening.amount)[:-4]
openingdate = opening.date
closing = transactions.data['final_closing_balance']
closingamount = str(closing.amount)[:-4]
closingdate = closing.date
# create jinja env that can load template from filesystem
jinja_env = jinja2.Environment(loader = jinja2.FileSystemLoader(os.path.abspath('.')))
df = pd.read_excel('AllBookings.xlsx', dtype={'date': str,'amount':str})
df['CreditDebit'] = ''
df['CreditDebit'] = df['CreditDebit'].where(df['amount'].str.get(0).isin(['-']), 'CRDT')
df['CreditDebit'] = df['CreditDebit'].where(~df['amount'].str.get(0).isin(['-']), 'DBIT')
df['date'] = df['date'].str[:-9]
# Währung weg
df['amount'] = df['amount'].str[:-4]
# Vorzeichen weg
df['amount'] = df['amount'].str.replace('-','')
#df['amount'].replace('-','',inplace=True)
#df["amount"] = df["amount"].apply(lambda x: x.str.replace("-", ""))
template = jinja_env.get_template('Ntry.xml')
with open('FertigesXML.xml','w') as output:
output.write(template.render(data=df,
openingamount=openingamount,
openingdate=openingdate,
closingamount=closingamount,
closingdate=closingdate
))
Jinja2 XML-Template
Das XML-Template für Jinja2 findet ihr hier:
Ntry_blog