apply flake8, remove Python 3.10 Syntax

make sure that the package works with older Python versions:

- replace matches with if-then-else
- Replace "|" with "Union"
- Remove "TypeAbbrev"

Make sure taht flake8 produces few warnings.

Add github action for automatic checks.
This commit is contained in:
Thomas Türk 2023-05-06 21:49:04 +02:00
parent d88469e711
commit 3566c9ba3e
23 changed files with 1191 additions and 1045 deletions

2
.flake8 Normal file
View File

@ -0,0 +1,2 @@
[flake8]
max-line-length = 250

43
.github/workflows/python-package.yml vendored Normal file
View File

@ -0,0 +1,43 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
name: Python package
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
sudo apt update
sudo apt install -y unixodbc
python -m pip install --upgrade pip
python -m pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
pip install -e .
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
pytest .

View File

@ -10,6 +10,7 @@ import PyAPplus64
import applus_configs import applus_configs
import pathlib import pathlib
def main(confFile: pathlib.Path, outfile: str) -> None: def main(confFile: pathlib.Path, outfile: str) -> None:
server = PyAPplus64.applus.applusFromConfigFile(confFile) server = PyAPplus64.applus.applusFromConfigFile(confFile)

View File

@ -14,4 +14,3 @@ configdir = basedir.joinpath("config")
serverConfYamlDeploy = configdir.joinpath("applus-server-deploy.yaml") serverConfYamlDeploy = configdir.joinpath("applus-server-deploy.yaml")
serverConfYamlTest = configdir.joinpath("applus-server-test.yaml") serverConfYamlTest = configdir.joinpath("applus-server-test.yaml")
serverConfYamlProd = configdir.joinpath("applus-server-prod.yaml") serverConfYamlProd = configdir.joinpath("applus-server-prod.yaml")

View File

@ -9,26 +9,29 @@
import pathlib import pathlib
import PyAPplus64 import PyAPplus64
import applus_configs import applus_configs
from typing import Optional
def main(confFile : pathlib.Path, updateDB:bool, docDir:str|None = None) -> None:
def main(confFile: pathlib.Path, updateDB: bool, docDir: Optional[str] = None) -> None:
server = PyAPplus64.applus.applusFromConfigFile(confFile) server = PyAPplus64.applus.applusFromConfigFile(confFile)
if docDir is None: if docDir is None:
docDir = str(server.scripttool.getInstallPathWebServer().joinpath("DocLib")) docDir = str(server.scripttool.getInstallPathWebServer().joinpath("DocLib"))
sql = PyAPplus64.sql_utils.SqlStatementSelect("ARTIKEL"); sql = PyAPplus64.sql_utils.SqlStatementSelect("ARTIKEL")
sql.addFields("ID", "ARTIKEL", "DOCUMENTS"); sql.addFields("ID", "ARTIKEL", "DOCUMENTS")
sql.where.addConditionFieldStringNotEmpty("DOCUMENTS"); sql.where.addConditionFieldStringNotEmpty("DOCUMENTS")
for row in server.dbQueryAll(sql): for row in server.dbQueryAll(sql):
doc = pathlib.Path(docDir + row.DOCUMENTS); doc = pathlib.Path(docDir + row.DOCUMENTS)
if not doc.exists(): if not doc.exists():
print("Bild '{}' für Artikel '{}' nicht gefunden".format(doc, row.ARTIKEL)) print("Bild '{}' für Artikel '{}' nicht gefunden".format(doc, row.ARTIKEL))
if updateDB: if updateDB:
upd = server.mkUseXMLRowUpdate("ARTIKEL", row.ID); upd = server.mkUseXMLRowUpdate("ARTIKEL", row.ID)
upd.addField("DOCUMENTS", None); upd.addField("DOCUMENTS", None)
upd.update(); upd.update()
if __name__ == "__main__": if __name__ == "__main__":
main(applus_configs.serverConfYamlTest, False) main(applus_configs.serverConfYamlTest, False)

View File

@ -24,18 +24,19 @@ import PyAPplus64
import applus_configs import applus_configs
import logging import logging
import yaml import yaml
from typing import Optional
def main(confFile:pathlib.Path, artikel:str, artikelNeu:str|None=None) -> None: def main(confFile: pathlib.Path, artikel: str, artikelNeu: Optional[str] = None) -> None:
# Server verbinden # Server verbinden
server = PyAPplus64.applus.applusFromConfigFile(confFile) server = PyAPplus64.applus.applusFromConfigFile(confFile)
# DuplicateBusinessObject für Artikel erstellen # DuplicateBusinessObject für Artikel erstellen
dArt = PyAPplus64.duplicate.loadDBDuplicateArtikel(server, artikel); dArt = PyAPplus64.duplicate.loadDBDuplicateArtikel(server, artikel)
# DuplicateBusinessObject zur Demonstration in YAML konvertieren und zurück # DuplicateBusinessObject zur Demonstration in YAML konvertieren und zurück
dArtYaml = yaml.dump(dArt); dArtYaml = yaml.dump(dArt)
print(dArtYaml); print(dArtYaml)
dArt2 = yaml.load(dArtYaml, Loader=yaml.UnsafeLoader) dArt2 = yaml.load(dArtYaml, Loader=yaml.UnsafeLoader)
# Neue Artikel-Nummer bestimmen und DuplicateBusinessObject in DB schreiben # Neue Artikel-Nummer bestimmen und DuplicateBusinessObject in DB schreiben
@ -45,8 +46,8 @@ def main(confFile:pathlib.Path, artikel:str, artikelNeu:str|None=None) -> None:
if not (dArt is None): if not (dArt is None):
dArt.setFields({"artikel": artikelNeu}) dArt.setFields({"artikel": artikelNeu})
res = dArt.insert(server); res = dArt.insert(server)
print(res); print(res)
if __name__ == "__main__": if __name__ == "__main__":
@ -56,4 +57,3 @@ if __name__ == "__main__":
# logger.setLevel(logging.ERROR) # logger.setLevel(logging.ERROR)
main(applus_configs.serverConfYamlTest, "my-artikel", artikelNeu="my-artikel-copy") main(applus_configs.serverConfYamlTest, "my-artikel", artikelNeu="my-artikel-copy")

View File

@ -13,12 +13,13 @@ import PyAPplus64
import applus_configs import applus_configs
import pandas as pd # type: ignore import pandas as pd # type: ignore
import pathlib import pathlib
from typing import * from typing import Tuple, Union, Optional
def ladeAlleWerkstattauftragMengenabweichungen( def ladeAlleWerkstattauftragMengenabweichungen(
server: PyAPplus64.APplusServer, server: PyAPplus64.APplusServer,
cond:PyAPplus64.SqlCondition|str|None=None) -> pd.DataFrame: cond: Union[PyAPplus64.SqlCondition, str, None] = None) -> pd.DataFrame:
sql = PyAPplus64.sql_utils.SqlStatementSelect("WAUFTRAG w"); sql = PyAPplus64.sql_utils.SqlStatementSelect("WAUFTRAG w")
sql.addLeftJoin("personal p", "w.UPDUSER = p.PERSONAL") sql.addLeftJoin("personal p", "w.UPDUSER = p.PERSONAL")
sql.addFieldsTable("w", "ID", "BAUFTRAG", "POSITION") sql.addFieldsTable("w", "ID", "BAUFTRAG", "POSITION")
@ -31,18 +32,20 @@ def ladeAlleWerkstattauftragMengenabweichungen(
sql.where.addCondition("abs(w.MENGE-w.MENGE_IST) > 0.001") sql.where.addCondition("abs(w.MENGE-w.MENGE_IST) > 0.001")
sql.where.addCondition(cond) sql.where.addCondition(cond)
sql.order = "w.UPDDATE" sql.order = "w.UPDDATE"
dfOrg = PyAPplus64.pandas.pandasReadSql(server, sql); dfOrg = PyAPplus64.pandas.pandasReadSql(server, sql)
# Add Links # Add Links
df = dfOrg.copy(); df = dfOrg.copy()
df = df.drop(columns=["ID"]); df = df.drop(columns=["ID"])
# df = df[['POSITION', 'BAUFTRAG', 'MENGE']] # reorder / filter columns # df = df[['POSITION', 'BAUFTRAG', 'MENGE']] # reorder / filter columns
df['POSITION'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg, df['POSITION'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
dfOrg,
lambda r: r.POSITION, lambda r: r.POSITION,
lambda r: server.makeWebLinkWauftrag( lambda r: server.makeWebLinkWauftrag(
bauftrag=r.BAUFTRAG, accessid=r.ID)) bauftrag=r.BAUFTRAG, accessid=r.ID))
df['BAUFTRAG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg, df['BAUFTRAG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
dfOrg,
lambda r: r.BAUFTRAG, lambda r: r.BAUFTRAG,
lambda r: server.makeWebLinkBauftrag(bauftrag=r.BAUFTRAG)) lambda r: server.makeWebLinkBauftrag(bauftrag=r.BAUFTRAG))
@ -57,15 +60,15 @@ def ladeAlleWerkstattauftragMengenabweichungen(
"UPDDATE": "geändert am", "UPDDATE": "geändert am",
"UPDNAME": "geändert von" "UPDNAME": "geändert von"
} }
df.rename(columns=colNames, inplace=True); df.rename(columns=colNames, inplace=True)
return df return df
def ladeAlleWerkstattauftragPosMengenabweichungen( def ladeAlleWerkstattauftragPosMengenabweichungen(
server: PyAPplus64.APplusServer, server: PyAPplus64.APplusServer,
cond:PyAPplus64.SqlCondition|str|None=None) -> pd.DataFrame: cond: Union[PyAPplus64.SqlCondition, str, None] = None) -> pd.DataFrame:
sql = PyAPplus64.sql_utils.SqlStatementSelect("WAUFTRAGPOS w"); sql = PyAPplus64.sql_utils.SqlStatementSelect("WAUFTRAGPOS w")
sql.addLeftJoin("personal p", "w.UPDUSER = p.PERSONAL") sql.addLeftJoin("personal p", "w.UPDUSER = p.PERSONAL")
sql.addFieldsTable("w", "ID", "BAUFTRAG", "POSITION", "AG") sql.addFieldsTable("w", "ID", "BAUFTRAG", "POSITION", "AG")
@ -78,19 +81,22 @@ def ladeAlleWerkstattauftragPosMengenabweichungen(
sql.where.addCondition(cond) sql.where.addCondition(cond)
sql.order = "w.UPDDATE" sql.order = "w.UPDDATE"
dfOrg = PyAPplus64.pandas.pandasReadSql(server, sql); dfOrg = PyAPplus64.pandas.pandasReadSql(server, sql)
# Add Links # Add Links
df = dfOrg.copy(); df = dfOrg.copy()
df = df.drop(columns=["ID"]); df = df.drop(columns=["ID"])
df['POSITION'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg, df['POSITION'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
dfOrg,
lambda r: r.POSITION, lambda r: r.POSITION,
lambda r: server.makeWebLinkWauftrag( lambda r: server.makeWebLinkWauftrag(
bauftrag=r.BAUFTRAG, accessid=r.ID)) bauftrag=r.BAUFTRAG, accessid=r.ID))
df['BAUFTRAG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg, df['BAUFTRAG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
dfOrg,
lambda r: r.BAUFTRAG, lambda r: r.BAUFTRAG,
lambda r: server.makeWebLinkBauftrag(bauftrag=r.BAUFTRAG)) lambda r: server.makeWebLinkBauftrag(bauftrag=r.BAUFTRAG))
df['AG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg, df['AG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
dfOrg,
lambda r: r.AG, lambda r: r.AG,
lambda r: server.makeWebLinkWauftragPos( lambda r: server.makeWebLinkWauftragPos(
bauftrag=r.BAUFTRAG, position=r.POSITION, accessid=r.ID)) bauftrag=r.BAUFTRAG, position=r.POSITION, accessid=r.ID))
@ -111,11 +117,12 @@ def ladeAlleWerkstattauftragPosMengenabweichungen(
"UPDDATE": "geändert am", "UPDDATE": "geändert am",
"UPDNAME": "geändert von" "UPDNAME": "geändert von"
} }
df.rename(columns=colNames, inplace=True); df.rename(columns=colNames, inplace=True)
return df return df
def computeInYearMonthCond(field : str, year:int|None=None,
month:int|None=None) -> PyAPplus64.SqlCondition | None: def computeInYearMonthCond(field: str, year: Optional[int] = None,
month: Optional[int] = None) -> Optional[PyAPplus64.SqlCondition]:
if not (year is None): if not (year is None):
if month is None: if month is None:
return PyAPplus64.sql_utils.SqlConditionDateTimeFieldInYear(field, year) return PyAPplus64.sql_utils.SqlConditionDateTimeFieldInYear(field, year)
@ -124,52 +131,59 @@ def computeInYearMonthCond(field : str, year:int|None=None,
else: else:
return None return None
def computeFileName(year:int|None=None, month:int|None=None) -> str:
def computeFileName(year: Optional[int] = None, month: Optional[int] = None) -> str:
if year is None: if year is None:
return 'mengenabweichungen-all.xlsx'; return 'mengenabweichungen-all.xlsx'
else: else:
if month is None: if month is None:
return 'mengenabweichungen-{:04d}.xlsx'.format(year); return 'mengenabweichungen-{:04d}.xlsx'.format(year)
else: else:
return 'mengenabweichungen-{:04d}-{:02d}.xlsx'.format(year, month); return 'mengenabweichungen-{:04d}-{:02d}.xlsx'.format(year, month)
def _exportInternal(server: PyAPplus64.APplusServer, fn: str, def _exportInternal(server: PyAPplus64.APplusServer, fn: str,
cond: Union[PyAPplus64.SqlCondition, str, None]) -> int: cond: Union[PyAPplus64.SqlCondition, str, None]) -> int:
df1 = ladeAlleWerkstattauftragMengenabweichungen(server, cond) df1 = ladeAlleWerkstattauftragMengenabweichungen(server, cond)
df2 = ladeAlleWerkstattauftragPosMengenabweichungen(server, cond) df2 = ladeAlleWerkstattauftragPosMengenabweichungen(server, cond)
print ("erzeuge " + fn); print("erzeuge " + fn)
PyAPplus64.pandas.exportToExcel(fn, [(df1, "WAuftrag"), (df2, "WAuftrag-Pos")], addTable=True) PyAPplus64.pandas.exportToExcel(fn, [(df1, "WAuftrag"), (df2, "WAuftrag-Pos")], addTable=True)
return len(df1.index) + len(df2.index) return len(df1.index) + len(df2.index)
def exportVonBis(server: PyAPplus64.APplusServer, fn: str, def exportVonBis(server: PyAPplus64.APplusServer, fn: str,
von:datetime.datetime|None, bis:datetime.datetime|None) -> int: von: Optional[datetime.datetime], bis: Optional[datetime.datetime]) -> int:
cond = PyAPplus64.sql_utils.SqlConditionDateTimeFieldInRange("w.UPDDATE", von, bis) cond = PyAPplus64.sql_utils.SqlConditionDateTimeFieldInRange("w.UPDDATE", von, bis)
return _exportInternal(server, fn, cond) return _exportInternal(server, fn, cond)
def exportYearMonth(server: PyAPplus64.APplusServer, def exportYearMonth(server: PyAPplus64.APplusServer,
year:int|None=None, month:int|None=None) -> int: year: Optional[int] = None, month: Optional[int] = None) -> int:
cond = computeInYearMonthCond("w.UPDDATE", year=year, month=month) cond = computeInYearMonthCond("w.UPDDATE", year=year, month=month)
fn = computeFileName(year=year, month=month) fn = computeFileName(year=year, month=month)
return _exportInternal(server, fn, cond) return _exportInternal(server, fn, cond)
def computePreviousMonthYear(cyear: int, cmonth: int) -> Tuple[int, int]: def computePreviousMonthYear(cyear: int, cmonth: int) -> Tuple[int, int]:
if cmonth == 1: if cmonth == 1:
return (cyear-1, 12) return (cyear-1, 12)
else: else:
return (cyear, cmonth-1); return (cyear, cmonth-1)
def computeNextMonthYear(cyear: int, cmonth: int) -> Tuple[int, int]: def computeNextMonthYear(cyear: int, cmonth: int) -> Tuple[int, int]:
if cmonth == 12: if cmonth == 12:
return (cyear+1, 1) return (cyear+1, 1)
else: else:
return (cyear, cmonth+1); return (cyear, cmonth+1)
def main(confFile : str|pathlib.Path, user:str|None=None, env:str|None=None) -> None:
def main(confFile: Union[str, pathlib.Path], user: Optional[str] = None, env: Optional[str] = None) -> None:
server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env) server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env)
now = datetime.date.today() now = datetime.date.today()
(cmonth, cyear) = (now.month, now.year) (cmonth, cyear) = (now.month, now.year)
(pyear, pmonth) = computePreviousMonthYear(cyear, cmonth); (pyear, pmonth) = computePreviousMonthYear(cyear, cmonth)
# Ausgaben # Ausgaben
exportYearMonth(server, cyear, cmonth) # Aktueller Monat exportYearMonth(server, cyear, cmonth) # Aktueller Monat
@ -178,5 +192,6 @@ def main(confFile : str|pathlib.Path, user:str|None=None, env:str|None=None) ->
# export(cyear-1) # letztes Jahr # export(cyear-1) # letztes Jahr
# export() # alles # export() # alles
if __name__ == "__main__": if __name__ == "__main__":
main(applus_configs.serverConfYamlTest) main(applus_configs.serverConfYamlTest)

View File

@ -12,9 +12,10 @@ import datetime
import PyAPplus64 import PyAPplus64
import applus_configs import applus_configs
import pathlib import pathlib
from typing import * from typing import Tuple, Optional, Union
def parseDate (dateS:str) -> Tuple[datetime.datetime|None, bool]:
def parseDate(dateS: str) -> Tuple[Optional[datetime.datetime], bool]:
if dateS is None or dateS == '': if dateS is None or dateS == '':
return (None, True) return (None, True)
else: else:
@ -24,12 +25,15 @@ def parseDate (dateS:str) -> Tuple[datetime.datetime|None, bool]:
sg.popup_error("Fehler beim Parsen des Datums '{}'".format(dateS)) sg.popup_error("Fehler beim Parsen des Datums '{}'".format(dateS))
return (None, False) return (None, False)
def createFile(server: PyAPplus64.APplusServer, fileS: str, vonS: str, bisS: str) -> None: def createFile(server: PyAPplus64.APplusServer, fileS: str, vonS: str, bisS: str) -> None:
(von, vonOK) = parseDate(vonS) (von, vonOK) = parseDate(vonS)
if not vonOK: return if not vonOK:
return
(bis, bisOK) = parseDate(bisS) (bis, bisOK) = parseDate(bisS)
if not bisOK: return if not bisOK:
return
if (fileS is None) or fileS == '': if (fileS is None) or fileS == '':
sg.popup_error("Es wurde keine Ausgabedatei ausgewählt.") sg.popup_error("Es wurde keine Ausgabedatei ausgewählt.")
@ -41,7 +45,7 @@ def createFile(server:PyAPplus64.APplusServer, fileS:str, vonS:str, bisS:str)->N
sg.popup_ok("{} Datensätze erfolgreich in Datei '{}' geschrieben.".format(c, file)) sg.popup_ok("{} Datensätze erfolgreich in Datei '{}' geschrieben.".format(c, file))
def main(confFile : str|pathlib.Path, user:str|None=None, env:str|None=None) -> None: def main(confFile: Union[str, pathlib.Path], user: Optional[str] = None, env: Optional[str] = None) -> None:
server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env) server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env)
layout = [ layout = [
@ -54,7 +58,8 @@ def main(confFile : str|pathlib.Path, user:str|None=None, env:str|None=None) ->
sg.CalendarButton("Kalender", close_when_date_chosen=True, sg.CalendarButton("Kalender", close_when_date_chosen=True,
target="Bis", format='%d.%m.%Y')], target="Bis", format='%d.%m.%Y')],
[sg.Text('Ausgabedatei', size=(15, 1)), sg.InputText(key='File'), [sg.Text('Ausgabedatei', size=(15, 1)), sg.InputText(key='File'),
sg.FileSaveAs(button_text="wählen", target="File", sg.FileSaveAs(button_text="wählen",
target="File",
file_types=(('Excel Files', '*.xlsx'),), file_types=(('Excel Files', '*.xlsx'),),
default_extension=".xlsx")], default_extension=".xlsx")],
[sg.Button("Aktueller Monat"), sg.Button("Letzter Monat"), [sg.Button("Aktueller Monat"), sg.Button("Letzter Monat"),
@ -66,33 +71,34 @@ def main(confFile : str|pathlib.Path, user:str|None=None, env:str|None=None) ->
window = sg.Window("Mengenabweichung " + systemName, layout) window = sg.Window("Mengenabweichung " + systemName, layout)
now = datetime.date.today() now = datetime.date.today()
(cmonth, cyear) = (now.month, now.year) (cmonth, cyear) = (now.month, now.year)
(pyear, pmonth) = mengenabweichung.computePreviousMonthYear(cyear, cmonth); (pyear, pmonth) = mengenabweichung.computePreviousMonthYear(cyear, cmonth)
(nyear, nmonth) = mengenabweichung.computeNextMonthYear(cyear, cmonth); (nyear, nmonth) = mengenabweichung.computeNextMonthYear(cyear, cmonth)
while True: while True:
event, values = window.read() event, values = window.read()
if event == sg.WIN_CLOSED or event == 'Beenden': if event == sg.WIN_CLOSED or event == 'Beenden':
break break
if event == 'Aktueller Monat': if event == 'Aktueller Monat':
window['Von'].update(value="01.{:02d}.{:04d}".format(cmonth, cyear)); window['Von'].update(value="01.{:02d}.{:04d}".format(cmonth, cyear))
window['Bis'].update(value="01.{:02d}.{:04d}".format(nmonth, nyear)); window['Bis'].update(value="01.{:02d}.{:04d}".format(nmonth, nyear))
if event == 'Letzter Monat': if event == 'Letzter Monat':
window['Von'].update(value="01.{:02d}.{:04d}".format(pmonth, pyear)); window['Von'].update(value="01.{:02d}.{:04d}".format(pmonth, pyear))
window['Bis'].update(value="01.{:02d}.{:04d}".format(cmonth, cyear)); window['Bis'].update(value="01.{:02d}.{:04d}".format(cmonth, cyear))
if event == 'Aktuelles Jahr': if event == 'Aktuelles Jahr':
window['Von'].update(value="01.01.{:04d}".format(cyear)); window['Von'].update(value="01.01.{:04d}".format(cyear))
window['Bis'].update(value="01.01.{:04d}".format(cyear+1)); window['Bis'].update(value="01.01.{:04d}".format(cyear+1))
if event == 'Letztes Jahr': if event == 'Letztes Jahr':
window['Von'].update(value="01.01.{:04d}".format(cyear-1)); window['Von'].update(value="01.01.{:04d}".format(cyear-1))
window['Bis'].update(value="01.01.{:04d}".format(cyear)); window['Bis'].update(value="01.01.{:04d}".format(cyear))
if event == 'Speichern': if event == 'Speichern':
try: try:
createFile(server, values.get('File', None), createFile(server, values.get('File', None),
values.get('Von', None), values.get('Bis', None)) values.get('Von', None), values.get('Bis', None))
except Exception as e: except Exception as e:
sg.popup_error_with_traceback("Beim Erzeugen der Excel-Datei trat ein Fehler auf:", e); sg.popup_error_with_traceback("Beim Erzeugen der Excel-Datei trat ein Fehler auf:", e)
window.close() window.close()
if __name__ == "__main__": if __name__ == "__main__":
main(applus_configs.serverConfYamlProd) main(applus_configs.serverConfYamlProd)

View File

@ -13,9 +13,10 @@
import pathlib import pathlib
import PyAPplus64 import PyAPplus64
import applus_configs import applus_configs
import lxml.etree as ET # type: ignore from typing import Optional, Union
def main(confFile : str|pathlib.Path, user:str|None=None, env:str|None=None) -> None:
def main(confFile: Union[str, pathlib.Path], user: Optional[str] = None, env: Optional[str] = None) -> None:
server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env) server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env)
print("\n\nSysConf Lookups:") print("\n\nSysConf Lookups:")
@ -23,7 +24,8 @@ def main(confFile : str|pathlib.Path, user:str|None=None, env:str|None=None) ->
print(" Default Auftragsart:", server.sysconf.getString("STAMM", "DEFAULTAUFTRAGSART")) print(" Default Auftragsart:", server.sysconf.getString("STAMM", "DEFAULTAUFTRAGSART"))
print(" Auftragsarten:") print(" Auftragsarten:")
arten = server.sysconf.getList("STAMM", "AUFTRAGSART", sep='\n') arten = server.sysconf.getList("STAMM", "AUFTRAGSART", sep='\n')
if not arten: arten = [] if not arten:
arten = []
for a in arten: for a in arten:
print(" - " + a) print(" - " + a)
@ -47,6 +49,5 @@ def main(confFile : str|pathlib.Path, user:str|None=None, env:str|None=None) ->
print(" ServerInfo - Version:", server.scripttool.getServerInfo().find("version").text) print(" ServerInfo - Version:", server.scripttool.getServerInfo().find("version").text)
if __name__ == "__main__": if __name__ == "__main__":
main(applus_configs.serverConfYamlTest) main(applus_configs.serverConfYamlTest)

View File

@ -6,8 +6,6 @@
# license that can be found in the LICENSE file or at # license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT. # https://opensource.org/licenses/MIT.
#-*- coding: utf-8 -*-
from . import applus_db from . import applus_db
from . import applus_server from . import applus_server
from . import applus_sysconf from . import applus_sysconf
@ -18,13 +16,12 @@ import yaml
import urllib.parse import urllib.parse
from zeep import Client from zeep import Client
import pyodbc # type: ignore import pyodbc # type: ignore
from typing import * from typing import TYPE_CHECKING, Optional, Any, Callable, Dict, Sequence, Set, List
if TYPE_CHECKING: if TYPE_CHECKING:
from _typeshed import FileDescriptorOrPath from _typeshed import FileDescriptorOrPath
class APplusServer: class APplusServer:
""" """
Verbindung zu einem APplus DB und App Server mit Hilfsfunktionen für den komfortablen Zugriff. Verbindung zu einem APplus DB und App Server mit Hilfsfunktionen für den komfortablen Zugriff.
@ -36,7 +33,10 @@ class APplusServer:
:param web_settings: die Einstellungen für die Verbindung mit dem APplus Web Server :param web_settings: die Einstellungen für die Verbindung mit dem APplus Web Server
:type web_settings: APplusWebServerSettings :type web_settings: APplusWebServerSettings
""" """
def __init__(self, db_settings : applus_db.APplusDBSettings, server_settings : applus_server.APplusAppServerSettings, web_settings : applus_server.APplusWebServerSettings): def __init__(self,
db_settings: applus_db.APplusDBSettings,
server_settings: applus_server.APplusAppServerSettings,
web_settings: applus_server.APplusWebServerSettings):
self.db_settings: applus_db.APplusDBSettings = db_settings self.db_settings: applus_db.APplusDBSettings = db_settings
"""Die Einstellungen für die Datenbankverbindung""" """Die Einstellungen für die Datenbankverbindung"""
@ -51,18 +51,17 @@ class APplusServer:
Diese Connection kann in Verbindung mit den Funktionen aus :mod:`PyAPplus64.applus_db` genutzt werden. Diese Connection kann in Verbindung mit den Funktionen aus :mod:`PyAPplus64.applus_db` genutzt werden.
""" """
self.server_conn: applus_server.APplusServerConnection = applus_server.APplusServerConnection(server_settings)
self.server_conn : applus_server.APplusServerConnection = applus_server.APplusServerConnection(server_settings);
"""erlaubt den Zugriff auf den AppServer""" """erlaubt den Zugriff auf den AppServer"""
self.sysconf : applus_sysconf.APplusSysConf = applus_sysconf.APplusSysConf(self); self.sysconf: applus_sysconf.APplusSysConf = applus_sysconf.APplusSysConf(self)
"""erlaubt den Zugriff auf die Sysconfig""" """erlaubt den Zugriff auf die Sysconfig"""
self.scripttool : applus_scripttool.APplusScriptTool = applus_scripttool.APplusScriptTool(self); self.scripttool: applus_scripttool.APplusScriptTool = applus_scripttool.APplusScriptTool(self)
"""erlaubt den einfachen Zugriff auf Funktionen des ScriptTools""" """erlaubt den einfachen Zugriff auf Funktionen des ScriptTools"""
self.client_table = self.server_conn.getClient("p2core","Table"); self.client_table = self.server_conn.getClient("p2core", "Table")
self.client_xml = self.server_conn.getClient("p2core","XML"); self.client_xml = self.server_conn.getClient("p2core", "XML")
self.client_nummer = self.server_conn.getClient("p2system", "Nummer") self.client_nummer = self.server_conn.getClient("p2system", "Nummer")
def reconnectDB(self) -> None: def reconnectDB(self) -> None:
@ -86,13 +85,13 @@ class APplusServer:
if raw: if raw:
return str(sql) return str(sql)
else: else:
return self.client_table.service.getCompleteSQL(sql); return self.client_table.service.getCompleteSQL(sql)
def dbQueryAll(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False, def dbQueryAll(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False,
apply: Optional[Callable[[pyodbc.Row], Any]] = None) -> Any: apply: Optional[Callable[[pyodbc.Row], Any]] = None) -> Any:
"""Führt eine SQL Query aus und liefert alle Zeilen zurück. Das SQL wird zunächst """Führt eine SQL Query aus und liefert alle Zeilen zurück. Das SQL wird zunächst
vom Server angepasst, so dass z.B. Mandanteninformation hinzugefügt werden.""" vom Server angepasst, so dass z.B. Mandanteninformation hinzugefügt werden."""
sqlC = self.completeSQL(sql, raw=raw); sqlC = self.completeSQL(sql, raw=raw)
return applus_db.rawQueryAll(self.db_conn, sqlC, *args, apply=apply) return applus_db.rawQueryAll(self.db_conn, sqlC, *args, apply=apply)
def dbQuerySingleValues(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Sequence[Any]: def dbQuerySingleValues(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Sequence[Any]:
@ -102,33 +101,33 @@ class APplusServer:
def dbQuery(self, sql: sql_utils.SqlStatement, f: Callable[[pyodbc.Row], None], *args: Any, raw: bool = False) -> None: def dbQuery(self, sql: sql_utils.SqlStatement, f: Callable[[pyodbc.Row], None], *args: Any, raw: bool = False) -> None:
"""Führt eine SQL Query aus und führt für jede Zeile die übergeben Funktion aus. """Führt eine SQL Query aus und führt für jede Zeile die übergeben Funktion aus.
Das SQL wird zunächst vom Server angepasst, so dass z.B. Mandanteninformation hinzugefügt werden.""" Das SQL wird zunächst vom Server angepasst, so dass z.B. Mandanteninformation hinzugefügt werden."""
sqlC = self.completeSQL(sql, raw=raw); sqlC = self.completeSQL(sql, raw=raw)
applus_db.rawQuery(self.db_conn, sqlC, f, *args) applus_db.rawQuery(self.db_conn, sqlC, f, *args)
def dbQuerySingleRow(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Optional[pyodbc.Row]: def dbQuerySingleRow(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Optional[pyodbc.Row]:
"""Führt eine SQL Query aus, die maximal eine Zeile zurückliefern soll. Diese Zeile wird geliefert.""" """Führt eine SQL Query aus, die maximal eine Zeile zurückliefern soll. Diese Zeile wird geliefert."""
sqlC = self.completeSQL(sql, raw=raw); sqlC = self.completeSQL(sql, raw=raw)
return applus_db.rawQuerySingleRow(self.db_conn, sqlC, *args) return applus_db.rawQuerySingleRow(self.db_conn, sqlC, *args)
def dbQuerySingleRowDict(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Optional[Dict[str, Any]]: def dbQuerySingleRowDict(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Optional[Dict[str, Any]]:
"""Führt eine SQL Query aus, die maximal eine Zeile zurückliefern soll. """Führt eine SQL Query aus, die maximal eine Zeile zurückliefern soll.
Diese Zeile wird als Dictionary geliefert.""" Diese Zeile wird als Dictionary geliefert."""
row = self.dbQuerySingleRow(sql, *args, raw=raw); row = self.dbQuerySingleRow(sql, *args, raw=raw)
if row: if row:
return applus_db.row_to_dict(row); return applus_db.row_to_dict(row)
else: else:
return None return None
def dbQuerySingleValue(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Any: def dbQuerySingleValue(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Any:
"""Führt eine SQL Query aus, die maximal einen Wert zurückliefern soll. """Führt eine SQL Query aus, die maximal einen Wert zurückliefern soll.
Dieser Wert oder None wird geliefert.""" Dieser Wert oder None wird geliefert."""
sqlC = self.completeSQL(sql, raw=raw); sqlC = self.completeSQL(sql, raw=raw)
return applus_db.rawQuerySingleValue(self.db_conn, sqlC, *args) return applus_db.rawQuerySingleValue(self.db_conn, sqlC, *args)
def isDBTableKnown(self, table: str) -> bool: def isDBTableKnown(self, table: str) -> bool:
"""Prüft, ob eine Tabelle im System bekannt ist""" """Prüft, ob eine Tabelle im System bekannt ist"""
sql = "select count(*) from SYS.TABLES T where T.NAME=?" sql = "select count(*) from SYS.TABLES T where T.NAME=?"
c = self.dbQuerySingleValue(sql, table); c = self.dbQuerySingleValue(sql, table)
return (c > 0) return (c > 0)
def getClient(self, package: str, name: str) -> Client: def getClient(self, package: str, name: str) -> Client:
@ -146,7 +145,7 @@ class APplusServer:
:return: den Client :return: den Client
:rtype: Client :rtype: Client
""" """
return self.server_conn.getClient(package, name); return self.server_conn.getClient(package, name)
def getTableFields(self, table: str, isComputed: Optional[bool] = None) -> Set[str]: def getTableFields(self, table: str, isComputed: Optional[bool] = None) -> Set[str]:
""" """
@ -158,14 +157,14 @@ class APplusServer:
:rtype: {str} :rtype: {str}
""" """
sql = sql_utils.SqlStatementSelect("SYS.TABLES T") sql = sql_utils.SqlStatementSelect("SYS.TABLES T")
join = sql.addInnerJoin("SYS.COLUMNS C"); join = sql.addInnerJoin("SYS.COLUMNS C")
join.on.addConditionFieldsEq("T.Object_ID", "C.Object_ID") join.on.addConditionFieldsEq("T.Object_ID", "C.Object_ID")
if not (isComputed == None): if not (isComputed is None):
join.on.addConditionFieldEq("c.is_computed", isComputed) join.on.addConditionFieldEq("c.is_computed", isComputed)
sql.addFields("C.NAME") sql.addFields("C.NAME")
sql.where.addConditionFieldEq("t.name", sql_utils.SqlParam()) sql.where.addConditionFieldEq("t.name", sql_utils.SqlParam())
return sql_utils.normaliseDBfieldSet(self.dbQueryAll(sql, table, apply=lambda r : r.NAME)); return sql_utils.normaliseDBfieldSet(self.dbQueryAll(sql, table, apply=lambda r: r.NAME))
def getUniqueFieldsOfTable(self, table: str) -> Dict[str, List[str]]: def getUniqueFieldsOfTable(self, table: str) -> Dict[str, List[str]]:
""" """
@ -176,11 +175,9 @@ class APplusServer:
""" """
return applus_db.getUniqueFieldsOfTable(self.db_conn, table) return applus_db.getUniqueFieldsOfTable(self.db_conn, table)
def useXML(self, xml: str) -> Any: def useXML(self, xml: str) -> Any:
"""Ruft ``p2core.xml.usexml`` auf. Wird meist durch ein ``UseXMLRow-Objekt`` aufgerufen.""" """Ruft ``p2core.xml.usexml`` auf. Wird meist durch ein ``UseXMLRow-Objekt`` aufgerufen."""
return self.client_xml.service.useXML(xml); return self.client_xml.service.useXML(xml)
def mkUseXMLRowInsert(self, table: str) -> applus_usexml.UseXmlRowInsert: def mkUseXMLRowInsert(self, table: str) -> applus_usexml.UseXmlRowInsert:
""" """
@ -209,13 +206,12 @@ class APplusServer:
return applus_usexml.UseXmlRowInsertOrUpdate(self, table) return applus_usexml.UseXmlRowInsertOrUpdate(self, table)
def mkUseXMLRowDelete(self, table: str, id: int) -> applus_usexml.UseXmlRowDelete: def mkUseXMLRowDelete(self, table: str, id: int) -> applus_usexml.UseXmlRowDelete:
return applus_usexml.UseXmlRowDelete(self, table, id) return applus_usexml.UseXmlRowDelete(self, table, id)
def execUseXMLRowDelete(self, table: str, id: int) -> None: def execUseXMLRowDelete(self, table: str, id: int) -> None:
delRow = self.mkUseXMLRowDelete(table, id) delRow = self.mkUseXMLRowDelete(table, id)
delRow.delete(); delRow.delete()
def nextNumber(self, obj: str) -> str: def nextNumber(self, obj: str) -> str:
""" """
@ -225,29 +221,28 @@ class APplusServer:
def makeWebLink(self, base: str, **kwargs: Any) -> str: def makeWebLink(self, base: str, **kwargs: Any) -> str:
if not self.web_settings.baseurl: if not self.web_settings.baseurl:
raise Exception("keine Webserver-BaseURL gesetzt"); raise Exception("keine Webserver-BaseURL gesetzt")
url = str(self.web_settings.baseurl) + base; url = str(self.web_settings.baseurl) + base
firstArg = True firstArg = True
for arg, argv in kwargs.items(): for arg, argv in kwargs.items():
if not (argv == None): if not (argv is None):
if firstArg: if firstArg:
firstArg = False; firstArg = False
url += "?" url += "?"
else: else:
url += "&" url += "&"
url += arg + "=" + urllib.parse.quote(str(argv)) url += arg + "=" + urllib.parse.quote(str(argv))
return url; return url
def makeWebLinkWauftragPos(self, **kwargs: Any) -> str: def makeWebLinkWauftragPos(self, **kwargs: Any) -> str:
return self.makeWebLink("wp/wauftragPosRec.aspx", **kwargs); return self.makeWebLink("wp/wauftragPosRec.aspx", **kwargs)
def makeWebLinkWauftrag(self, **kwargs: Any) -> str: def makeWebLinkWauftrag(self, **kwargs: Any) -> str:
return self.makeWebLink("wp/wauftragRec.aspx", **kwargs); return self.makeWebLink("wp/wauftragRec.aspx", **kwargs)
def makeWebLinkBauftrag(self, **kwargs: Any) -> str: def makeWebLinkBauftrag(self, **kwargs: Any) -> str:
return self.makeWebLink("wp/bauftragRec.aspx", **kwargs); return self.makeWebLink("wp/bauftragRec.aspx", **kwargs)
def applusFromConfigDict(yamlDict: Dict[str, Any], user: Optional[str] = None, env: Optional[str] = None) -> APplusServer: def applusFromConfigDict(yamlDict: Dict[str, Any], user: Optional[str] = None, env: Optional[str] = None) -> APplusServer:
@ -268,8 +263,9 @@ def applusFromConfigDict(yamlDict:Dict[str, Any], user:Optional[str]=None, env:O
server=yamlDict["dbserver"]["server"], server=yamlDict["dbserver"]["server"],
database=yamlDict["dbserver"]["db"], database=yamlDict["dbserver"]["db"],
user=yamlDict["dbserver"]["user"], user=yamlDict["dbserver"]["user"],
password=yamlDict["dbserver"]["password"]); password=yamlDict["dbserver"]["password"])
return APplusServer(dbparams, app_server, web_server); return APplusServer(dbparams, app_server, web_server)
def applusFromConfigFile(yamlfile: 'FileDescriptorOrPath', def applusFromConfigFile(yamlfile: 'FileDescriptorOrPath',
user: Optional[str] = None, env: Optional[str] = None) -> APplusServer: user: Optional[str] = None, env: Optional[str] = None) -> APplusServer:
@ -280,8 +276,8 @@ def applusFromConfigFile(yamlfile : 'FileDescriptorOrPath',
return applusFromConfigDict(yamlDict, user=user, env=env) return applusFromConfigDict(yamlDict, user=user, env=env)
def applusFromConfig(yamlString: str, user: Optional[str] = None, env: Optional[str] = None) -> APplusServer: def applusFromConfig(yamlString: str, user: Optional[str] = None, env: Optional[str] = None) -> APplusServer:
"""Läd Einstellungen aus einer Config-Datei und erzeugt daraus ein APplus-Objekt""" """Läd Einstellungen aus einer Config-Datei und erzeugt daraus ein APplus-Objekt"""
yamlDict = yaml.safe_load(yamlString) yamlDict = yaml.safe_load(yamlString)
return applusFromConfigDict(yamlDict, user=user, env=env) return applusFromConfigDict(yamlDict, user=user, env=env)

View File

@ -6,16 +6,15 @@
# license that can be found in the LICENSE file or at # license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT. # https://opensource.org/licenses/MIT.
#-*- coding: utf-8 -*-
import pyodbc # type: ignore import pyodbc # type: ignore
import logging import logging
from .sql_utils import SqlStatement from .sql_utils import SqlStatement
from . import sql_utils from . import sql_utils
from typing import * from typing import List, Dict, Set, Any, Optional, Callable, Sequence
logger = logging.getLogger(__name__); logger = logging.getLogger(__name__)
class APplusDBSettings: class APplusDBSettings:
""" """
@ -24,11 +23,10 @@ class APplusDBSettings:
def __init__(self, server: str, database: str, user: str, password: str): def __init__(self, server: str, database: str, user: str, password: str):
self.server = server self.server = server
self.database = database; self.database = database
self.user = user self.user = user
self.password = password self.password = password
def getConnectionString(self) -> str: def getConnectionString(self) -> str:
"""Liefert den ODBC Connection-String für die Verbindung. """Liefert den ODBC Connection-String für die Verbindung.
:return: den Connection-String :return: den Connection-String
@ -45,17 +43,18 @@ class APplusDBSettings:
return pyodbc.connect(self.getConnectionString()) return pyodbc.connect(self.getConnectionString())
def row_to_dict(row: pyodbc.Row) -> Dict[str, Any]: def row_to_dict(row: pyodbc.Row) -> Dict[str, Any]:
"""Konvertiert eine Zeile in ein Dictionary""" """Konvertiert eine Zeile in ein Dictionary"""
return dict(zip([t[0] for t in row.cursor_description], row)) return dict(zip([t[0] for t in row.cursor_description], row))
def _logSQLWithArgs(sql: SqlStatement, *args: Any) -> None: def _logSQLWithArgs(sql: SqlStatement, *args: Any) -> None:
if args: if args:
logger.debug("executing '{}' with args {}".format(str(sql), str(args))) logger.debug("executing '{}' with args {}".format(str(sql), str(args)))
else: else:
logger.debug("executing '{}'".format(str(sql))) logger.debug("executing '{}'".format(str(sql)))
def rawQueryAll( def rawQueryAll(
cnxn: pyodbc.Connection, cnxn: pyodbc.Connection,
sql: SqlStatement, sql: SqlStatement,
@ -69,42 +68,46 @@ def rawQueryAll(
with cnxn.cursor() as cursor: with cnxn.cursor() as cursor:
cursor.execute(str(sql), *args) cursor.execute(str(sql), *args)
rows = cursor.fetchall(); rows = cursor.fetchall()
if apply is None: if apply is None:
return rows return rows
else: else:
res = [] res = []
for r in rows: for r in rows:
rr = apply(r) rr = apply(r)
if not (rr == None): if not (rr is None):
res.append(rr) res.append(rr)
return res return res
def rawQuery(cnxn: pyodbc.Connection, sql: sql_utils.SqlStatement, f: Callable[[pyodbc.Row], None], *args: Any) -> None: def rawQuery(cnxn: pyodbc.Connection, sql: sql_utils.SqlStatement, f: Callable[[pyodbc.Row], None], *args: Any) -> None:
"""Führt eine SQL Query direkt aus und führt für jede Zeile die übergeben Funktion aus.""" """Führt eine SQL Query direkt aus und führt für jede Zeile die übergeben Funktion aus."""
_logSQLWithArgs(sql, *args) _logSQLWithArgs(sql, *args)
with cnxn.cursor() as cursor: with cnxn.cursor() as cursor:
cursor.execute(str(sql), *args) cursor.execute(str(sql), *args)
for row in cursor: for row in cursor:
f(row); f(row)
def rawQuerySingleRow(cnxn: pyodbc.Connection, sql: SqlStatement, *args: Any) -> Optional[pyodbc.Row]: def rawQuerySingleRow(cnxn: pyodbc.Connection, sql: SqlStatement, *args: Any) -> Optional[pyodbc.Row]:
"""Führt eine SQL Query direkt aus, die maximal eine Zeile zurückliefern soll. Diese Zeile wird geliefert.""" """Führt eine SQL Query direkt aus, die maximal eine Zeile zurückliefern soll. Diese Zeile wird geliefert."""
_logSQLWithArgs(sql, *args) _logSQLWithArgs(sql, *args)
with cnxn.cursor() as cursor: with cnxn.cursor() as cursor:
cursor.execute(str(sql), *args) cursor.execute(str(sql), *args)
return cursor.fetchone(); return cursor.fetchone()
def rawQuerySingleValue(cnxn: pyodbc.Connection, sql: SqlStatement, *args: Any) -> Any: def rawQuerySingleValue(cnxn: pyodbc.Connection, sql: SqlStatement, *args: Any) -> Any:
"""Führt eine SQL Query direkt aus, die maximal einen Wert zurückliefern soll. Dieser Wert oder None wird geliefert.""" """Führt eine SQL Query direkt aus, die maximal einen Wert zurückliefern soll. Dieser Wert oder None wird geliefert."""
_logSQLWithArgs(sql, *args) _logSQLWithArgs(sql, *args)
with cnxn.cursor() as cursor: with cnxn.cursor() as cursor:
cursor.execute(str(sql), *args) cursor.execute(str(sql), *args)
row = cursor.fetchone(); row = cursor.fetchone()
if row: if row:
return row[0]; return row[0]
else: else:
return None; return None
def getUniqueFieldsOfTable(cnxn: pyodbc.Connection, table: str) -> Dict[str, List[str]]: def getUniqueFieldsOfTable(cnxn: pyodbc.Connection, table: str) -> Dict[str, List[str]]:
""" """
@ -149,7 +152,7 @@ class DBTableIDs():
""" """
table = table.upper() table = table.upper()
if not (table in self.data): if not (table in self.data):
self.data[table] = set(ids); self.data[table] = set(ids)
else: else:
self.data[table].update(ids) self.data[table].update(ids)
@ -167,5 +170,3 @@ class DBTableIDs():
def __str__(self) -> str: def __str__(self) -> str:
return str(self.data) return str(self.data)

View File

@ -6,14 +6,13 @@
# license that can be found in the LICENSE file or at # license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT. # https://opensource.org/licenses/MIT.
#-*- coding: utf-8 -*-
from .applus import APplusServer from .applus import APplusServer
from . import sql_utils from . import sql_utils
import lxml.etree as ET # type: ignore import lxml.etree as ET # type: ignore
from typing import * from typing import Optional, Tuple, Set
import pathlib import pathlib
class XMLDefinition: class XMLDefinition:
"""Repräsentation eines XML-Dokuments""" """Repräsentation eines XML-Dokuments"""
@ -32,10 +31,10 @@ class XMLDefinition:
:rtype: Tuple[Set[str], bool] :rtype: Tuple[Set[str], bool]
""" """
res: Set[str] = set() res: Set[str] = set()
excl = True; excl = True
dupl = self.root.find("duplicate") dupl = self.root.find("duplicate")
if (dupl is None): if (dupl is None):
return (res, excl); return (res, excl)
exclS = dupl.get("type", default="exclude") exclS = dupl.get("type", default="exclude")
excl = exclS.casefold() == "exclude" excl = exclS.casefold() == "exclude"
@ -91,13 +90,13 @@ class APplusScriptTool:
""" """
Liefert den Installionspfad des Appservers als PathLib-Path Liefert den Installionspfad des Appservers als PathLib-Path
""" """
return pathlib.Path(self.getInstallPath()); return pathlib.Path(self.getInstallPath())
def getInstallPathWebServer(self) -> pathlib.Path: def getInstallPathWebServer(self) -> pathlib.Path:
""" """
Liefert den Installionspfad des Webservers als PathLib-Path Liefert den Installionspfad des Webservers als PathLib-Path
""" """
return self.getInstallPathAppServer().parents[0].joinpath("WebServer"); return self.getInstallPathAppServer().parents[0].joinpath("WebServer")
def getXMLDefinitionString(self, obj: str, mandant: str = "") -> str: def getXMLDefinitionString(self, obj: str, mandant: str = "") -> str:
""" """
@ -140,19 +139,18 @@ class APplusScriptTool:
:return: das gefundene und geparste XML-Dokument :return: das gefundene und geparste XML-Dokument
:rtype: Optional[XMLDefinition] :rtype: Optional[XMLDefinition]
""" """
e = self.getXMLDefinition(obj, mandant=mandant); e = self.getXMLDefinition(obj, mandant=mandant)
if e is None: if e is None:
return None return None
if e.find("md5") is None: if e.find("md5") is None:
return None; return None
o = e.find("object") o = e.find("object")
if o is None: if o is None:
return None return None
else: else:
return XMLDefinition(o); return XMLDefinition(o)
def getMandant(self) -> str: def getMandant(self) -> str:
""" """

View File

@ -6,8 +6,6 @@
# license that can be found in the LICENSE file or at # license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT. # https://opensource.org/licenses/MIT.
#-*- coding: utf-8 -*-
from requests import Session # type: ignore from requests import Session # type: ignore
from requests.auth import HTTPBasicAuth # type: ignore # or HTTPDigestAuth, or OAuth1, etc. from requests.auth import HTTPBasicAuth # type: ignore # or HTTPDigestAuth, or OAuth1, etc.
from zeep import Client from zeep import Client
@ -27,17 +25,18 @@ class APplusAppServerSettings:
self.user = user self.user = user
self.env = env self.env = env
class APplusWebServerSettings: class APplusWebServerSettings:
""" """
Einstellungen, mit welchem APplus Web-Server sich verbunden werden soll. Einstellungen, mit welchem APplus Web-Server sich verbunden werden soll.
""" """
def __init__(self, baseurl: Optional[str] = None): def __init__(self, baseurl: Optional[str] = None):
self.baseurl : Optional[str] = baseurl; self.baseurl: Optional[str] = baseurl
try: try:
assert (isinstance(self.baseurl, str)) assert (isinstance(self.baseurl, str))
if not (self.baseurl == None) and not (self.baseurl[-1] == "/"): if not (self.baseurl is None) and not (self.baseurl[-1] == "/"):
self.baseurl = self.baseurl + "/"; self.baseurl = self.baseurl + "/"
except: except:
pass pass
@ -49,7 +48,7 @@ class APplusServerConnection:
:type settings: APplusAppServerSettings :type settings: APplusAppServerSettings
""" """
def __init__(self, settings: APplusAppServerSettings) -> None: def __init__(self, settings: APplusAppServerSettings) -> None:
userEnv = settings.user; userEnv = settings.user
if (settings.env): if (settings.env):
userEnv += "|" + settings.env userEnv += "|" + settings.env
@ -59,8 +58,8 @@ class APplusServerConnection:
self.transport = Transport(cache=SqliteCache(), session=session) self.transport = Transport(cache=SqliteCache(), session=session)
# self.transport = Transport(session=session) # self.transport = Transport(session=session)
self.clientCache: Dict[str, Client] = {} self.clientCache: Dict[str, Client] = {}
self.settings=settings; self.settings = settings
self.appserverUrl = "http://" + settings.appserver + ":" + str(settings.appserverPort) + "/"; self.appserverUrl = "http://" + settings.appserver + ":" + str(settings.appserverPort) + "/"
def getClient(self, package: str, name: str) -> Client: def getClient(self, package: str, name: str) -> Client:
"""Erzeugt einen zeep - Client. """Erzeugt einen zeep - Client.
@ -77,11 +76,11 @@ class APplusServerConnection:
:return: den Client :return: den Client
:rtype: Client :rtype: Client
""" """
url = package+"/"+name; url = package+"/"+name
try: try:
return self.clientCache[url]; return self.clientCache[url]
except: except:
fullClientUrl = self.appserverUrl + url + ".jws?wsdl" fullClientUrl = self.appserverUrl + url + ".jws?wsdl"
client = Client(fullClientUrl, transport=self.transport) client = Client(fullClientUrl, transport=self.transport)
self.clientCache[url] = client; self.clientCache[url] = client
return client; return client

View File

@ -6,9 +6,7 @@
# license that can be found in the LICENSE file or at # license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT. # https://opensource.org/licenses/MIT.
#-*- coding: utf-8 -*- from typing import TYPE_CHECKING, Optional, Dict, Any, Callable, Sequence
from typing import *
if TYPE_CHECKING: if TYPE_CHECKING:
from .applus import APplusServer from .applus import APplusServer
@ -28,32 +26,32 @@ class APplusSysConf:
self.cache: Dict[str, type] = {} self.cache: Dict[str, type] = {}
def clearCache(self) -> None: def clearCache(self) -> None:
self.cache = {}; self.cache = {}
def _getGeneral(self, ty: str, f: Callable[[str, str], Any], module: str, name: str, useCache: bool) -> Any: def _getGeneral(self, ty: str, f: Callable[[str, str], Any], module: str, name: str, useCache: bool) -> Any:
cacheKey = module + "/" + name + "/" + ty; cacheKey = module + "/" + name + "/" + ty
if useCache and cacheKey in self.cache: if useCache and cacheKey in self.cache:
return self.cache[cacheKey] return self.cache[cacheKey]
else: else:
v = f(module, name); v = f(module, name)
self.cache[cacheKey] = v; self.cache[cacheKey] = v
return v; return v
def getString(self, module: str, name: str, useCache: bool = True) -> str: def getString(self, module: str, name: str, useCache: bool = True) -> str:
return self._getGeneral("string", self.client.service.getString, module, name, useCache); return self._getGeneral("string", self.client.service.getString, module, name, useCache)
def getInt(self, module: str, name: str, useCache: bool = True) -> int: def getInt(self, module: str, name: str, useCache: bool = True) -> int:
return self._getGeneral("int", self.client.service.getInt, module, name, useCache); return self._getGeneral("int", self.client.service.getInt, module, name, useCache)
def getDouble(self, module: str, name: str, useCache: bool = True) -> float: def getDouble(self, module: str, name: str, useCache: bool = True) -> float:
return self._getGeneral("double", self.client.service.getDouble, module, name, useCache); return self._getGeneral("double", self.client.service.getDouble, module, name, useCache)
def getBoolean(self, module: str, name: str, useCache: bool = True) -> bool: def getBoolean(self, module: str, name: str, useCache: bool = True) -> bool:
return self._getGeneral("boolean", self.client.service.getBoolean, module, name, useCache); return self._getGeneral("boolean", self.client.service.getBoolean, module, name, useCache)
def getList(self, module: str, name: str, useCache: bool = True, sep: str = ",") -> Optional[Sequence[str]]: def getList(self, module: str, name: str, useCache: bool = True, sep: str = ",") -> Optional[Sequence[str]]:
s = self.getString(module, name, useCache=useCache); s = self.getString(module, name, useCache=useCache)
if (s == None or s == ""): if (s is None or s == ""):
return None return None
return s.split(sep); return s.split(sep)

View File

@ -6,26 +6,23 @@
# license that can be found in the LICENSE file or at # license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT. # https://opensource.org/licenses/MIT.
#-*- coding: utf-8 -*-
import lxml.etree as ET # type: ignore import lxml.etree as ET # type: ignore
from . import sql_utils from . import sql_utils
import datetime import datetime
from typing import * from typing import TYPE_CHECKING, Any, Dict, Optional
if TYPE_CHECKING: if TYPE_CHECKING:
from .applus import APplusServer from .applus import APplusServer
def _formatValueForXMLRow(v: Any) -> str: def _formatValueForXMLRow(v: Any) -> str:
"""Hilfsfunktion zum Formatieren eines Wertes für XML""" """Hilfsfunktion zum Formatieren eines Wertes für XML"""
if (v is None): if (v is None):
return ""; return ""
if isinstance(v, (int, float)): if isinstance(v, (int, float)):
return str(v); return str(v)
elif isinstance(v, str): elif isinstance(v, str):
return v; return v
elif isinstance(v, datetime.datetime): elif isinstance(v, datetime.datetime):
return v.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] return v.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
elif isinstance(v, datetime.date): elif isinstance(v, datetime.date):
@ -71,7 +68,7 @@ class UseXmlRow:
def _buildXML(self) -> ET.Element: def _buildXML(self) -> ET.Element:
"""Hilfsfunktion, die das eigentliche XML baut""" """Hilfsfunktion, die das eigentliche XML baut"""
row = ET.Element("row", cmd=self.cmd, table=self.table, nsmap={ "dt" : "urn:schemas-microsoft-com:datatypes"}); row = ET.Element("row", cmd=self.cmd, table=self.table, nsmap={"dt": "urn:schemas-microsoft-com:datatypes"})
for name, value in self.fields.items(): for name, value in self.fields.items():
child = ET.Element(name) child = ET.Element(name)
@ -80,7 +77,6 @@ class UseXmlRow:
return row return row
def toprettyxml(self) -> str: def toprettyxml(self) -> str:
""" """
Gibt das formatierte XML aus. Dieses kann per useXML an den AppServer übergeben werden. Gibt das formatierte XML aus. Dieses kann per useXML an den AppServer übergeben werden.
@ -93,7 +89,7 @@ class UseXmlRow:
if name is None: if name is None:
return None return None
name = sql_utils.normaliseDBfield(name); name = sql_utils.normaliseDBfield(name)
if name in self.fields: if name in self.fields:
return self.fields[name] return self.fields[name]
@ -116,7 +112,7 @@ class UseXmlRow:
return False return False
return True return True
def addField(self, name:str|None, value:Any) -> None: def addField(self, name: Optional[str], value: Any) -> None:
""" """
Fügt ein Feld zum Row-Node hinzu. Fügt ein Feld zum Row-Node hinzu.
@ -129,7 +125,6 @@ class UseXmlRow:
self.fields[sql_utils.normaliseDBfield(name)] = value self.fields[sql_utils.normaliseDBfield(name)] = value
def addTimestampField(self, id: int, ts: Optional[bytes] = None) -> None: def addTimestampField(self, id: int, ts: Optional[bytes] = None) -> None:
""" """
Fügt ein Timestamp-Feld hinzu. Wird kein Timestamp übergeben, wird mittels der ID der aktuelle Fügt ein Timestamp-Feld hinzu. Wird kein Timestamp übergeben, wird mittels der ID der aktuelle
@ -146,13 +141,12 @@ class UseXmlRow:
:type ts: bytes :type ts: bytes
""" """
if ts is None: if ts is None:
ts = self.applus.dbQuerySingleValue("select timestamp from " + self.table + " where id = ?", id); ts = self.applus.dbQuerySingleValue("select timestamp from " + self.table + " where id = ?", id)
if ts: if ts:
self.addField("timestamp", ts.hex()); self.addField("timestamp", ts.hex())
else: else:
raise Exception("kein Eintrag in Tabelle '" + self.table + " mit ID " + str(id) + " gefunden") raise Exception("kein Eintrag in Tabelle '" + self.table + " mit ID " + str(id) + " gefunden")
def addTimestampIDFields(self, id: int, ts: Optional[bytes] = None) -> None: def addTimestampIDFields(self, id: int, ts: Optional[bytes] = None) -> None:
""" """
Fügt ein Timestamp-Feld sowie ein Feld id hinzu. Wird kein Timestamp übergeben, wird mittels der ID der aktuelle Fügt ein Timestamp-Feld sowie ein Feld id hinzu. Wird kein Timestamp übergeben, wird mittels der ID der aktuelle
@ -164,14 +158,14 @@ class UseXmlRow:
:type ts: bytes :type ts: bytes
""" """
self.addField("id", id) self.addField("id", id)
self.addTimestampField(id, ts=ts); self.addTimestampField(id, ts=ts)
def exec(self) -> Any: def exec(self) -> Any:
""" """
Führt die UseXmlRow mittels useXML aus. Je nach Art der Zeile wird etwas zurückgeliefert oder nicht. Führt die UseXmlRow mittels useXML aus. Je nach Art der Zeile wird etwas zurückgeliefert oder nicht.
In jedem Fall kann eine Exception geworfen werden. In jedem Fall kann eine Exception geworfen werden.
""" """
return self.applus.useXML(self.toprettyxml()); return self.applus.useXML(self.toprettyxml())
class UseXmlRowInsert(UseXmlRow): class UseXmlRowInsert(UseXmlRow):
@ -185,14 +179,14 @@ class UseXmlRowInsert(UseXmlRow):
""" """
def __init__(self, applus: 'APplusServer', table: str) -> None: def __init__(self, applus: 'APplusServer', table: str) -> None:
super().__init__(applus, table, "insert"); super().__init__(applus, table, "insert")
def insert(self) -> int: def insert(self) -> int:
""" """
Führt das insert aus. Entweder wird dabei eine Exception geworfen oder die ID des neuen Eintrags zurückgegeben. Führt das insert aus. Entweder wird dabei eine Exception geworfen oder die ID des neuen Eintrags zurückgegeben.
Dies ist eine Umbenennung von :meth:`exec`. Dies ist eine Umbenennung von :meth:`exec`.
""" """
return super().exec(); return super().exec()
class UseXmlRowDelete(UseXmlRow): class UseXmlRowDelete(UseXmlRow):
@ -212,16 +206,15 @@ class UseXmlRowDelete(UseXmlRow):
""" """
def __init__(self, applus: 'APplusServer', table: str, id: int, ts: Optional[bytes] = None) -> None: def __init__(self, applus: 'APplusServer', table: str, id: int, ts: Optional[bytes] = None) -> None:
super().__init__(applus, table, "delete"); super().__init__(applus, table, "delete")
self.addTimestampIDFields(id, ts=ts); self.addTimestampIDFields(id, ts=ts)
def delete(self) -> None: def delete(self) -> None:
""" """
Führt das delete aus. Evtl. wird dabei eine Exception geworfen. Führt das delete aus. Evtl. wird dabei eine Exception geworfen.
Dies ist eine Umbenennung von :meth:`exec`. Dies ist eine Umbenennung von :meth:`exec`.
""" """
super().exec(); super().exec()
class UseXmlRowUpdate(UseXmlRow): class UseXmlRowUpdate(UseXmlRow):
@ -240,17 +233,15 @@ class UseXmlRowUpdate(UseXmlRow):
""" """
def __init__(self, applus: 'APplusServer', table: str, id: int, ts: Optional[bytes] = None) -> None: def __init__(self, applus: 'APplusServer', table: str, id: int, ts: Optional[bytes] = None) -> None:
super().__init__(applus, table, "update"); super().__init__(applus, table, "update")
self.addTimestampIDFields(id, ts=ts); self.addTimestampIDFields(id, ts=ts)
def update(self) -> None: def update(self) -> None:
""" """
Führt das update aus. Evtl. wird dabei eine Exception geworfen. Führt das update aus. Evtl. wird dabei eine Exception geworfen.
Dies ist eine Umbenennung von :meth:`exec`. Dies ist eine Umbenennung von :meth:`exec`.
""" """
super().exec(); super().exec()
class UseXmlRowInsertOrUpdate(UseXmlRow): class UseXmlRowInsertOrUpdate(UseXmlRow):
@ -268,20 +259,19 @@ class UseXmlRowInsertOrUpdate(UseXmlRow):
""" """
def __init__(self, applus: 'APplusServer', table: str) -> None: def __init__(self, applus: 'APplusServer', table: str) -> None:
super().__init__(applus, table, ""); super().__init__(applus, table, "")
def checkExists(self) -> Optional[int]:
def checkExists(self) -> int|None:
""" """
Prüft, ob der Datensatz bereits in der DB existiert. Prüft, ob der Datensatz bereits in der DB existiert.
Ist dies der Fall, wird die ID geliefert, sonst None Ist dies der Fall, wird die ID geliefert, sonst None
""" """
# Baue Bedingung # Baue Bedingung
cond = sql_utils.SqlConditionOr(); cond = sql_utils.SqlConditionOr()
for idx, fs in self.applus.getUniqueFieldsOfTable(self.table).items(): for idx, fs in self.applus.getUniqueFieldsOfTable(self.table).items():
if (self.checkFieldsSet(*fs)): if (self.checkFieldsSet(*fs)):
condIdx = sql_utils.SqlConditionAnd(); condIdx = sql_utils.SqlConditionAnd()
for f in fs: for f in fs:
condIdx.addConditionFieldEq(f, self.getField(f)) condIdx.addConditionFieldEq(f, self.getField(f))
cond.addCondition(condIdx) cond.addCondition(condIdx)
@ -296,7 +286,7 @@ class UseXmlRowInsertOrUpdate(UseXmlRow):
r = UseXmlRowInsert(self.applus, self.table) r = UseXmlRowInsert(self.applus, self.table)
for k, v in self.fields.items(): for k, v in self.fields.items():
r.addField(k, v) r.addField(k, v)
return r.insert(); return r.insert()
def update(self, id: Optional[int] = None, ts: Optional[bytes] = None) -> int: def update(self, id: Optional[int] = None, ts: Optional[bytes] = None) -> int:
"""Führt ein Update aus. Falls ID oder Timestamp nicht übergeben werden, wird """Führt ein Update aus. Falls ID oder Timestamp nicht übergeben werden, wird
@ -311,7 +301,7 @@ class UseXmlRowInsertOrUpdate(UseXmlRow):
r = UseXmlRowUpdate(self.applus, self.table, id, ts=ts) r = UseXmlRowUpdate(self.applus, self.table, id, ts=ts)
for k, v in self.fields.items(): for k, v in self.fields.items():
r.addField(k, v) r.addField(k, v)
r.update(); r.update()
return id return id
def exec(self) -> int: def exec(self) -> int:
@ -320,8 +310,8 @@ class UseXmlRowInsertOrUpdate(UseXmlRow):
der DB existiert. In jedem Fall wird die ID des erzeugten oder geänderten Objekts geliefert. der DB existiert. In jedem Fall wird die ID des erzeugten oder geänderten Objekts geliefert.
""" """
id = self.checkExists(); id = self.checkExists()
if id == None: if id is None:
return self.insert() return self.insert()
else: else:
return self.update(id=id) return self.update(id=id)
@ -332,4 +322,4 @@ class UseXmlRowInsertOrUpdate(UseXmlRow):
Dies ist eine Umbenennung von :meth:`exec`. Dies ist eine Umbenennung von :meth:`exec`.
Es wird die ID des Eintrages geliefert Es wird die ID des Eintrages geliefert
""" """
return self.exec(); return self.exec()

View File

@ -6,8 +6,6 @@
# license that can be found in the LICENSE file or at # license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT. # https://opensource.org/licenses/MIT.
#-*- coding: utf-8 -*-
""" """
Dupliziert ein oder mehrere APplus Business-Objekte Dupliziert ein oder mehrere APplus Business-Objekte
""" """
@ -19,9 +17,9 @@ from .applus import APplusServer
import pyodbc # type: ignore import pyodbc # type: ignore
import traceback import traceback
import logging import logging
from typing import * from typing import List, Set, Optional, Dict, Tuple, Sequence, Any, Union
logger = logging.getLogger(__name__); logger = logging.getLogger(__name__)
noCopyFields = sql_utils.normaliseDBfieldSet({"INSUSER", "UPDDATE", "TIMESTAMP", "MANDANT", "GUID", "ID", "TIMESTAMP_A", "INSDATE", "ID_A", "UPDUSER"}) noCopyFields = sql_utils.normaliseDBfieldSet({"INSUSER", "UPDDATE", "TIMESTAMP", "MANDANT", "GUID", "ID", "TIMESTAMP_A", "INSDATE", "ID_A", "UPDUSER"})
"""Menge von Feld-Namen, die nie kopiert werden sollen.""" """Menge von Feld-Namen, die nie kopiert werden sollen."""
@ -38,7 +36,7 @@ def getFieldsToCopyForTable(server : APplusServer, table : str, force:bool=True)
fields: Set[str] fields: Set[str]
if (xmlDefs is None): if (xmlDefs is None):
if not force: if not force:
raise Exception ("Keine XML-Definitionen für '{}' gefunden".format(table)); raise Exception("Keine XML-Definitionen für '{}' gefunden".format(table))
(fields, excl) = (set(), True) (fields, excl) = (set(), True)
else: else:
(fields, excl) = xmlDefs.getDuplicate() (fields, excl) = xmlDefs.getDuplicate()
@ -46,8 +44,7 @@ def getFieldsToCopyForTable(server : APplusServer, table : str, force:bool=True)
return fields.difference(noCopyFields) return fields.difference(noCopyFields)
allFields = server.getTableFields(table, isComputed=False) allFields = server.getTableFields(table, isComputed=False)
return allFields.difference(fields).difference(noCopyFields); return allFields.difference(fields).difference(noCopyFields)
class FieldsToCopyForTableCache(): class FieldsToCopyForTableCache():
@ -85,7 +82,7 @@ def initFieldsToCopyForTableCacheIfNeeded(server : APplusServer, cache : Optiona
if cache is None: if cache is None:
return FieldsToCopyForTableCache(server) return FieldsToCopyForTableCache(server)
else: else:
return cache; return cache
class DuplicateBusinessObject(): class DuplicateBusinessObject():
@ -176,9 +173,9 @@ class DuplicateBusinessObject():
nonlocal res nonlocal res
insertRow: applus_usexml.UseXmlRow insertRow: applus_usexml.UseXmlRow
if do.allowUpdate: if do.allowUpdate:
insertRow = server.mkUseXMLRowInsertOrUpdate(do.table); insertRow = server.mkUseXMLRowInsertOrUpdate(do.table)
else: else:
insertRow = server.mkUseXMLRowInsert(do.table); insertRow = server.mkUseXMLRowInsert(do.table)
for f, v in do.fields.items(): for f, v in do.fields.items():
insertRow.addField(f, v) insertRow.addField(f, v)
@ -188,7 +185,7 @@ class DuplicateBusinessObject():
res.add(do.table, id) res.add(do.table, id)
return id return id
except: except:
msg = traceback.format_exc(); msg = traceback.format_exc()
logger.error("Exception inserting BusinessObjekt: %s\n%s", str(insertRow), msg) logger.error("Exception inserting BusinessObjekt: %s\n%s", str(insertRow), msg)
return None return None
@ -209,7 +206,7 @@ class DuplicateBusinessObject():
# load missing fields from DB # load missing fields from DB
if len(connectMissing) > 0: if len(connectMissing) > 0:
sql = sql_utils.SqlStatementSelect(do.table); sql = sql_utils.SqlStatementSelect(do.table)
sql.where.addConditionFieldEq("id", doID) sql.where.addConditionFieldEq("id", doID)
for fd in connectMissing: for fd in connectMissing:
sql.addFields(fd) sql.addFields(fd)
@ -224,7 +221,6 @@ class DuplicateBusinessObject():
if not (id is None): if not (id is None):
insertDeps(so, id) insertDeps(so, id)
def insertDeps(do: 'DuplicateBusinessObject', doID: int) -> None: def insertDeps(do: 'DuplicateBusinessObject', doID: int) -> None:
for so in do.dependentObjs: for so in do.dependentObjs:
insertDep(do, doID, so["dependentObj"], so["connection"]) insertDep(do, doID, so["dependentObj"], so["connection"])
@ -235,7 +231,6 @@ class DuplicateBusinessObject():
return res return res
def setFields(self, upds: Dict[str, Any]) -> None: def setFields(self, upds: Dict[str, Any]) -> None:
""" """
Setzt Felder des DuplicateBusinessObjektes und falls nötig seiner Unterobjekte. Setzt Felder des DuplicateBusinessObjektes und falls nötig seiner Unterobjekte.
@ -257,14 +252,12 @@ class DuplicateBusinessObject():
subupds[fs] = upds[fp] subupds[fs] = upds[fp]
setFieldsInternal(su["dependentObj"], subupds) setFieldsInternal(su["dependentObj"], subupds)
updsNorm: Dict[str, Any] = {} updsNorm: Dict[str, Any] = {}
for f, v in upds.items(): for f, v in upds.items():
updsNorm[sql_utils.normaliseDBfield(f)] = v updsNorm[sql_utils.normaliseDBfield(f)] = v
setFieldsInternal(self, updsNorm) setFieldsInternal(self, updsNorm)
def _loadDBDuplicateBusinessObjectDict( def _loadDBDuplicateBusinessObjectDict(
server: APplusServer, server: APplusServer,
table: str, table: str,
@ -282,7 +275,7 @@ def _loadDBDuplicateBusinessObjectDict(
:param cache: Cache, so dass benötigte Felder nicht immer wieder neu berechnet werden müssen :param cache: Cache, so dass benötigte Felder nicht immer wieder neu berechnet werden müssen
:return: das neue DuplicateBusinessObject :return: das neue DuplicateBusinessObject
""" """
table = table.upper(); table = table.upper()
def getFieldsToCopy() -> Set[str]: def getFieldsToCopy() -> Set[str]:
if cache is None: if cache is None:
@ -290,20 +283,18 @@ def _loadDBDuplicateBusinessObjectDict(
else: else:
return cache.getFieldsToCopyForTable(table) return cache.getFieldsToCopyForTable(table)
def getFields() -> Tuple[Dict[str, Any], Dict[str, Any]]: def getFields() -> Tuple[Dict[str, Any], Dict[str, Any]]:
ftc = getFieldsToCopy() ftc = getFieldsToCopy()
fields = {} fields = {}
fieldsNotCopied = {} fieldsNotCopied = {}
for f, v in applus_db.row_to_dict(row).items(): for f, v in applus_db.row_to_dict(row).items():
f = sql_utils.normaliseDBfield(f); f = sql_utils.normaliseDBfield(f)
if f in ftc: if f in ftc:
fields[f] = v fields[f] = v
else: else:
fieldsNotCopied[f] = v fieldsNotCopied[f] = v
return (fields, fieldsNotCopied) return (fields, fieldsNotCopied)
if (row is None): if (row is None):
return None return None
@ -335,21 +326,22 @@ def loadDBDuplicateBusinessObject(
:return: das neue DuplicateBusinessObject :return: das neue DuplicateBusinessObject
:rtype: Optional[DuplicateBusinessObject] :rtype: Optional[DuplicateBusinessObject]
""" """
table = table.upper(); table = table.upper()
def getRow() -> pyodbc.Row: def getRow() -> pyodbc.Row:
sql = sql_utils.SqlStatementSelect(table) sql = sql_utils.SqlStatementSelect(table)
sql.setTop(1) sql.setTop(1)
sql.where.addCondition(cond); sql.where.addCondition(cond)
return server.dbQuerySingleRow(sql) return server.dbQuerySingleRow(sql)
return _loadDBDuplicateBusinessObjectDict(server, table, getRow(), cache=cache, allowUpdate=allowUpdate); return _loadDBDuplicateBusinessObjectDict(server, table, getRow(), cache=cache, allowUpdate=allowUpdate)
def loadDBDuplicateBusinessObjectSimpleCond( def loadDBDuplicateBusinessObjectSimpleCond(
server: APplusServer, server: APplusServer,
table: str, table: str,
field: str, field: str,
value : Optional[Union[sql_utils.SqlValue, bool]], value: Union[sql_utils.SqlValue, bool, None],
cache: Optional[FieldsToCopyForTableCache] = None, cache: Optional[FieldsToCopyForTableCache] = None,
allowUpdate: bool = False) -> Optional[DuplicateBusinessObject]: allowUpdate: bool = False) -> Optional[DuplicateBusinessObject]:
""" """
@ -402,11 +394,12 @@ def loadDBDuplicateBusinessObjects(
sql.where.addCondition(cond) sql.where.addCondition(cond)
return server.dbQueryAll(sql, apply=processRow) return server.dbQueryAll(sql, apply=processRow)
def loadDBDuplicateBusinessObjectsSimpleCond( def loadDBDuplicateBusinessObjectsSimpleCond(
server: APplusServer, server: APplusServer,
table: str, table: str,
field: str, field: str,
value : Optional[Union[sql_utils.SqlValue, bool]], value: Union[sql_utils.SqlValue, bool, None],
cache: Optional[FieldsToCopyForTableCache] = None, cache: Optional[FieldsToCopyForTableCache] = None,
allowUpdate: bool = False) -> Sequence[DuplicateBusinessObject]: allowUpdate: bool = False) -> Sequence[DuplicateBusinessObject]:
""" """
@ -449,7 +442,7 @@ def loadDBDuplicateAPlan(
:rtype: DuplicateBusinessObject :rtype: DuplicateBusinessObject
""" """
cache = initFieldsToCopyForTableCacheIfNeeded(server, cache); cache = initFieldsToCopyForTableCacheIfNeeded(server, cache)
boMain = loadDBDuplicateBusinessObjectSimpleCond(server, "aplan", "APLAN", aplan, cache=cache) boMain = loadDBDuplicateBusinessObjectSimpleCond(server, "aplan", "APLAN", aplan, cache=cache)
if boMain is None: if boMain is None:
return None return None
@ -474,7 +467,7 @@ def loadDBDuplicateStueli(server : APplusServer, stueli : str, cache:Optional[Fi
:rtype: Optional[DuplicateBusinessObject] :rtype: Optional[DuplicateBusinessObject]
""" """
cache = initFieldsToCopyForTableCacheIfNeeded(server, cache); cache = initFieldsToCopyForTableCacheIfNeeded(server, cache)
boMain = loadDBDuplicateBusinessObjectSimpleCond(server, "stueli", "stueli", stueli, cache=cache) boMain = loadDBDuplicateBusinessObjectSimpleCond(server, "stueli", "stueli", stueli, cache=cache)
if boMain is None: if boMain is None:
return None return None
@ -484,6 +477,7 @@ def loadDBDuplicateStueli(server : APplusServer, stueli : str, cache:Optional[Fi
return boMain return boMain
def addSachgruppeDependentObjects( def addSachgruppeDependentObjects(
do: DuplicateBusinessObject, do: DuplicateBusinessObject,
server: APplusServer, server: APplusServer,
@ -498,9 +492,9 @@ def addSachgruppeDependentObjects(
:type cache: Optional[FieldsToCopyForTableCache] :type cache: Optional[FieldsToCopyForTableCache]
""" """
cache = initFieldsToCopyForTableCacheIfNeeded(server, cache); cache = initFieldsToCopyForTableCacheIfNeeded(server, cache)
klasse = do.fields.get(sql_utils.normaliseDBfield("SACHGRUPPENKLASSE"), None) klasse = do.fields.get(sql_utils.normaliseDBfield("SACHGRUPPENKLASSE"), None)
if (klasse == None): if (klasse is None):
# keine Klasse gesetzt, nichts zu kopieren # keine Klasse gesetzt, nichts zu kopieren
return return
@ -511,7 +505,7 @@ def addSachgruppeDependentObjects(
sql.where.addConditionFieldEq("tabelle", do.table) sql.where.addConditionFieldEq("tabelle", do.table)
return server.dbQueryAll(sql, apply=lambda r: r.sachgruppe) return server.dbQueryAll(sql, apply=lambda r: r.sachgruppe)
gruppen = loadGruppen(); gruppen = loadGruppen()
# Gruppe bearbeiten # Gruppe bearbeiten
def processGruppen() -> None: def processGruppen() -> None:
@ -525,11 +519,9 @@ def addSachgruppeDependentObjects(
for so in loadDBDuplicateBusinessObjects(server, "sachwert", cond, cache=cache, allowUpdate=True): for so in loadDBDuplicateBusinessObjects(server, "sachwert", cond, cache=cache, allowUpdate=True):
do.addDependentBusinessObject(so, ("guid", "instanzguid")) do.addDependentBusinessObject(so, ("guid", "instanzguid"))
processGruppen() processGruppen()
def loadDBDuplicateArtikel( def loadDBDuplicateArtikel(
server: APplusServer, server: APplusServer,
artikel: str, artikel: str,
@ -554,7 +546,7 @@ def loadDBDuplicateArtikel(
:rtype: DuplicateBusinessObject :rtype: DuplicateBusinessObject
""" """
cache = initFieldsToCopyForTableCacheIfNeeded(server, cache); cache = initFieldsToCopyForTableCacheIfNeeded(server, cache)
boArt = loadDBDuplicateBusinessObjectSimpleCond(server, "artikel", "ARTIKEL", artikel, cache=cache) boArt = loadDBDuplicateBusinessObjectSimpleCond(server, "artikel", "ARTIKEL", artikel, cache=cache)
if boArt is None: if boArt is None:
return None return None

View File

@ -8,14 +8,13 @@
"""Pandas Interface für PyAPplus64.""" """Pandas Interface für PyAPplus64."""
from typing import Annotated as Ann
import pandas as pd # type: ignore import pandas as pd # type: ignore
from pandas._typing import AggFuncType, FilePath, WriteExcelBuffer # type: ignore from pandas._typing import AggFuncType, FilePath, WriteExcelBuffer # type: ignore
import sqlalchemy import sqlalchemy
import traceback import traceback
from .applus import APplusServer from .applus import APplusServer
from .applus import sql_utils from .applus import sql_utils
from typing import * from typing import Optional, Callable, Sequence, Tuple, Any, Union
def createSqlAlchemyEngine(server: APplusServer) -> sqlalchemy.Engine: def createSqlAlchemyEngine(server: APplusServer) -> sqlalchemy.Engine:
@ -36,32 +35,32 @@ def pandasReadSql(
""" """
if engine is None: if engine is None:
engine = createSqlAlchemyEngine(server); engine = createSqlAlchemyEngine(server)
with engine.connect() as conn: with engine.connect() as conn:
return pd.read_sql(sqlalchemy.text(server.completeSQL(sql, raw=raw)), conn) return pd.read_sql(sqlalchemy.text(server.completeSQL(sql, raw=raw)), conn)
def _createHyperLinkGeneral(genOrg : Callable[[], str|int|float], genLink: Callable[[], str]) -> str|int|float: def _createHyperLinkGeneral(genOrg: Callable[[], Union[str, int, float]], genLink: Callable[[], str]) -> Union[str, int, float]:
""" """
Hilfsfunktion zum Generieren eines Excel-Links. Hilfsfunktion zum Generieren eines Excel-Links.
:param genLink: Funktion, die Parameter aufgerufen wird und einen Link generiert :param genLink: Funktion, die Parameter aufgerufen wird und einen Link generiert
""" """
org:str|int|float="" org: Union[str, int, float] = ""
org2:str|int|float org2: Union[str, int, float]
try: try:
org = genOrg(); org = genOrg()
if not org: if not org:
return org return org
else: else:
if isinstance(org, (int, float)): if isinstance(org, (int, float)):
org2 = org; org2 = org
else: else:
org2 = "\"" + str(org).replace("\"", "\"\"") + "\"" org2 = "\"" + str(org).replace("\"", "\"\"") + "\""
return "=HYPERLINK(\"{}\", {})".format(genLink(), org2) return "=HYPERLINK(\"{}\", {})".format(genLink(), org2)
except: except:
msg = traceback.format_exc(); msg = traceback.format_exc()
print("Exception: {}".format(msg)) print("Exception: {}".format(msg))
return org return org
@ -78,14 +77,14 @@ def mkDataframeColumn(df : pd.DataFrame, makeValue : AggFuncType) -> pd.Series:
try: try:
return makeValue(r) return makeValue(r)
except: except:
msg = traceback.format_exc(); msg = traceback.format_exc()
print("Exception: {}".format(msg)) print("Exception: {}".format(msg))
return "" return ""
if (len(df.index) > 0): if (len(df.index) > 0):
return df.apply(mkValueWrapper, axis=1) return df.apply(mkValueWrapper, axis=1)
else: else:
return df.apply(lambda r: "", axis=1); return df.apply(lambda r: "", axis=1)
def mkHyperlinkDataframeColumn(df: pd.DataFrame, makeOrig: AggFuncType, makeLink: Callable[[Any], str]) -> pd.Series: def mkHyperlinkDataframeColumn(df: pd.DataFrame, makeOrig: AggFuncType, makeLink: Callable[[Any], str]) -> pd.Series:
@ -100,11 +99,11 @@ def mkHyperlinkDataframeColumn(df : pd.DataFrame, makeOrig : AggFuncType, makeLi
if (len(df.index) > 0): if (len(df.index) > 0):
return df.apply(lambda r: _createHyperLinkGeneral(lambda: makeOrig(r), lambda: makeLink(r)), axis=1) return df.apply(lambda r: _createHyperLinkGeneral(lambda: makeOrig(r), lambda: makeLink(r)), axis=1)
else: else:
return df.apply(lambda r: "", axis=1); return df.apply(lambda r: "", axis=1)
def exportToExcel( def exportToExcel(
filename:FilePath | WriteExcelBuffer | pd.ExcelWriter, filename: Union[FilePath, WriteExcelBuffer, pd.ExcelWriter],
dfs: Sequence[Tuple[pd.DataFrame, str]], dfs: Sequence[Tuple[pd.DataFrame, str]],
addTable: bool = True) -> None: addTable: bool = True) -> None:
""" """
@ -126,6 +125,4 @@ def exportToExcel(
ws.add_table(0, 0, max_row, max_col - 1, {'columns': column_settings}) ws.add_table(0, 0, max_row, max_col - 1, {'columns': column_settings})
# Spaltenbreiten anpassen # Spaltenbreiten anpassen
ws.autofit(); ws.autofit()

View File

@ -6,7 +6,6 @@
# license that can be found in the LICENSE file or at # license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT. # https://opensource.org/licenses/MIT.
#-*- coding: utf-8 -*-
""" """
Diese Datei enthält Funktionen für den Bau von SQL Statements, besonders Diese Datei enthält Funktionen für den Bau von SQL Statements, besonders
SELECT-Statements. Es gibt viel ausgefeiltere Methoden für die Erstellung von SELECT-Statements. Es gibt viel ausgefeiltere Methoden für die Erstellung von
@ -22,19 +21,22 @@ APplus. Oft ist es sinnvoll, solche Parameter zu verwenden.
from __future__ import annotations from __future__ import annotations
import datetime import datetime
from typing import * from typing import Set, Sequence, Union, Optional, cast, List
def normaliseDBfield(f: str) -> str: def normaliseDBfield(f: str) -> str:
"""Normalisiert die Darstellung eines DB-Feldes""" """Normalisiert die Darstellung eines DB-Feldes"""
return str(f).upper(); return str(f).upper()
def normaliseDBfieldSet(s: Set[str]) -> Set[str]: def normaliseDBfieldSet(s: Set[str]) -> Set[str]:
"""Normalisiert eine Menge von DB-Feldern""" """Normalisiert eine Menge von DB-Feldern"""
return {normaliseDBfield(f) for f in s} return {normaliseDBfield(f) for f in s}
def normaliseDBfieldList(l : Sequence[str]) -> Sequence[str]:
def normaliseDBfieldList(fields: Sequence[str]) -> Sequence[str]:
"""Normalisiert eine Menge von DB-Feldern""" """Normalisiert eine Menge von DB-Feldern"""
return [normaliseDBfield(f) for f in l] return [normaliseDBfield(f) for f in fields]
class SqlField(): class SqlField():
@ -45,10 +47,11 @@ class SqlField():
:type fn: str :type fn: str
""" """
def __init__(self, fn: str): def __init__(self, fn: str):
self.field = normaliseDBfield(fn); self.field = normaliseDBfield(fn)
def __str__(self) -> str: def __str__(self) -> str:
return self.field; return self.field
class SqlFixed(): class SqlFixed():
""" """
@ -58,10 +61,11 @@ class SqlFixed():
:type s: str :type s: str
""" """
def __init__(self, s: str): def __init__(self, s: str):
self.s = str(s); self.s = str(s)
def __str__(self) -> str: def __str__(self) -> str:
return self.s; return self.s
class SqlDateTime(): class SqlDateTime():
""" """
@ -71,13 +75,14 @@ class SqlDateTime():
:type dt: Union[datetime.datetime, datetime.date] :type dt: Union[datetime.datetime, datetime.date]
""" """
def __init__(self, dt: Union[datetime.datetime, datetime.date] = datetime.datetime.now()) -> None: def __init__(self, dt: Union[datetime.datetime, datetime.date] = datetime.datetime.now()) -> None:
self.value = dt; self.value = dt
def __str__(self) -> str: def __str__(self) -> str:
# %f formatiert mit 6 Stellen, also microseconds. Es werden aber nur # %f formatiert mit 6 Stellen, also microseconds. Es werden aber nur
# 3 Stellen unterstützt, daher werden 3 weggeworfen. # 3 Stellen unterstützt, daher werden 3 weggeworfen.
return self.value.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] return self.value.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]
class SqlDate(): class SqlDate():
""" """
Wrapper um DateTime, die die Formatierung erleichtern Wrapper um DateTime, die die Formatierung erleichtern
@ -86,10 +91,11 @@ class SqlDate():
:type d: Union[datetime.datetime, datetime.date] :type d: Union[datetime.datetime, datetime.date]
""" """
def __init__(self, d: Union[datetime.datetime, datetime.date] = datetime.datetime.now()) -> None: def __init__(self, d: Union[datetime.datetime, datetime.date] = datetime.datetime.now()) -> None:
self.value = d; self.value = d
def __str__(self) -> str: def __str__(self) -> str:
return self.value.strftime("%Y%m%d"); return self.value.strftime("%Y%m%d")
class SqlTime(): class SqlTime():
""" """
@ -104,6 +110,7 @@ class SqlTime():
def __str__(self) -> str: def __str__(self) -> str:
return self.value.strftime("%H:%M:%S.%f")[:-3] return self.value.strftime("%H:%M:%S.%f")[:-3]
class SqlParam(): class SqlParam():
"""Hilfsklasse, für einen Parameter (?)""" """Hilfsklasse, für einen Parameter (?)"""
def __init__(self) -> None: def __init__(self) -> None:
@ -112,9 +119,11 @@ class SqlParam():
def __str__(self) -> str: def __str__(self) -> str:
return "?" return "?"
sqlParam = SqlParam() sqlParam = SqlParam()
"""Da SqlParam keinen Zustand hat, reicht ein einzelner statischer Wert""" """Da SqlParam keinen Zustand hat, reicht ein einzelner statischer Wert"""
def formatSqlValueString(s: str) -> str: def formatSqlValueString(s: str) -> str:
""" """
Formatiert einen String für ein Sql-Statement. Der String wird in "'" eingeschlossen Formatiert einen String für ein Sql-Statement. Der String wird in "'" eingeschlossen
@ -127,14 +136,15 @@ def formatSqlValueString(s:str) -> str:
""" """
if (s is None): if (s is None):
return "''"; return "''"
return "'" + str(s).replace("'", "''") + "'"; return "'" + str(s).replace("'", "''") + "'"
SqlValue : TypeAlias = Union[str, int, float, SqlParam, SqlField, SqlFixed, SqlDate, SqlDateTime, datetime.datetime, datetime.date, datetime.time] SqlValue = Union[str, int, float, SqlParam, SqlField, SqlFixed, SqlDate, SqlDateTime, datetime.datetime, datetime.date, datetime.time]
"""Union-Type aller unterstützter SQL-Werte""" """Union-Type aller unterstützter SQL-Werte"""
def formatSqlValue(v: SqlValue) -> str: def formatSqlValue(v: SqlValue) -> str:
""" """
Formatiert einen Wert für SQL. Je nachdem um welchen Typ es sich handelt, werden andere Formatierungen verwendet. Formatiert einen Wert für SQL. Je nachdem um welchen Typ es sich handelt, werden andere Formatierungen verwendet.
@ -145,25 +155,26 @@ def formatSqlValue(v : SqlValue) -> str:
:rtype: str :rtype: str
""" """
if (v == None): if v is None:
raise Exception("formatSqlValue: null not supported"); raise Exception("formatSqlValue: null not supported")
if isinstance(v, (int, float, SqlField)): if isinstance(v, (int, float, SqlField)):
return str(v); return str(v)
elif isinstance(v, str): elif isinstance(v, str):
return formatSqlValueString(v); return formatSqlValueString(v)
elif isinstance(v, datetime.datetime): elif isinstance(v, datetime.datetime):
return "'" + str(SqlDateTime(v)) + "'"; return "'" + str(SqlDateTime(v)) + "'"
elif isinstance(v, datetime.date): elif isinstance(v, datetime.date):
return "'" + str(SqlDate(v)) + "'"; return "'" + str(SqlDate(v)) + "'"
elif isinstance(v, datetime.time): elif isinstance(v, datetime.time):
return "'" + str(SqlTime(v)) + "'"; return "'" + str(SqlTime(v)) + "'"
elif isinstance(v, (SqlDateTime, SqlDate, SqlTime)): elif isinstance(v, (SqlDateTime, SqlDate, SqlTime)):
return "'" + str(v) + "'"; return "'" + str(v) + "'"
elif isinstance(v, (SqlParam, SqlFixed)): elif isinstance(v, (SqlParam, SqlFixed)):
return str(v); return str(v)
else: else:
raise Exception("formatSqlValue: unsupported type {}".format(type(v))); raise Exception("formatSqlValue: unsupported type {}".format(type(v)))
class SqlCondition(): class SqlCondition():
"""Eine abstrakte Sql-Bedingung. Unterklassen erledigen die eigentliche Arbeit.""" """Eine abstrakte Sql-Bedingung. Unterklassen erledigen die eigentliche Arbeit."""
@ -175,20 +186,21 @@ class SqlCondition():
:return: die Bedingung :return: die Bedingung
:rtype: str :rtype: str
""" """
raise Exception("Not implemented"); raise Exception("Not implemented")
def __str__(self) -> str: def __str__(self) -> str:
return self.getCondition(); return self.getCondition()
class SqlConditionPrepared(SqlCondition): class SqlConditionPrepared(SqlCondition):
"""Eine einfache Sql-Bedingung, die immer einen festen String zurückgibt.""" """Eine einfache Sql-Bedingung, die immer einen festen String zurückgibt."""
def __init__(self, cond: Union[SqlCondition, str]): def __init__(self, cond: Union[SqlCondition, str]):
self.cond = str(cond); self.cond = str(cond)
def getCondition(self) -> str: def getCondition(self) -> str:
return self.cond; return self.cond
class SqlConditionTrue(SqlConditionPrepared): class SqlConditionTrue(SqlConditionPrepared):
"""True-Bedingung""" """True-Bedingung"""
@ -196,12 +208,14 @@ class SqlConditionTrue(SqlConditionPrepared):
def __init__(self) -> None: def __init__(self) -> None:
super().__init__("(1=1)") super().__init__("(1=1)")
class SqlConditionFalse(SqlConditionPrepared): class SqlConditionFalse(SqlConditionPrepared):
"""False-Bedingung""" """False-Bedingung"""
def __init__(self) -> None: def __init__(self) -> None:
super().__init__("(1=0)") super().__init__("(1=0)")
class SqlConditionBool(SqlConditionPrepared): class SqlConditionBool(SqlConditionPrepared):
"""Fixe True-oder-False Bedingung""" """Fixe True-oder-False Bedingung"""
@ -211,6 +225,7 @@ class SqlConditionBool(SqlConditionPrepared):
else: else:
super().__init__(SqlConditionFalse()) super().__init__(SqlConditionFalse())
class SqlConditionNot(SqlCondition): class SqlConditionNot(SqlCondition):
""" """
Negation einer anderen Bedingung Negation einer anderen Bedingung
@ -220,10 +235,10 @@ class SqlConditionNot(SqlCondition):
""" """
def __init__(self, cond: SqlCondition): def __init__(self, cond: SqlCondition):
self.cond = cond; self.cond = cond
def getCondition(self) -> str: def getCondition(self) -> str:
return "(not {})".format(self.cond.getCondition()); return "(not {})".format(self.cond.getCondition())
class SqlConditionIsNull(SqlConditionPrepared): class SqlConditionIsNull(SqlConditionPrepared):
@ -237,10 +252,12 @@ class SqlConditionIsNull(SqlConditionPrepared):
def __init__(self, v: SqlValue): def __init__(self, v: SqlValue):
super().__init__("({} is null)".format(formatSqlValue(v))) super().__init__("({} is null)".format(formatSqlValue(v)))
class SqlConditionFieldIsNull(SqlConditionIsNull): class SqlConditionFieldIsNull(SqlConditionIsNull):
def __init__(self, field: str): def __init__(self, field: str):
super().__init__(SqlField(field)) super().__init__(SqlField(field))
class SqlConditionIsNotNull(SqlConditionPrepared): class SqlConditionIsNotNull(SqlConditionPrepared):
""" """
Wert soll nicht null sein Wert soll nicht null sein
@ -252,10 +269,12 @@ class SqlConditionIsNotNull(SqlConditionPrepared):
def __init__(self, v: SqlValue): def __init__(self, v: SqlValue):
super().__init__("({} is not null)".format(formatSqlValue(v))) super().__init__("({} is not null)".format(formatSqlValue(v)))
class SqlConditionFieldIsNotNull(SqlConditionIsNotNull): class SqlConditionFieldIsNotNull(SqlConditionIsNotNull):
def __init__(self, field: str): def __init__(self, field: str):
super().__init__(SqlField(field)) super().__init__(SqlField(field))
class SqlConditionStringStartsWith(SqlConditionPrepared): class SqlConditionStringStartsWith(SqlConditionPrepared):
""" """
Feld soll mit einem bestimmten String beginnen Feld soll mit einem bestimmten String beginnen
@ -267,9 +286,9 @@ class SqlConditionStringStartsWith(SqlConditionPrepared):
""" """
def __init__(self, field: str, value: str): def __init__(self, field: str, value: str):
cond = ""; cond = ""
if value: if value:
cond="(left({}, {}) = {})".format(normaliseDBfield(field), len(value), formatSqlValueString(value)); cond = "(left({}, {}) = {})".format(normaliseDBfield(field), len(value), formatSqlValueString(value))
else: else:
cond = "(1=1)" cond = "(1=1)"
super().__init__(cond) super().__init__(cond)
@ -285,8 +304,8 @@ class SqlConditionFieldStringNotEmpty(SqlConditionPrepared):
""" """
def __init__(self, field: str): def __init__(self, field: str):
field = normaliseDBfield(field); field = normaliseDBfield(field)
cond="({} is not null and {} != '')".format(field, field); cond = "({} is not null and {} != '')".format(field, field)
super().__init__(cond) super().__init__(cond)
@ -312,6 +331,7 @@ class SqlConditionIn(SqlConditionPrepared):
cond = "({} in ({}))".format(formatSqlValue(value), valuesS) cond = "({} in ({}))".format(formatSqlValue(value), valuesS)
super().__init__(cond) super().__init__(cond)
class SqlConditionFieldIn(SqlConditionIn): class SqlConditionFieldIn(SqlConditionIn):
def __init__(self, field: str, values: Sequence[SqlValue]): def __init__(self, field: str, values: Sequence[SqlValue]):
super().__init__(SqlField(field), values) super().__init__(SqlField(field), values)
@ -324,7 +344,7 @@ class SqlConditionEq(SqlConditionPrepared):
:param value1: der Wert, kann unterschiedliche Typen besitzen :param value1: der Wert, kann unterschiedliche Typen besitzen
:param value2: der Wert, kann unterschiedliche Typen besitzen :param value2: der Wert, kann unterschiedliche Typen besitzen
""" """
def __init__(self, value1 : Optional[Union[SqlValue, bool]], value2 : Optional[Union[SqlValue, bool]]): def __init__(self, value1: Union[SqlValue, bool, None], value2: Union[SqlValue, bool, None]):
cond: Union[SqlCondition, str] cond: Union[SqlCondition, str]
if (value1 is None) and (value2 is None): if (value1 is None) and (value2 is None):
cond = SqlConditionTrue() cond = SqlConditionTrue()
@ -340,13 +360,13 @@ class SqlConditionEq(SqlConditionPrepared):
cond = SqlConditionIsNull(value1) cond = SqlConditionIsNull(value1)
else: else:
if isinstance(value1, bool) and isinstance(value2, bool): if isinstance(value1, bool) and isinstance(value2, bool):
cond = SqlConditionBool(value1 == value2); cond = SqlConditionBool(value1 == value2)
elif isinstance(value1, bool) and not isinstance(value2, bool): elif isinstance(value1, bool) and not isinstance(value2, bool):
value2 = cast(SqlValue, value2) value2 = cast(SqlValue, value2)
if value1: if value1:
cond = "({} = 1)".format(formatSqlValue(value2)) cond = "({} = 1)".format(formatSqlValue(value2))
else: else:
cond = "({} = 0 OR {} is null)".format(formatSqlValue(value2), formatSqlValue(value2)); cond = "({} = 0 OR {} is null)".format(formatSqlValue(value2), formatSqlValue(value2))
elif not isinstance(value1, bool) and isinstance(value2, bool): elif not isinstance(value1, bool) and isinstance(value2, bool):
value1 = cast(SqlValue, value1) value1 = cast(SqlValue, value1)
if value2: if value2:
@ -356,7 +376,7 @@ class SqlConditionEq(SqlConditionPrepared):
else: else:
value1 = cast(SqlValue, value1) value1 = cast(SqlValue, value1)
value2 = cast(SqlValue, value2) value2 = cast(SqlValue, value2)
cond = "({} = {})".format(formatSqlValue(value1), formatSqlValue(value2)); cond = "({} = {})".format(formatSqlValue(value1), formatSqlValue(value2))
super().__init__(cond) super().__init__(cond)
@ -372,10 +392,10 @@ class SqlConditionBinComp(SqlConditionPrepared):
:type value2: SqlValue :type value2: SqlValue
""" """
def __init__(self, op: str, value1: SqlValue, value2: SqlValue): def __init__(self, op: str, value1: SqlValue, value2: SqlValue):
if not(value1) or not(value2): if not value1 or not value2:
raise Exception("SqlConditionBinComp: value not provided") raise Exception("SqlConditionBinComp: value not provided")
cond = "({} {} {})".format(formatSqlValue(value1), op, formatSqlValue(value2)); cond = "({} {} {})".format(formatSqlValue(value1), op, formatSqlValue(value2))
super().__init__(cond) super().__init__(cond)
@ -389,6 +409,7 @@ class SqlConditionLt(SqlConditionBinComp):
def __init__(self, value1: SqlValue, value2: SqlValue): def __init__(self, value1: SqlValue, value2: SqlValue):
super().__init__("<", value1, value2) super().__init__("<", value1, value2)
class SqlConditionLe(SqlConditionBinComp): class SqlConditionLe(SqlConditionBinComp):
""" """
Bedingung der Form 'value1 <= value2' Bedingung der Form 'value1 <= value2'
@ -399,6 +420,7 @@ class SqlConditionLe(SqlConditionBinComp):
def __init__(self, value1: SqlValue, value2: SqlValue): def __init__(self, value1: SqlValue, value2: SqlValue):
super().__init__("<=", value1, value2) super().__init__("<=", value1, value2)
class SqlConditionGt(SqlConditionBinComp): class SqlConditionGt(SqlConditionBinComp):
""" """
Bedingung der Form 'value1 > value2' Bedingung der Form 'value1 > value2'
@ -409,6 +431,7 @@ class SqlConditionGt(SqlConditionBinComp):
def __init__(self, value1: SqlValue, value2: SqlValue): def __init__(self, value1: SqlValue, value2: SqlValue):
super().__init__(">", value1, value2) super().__init__(">", value1, value2)
class SqlConditionGe(SqlConditionBinComp): class SqlConditionGe(SqlConditionBinComp):
""" """
Bedingung der Form 'value1 >= value2' Bedingung der Form 'value1 >= value2'
@ -421,25 +444,30 @@ class SqlConditionGe(SqlConditionBinComp):
class SqlConditionFieldEq(SqlConditionEq): class SqlConditionFieldEq(SqlConditionEq):
def __init__(self, field : str, value : Optional[Union[SqlValue, bool]]): def __init__(self, field: str, value: Union[SqlValue, bool, None]):
super().__init__(SqlField(field), value) super().__init__(SqlField(field), value)
class SqlConditionFieldLt(SqlConditionLt): class SqlConditionFieldLt(SqlConditionLt):
def __init__(self, field: str, value: SqlValue): def __init__(self, field: str, value: SqlValue):
super().__init__(SqlField(field), value) super().__init__(SqlField(field), value)
class SqlConditionFieldLe(SqlConditionLe): class SqlConditionFieldLe(SqlConditionLe):
def __init__(self, field: str, value: SqlValue): def __init__(self, field: str, value: SqlValue):
super().__init__(SqlField(field), value) super().__init__(SqlField(field), value)
class SqlConditionFieldGt(SqlConditionGt): class SqlConditionFieldGt(SqlConditionGt):
def __init__(self, field: str, value: SqlValue): def __init__(self, field: str, value: SqlValue):
super().__init__(SqlField(field), value) super().__init__(SqlField(field), value)
class SqlConditionFieldGe(SqlConditionGe): class SqlConditionFieldGe(SqlConditionGe):
def __init__(self, field: str, value: SqlValue): def __init__(self, field: str, value: SqlValue):
super().__init__(SqlField(field), value) super().__init__(SqlField(field), value)
class SqlConditionList(SqlCondition): class SqlConditionList(SqlCondition):
""" """
Eine SQL Bedingung, die sich aus einer Liste anderer Bedingungen zusammensetzen. Eine SQL Bedingung, die sich aus einer Liste anderer Bedingungen zusammensetzen.
@ -452,93 +480,93 @@ class SqlConditionList(SqlCondition):
""" """
def __init__(self, connector: str, emptyCond: str): def __init__(self, connector: str, emptyCond: str):
self.connector : str = connector; self.connector: str = connector
self.emptyCond : str = emptyCond; self.emptyCond: str = emptyCond
self.elems: List[SqlCondition] = [] self.elems: List[SqlCondition] = []
def addCondition(self, cond : SqlCondition | str | None) -> None: def addCondition(self, cond: Union[SqlCondition, str, None]) -> None:
if (cond is None): if (cond is None):
return return
if not (isinstance(cond, SqlCondition)): if not (isinstance(cond, SqlCondition)):
cond = SqlConditionPrepared("("+str(cond)+")"); cond = SqlConditionPrepared("("+str(cond)+")")
self.elems.append(cond); self.elems.append(cond)
def addConditions(self, *conds : SqlCondition | str | None) -> None: def addConditions(self, *conds: Union[SqlCondition, str, None]) -> None:
for cond in conds: for cond in conds:
self.addCondition(cond) self.addCondition(cond)
def addConditionFieldStringNotEmpty(self, field: str) -> None: def addConditionFieldStringNotEmpty(self, field: str) -> None:
self.addCondition(SqlConditionFieldStringNotEmpty(field)); self.addCondition(SqlConditionFieldStringNotEmpty(field))
def addConditionFieldIn(self, field: str, values: Sequence[SqlValue]) -> None: def addConditionFieldIn(self, field: str, values: Sequence[SqlValue]) -> None:
self.addCondition(SqlConditionFieldIn(field, values)); self.addCondition(SqlConditionFieldIn(field, values))
def addConditionFieldEq(self, field : str, value : Optional[Union[SqlValue, bool]]) -> None: def addConditionFieldEq(self, field: str, value: Union[SqlValue, bool, None]) -> None:
self.addCondition(SqlConditionFieldEq(field, value)); self.addCondition(SqlConditionFieldEq(field, value))
def addConditionFieldsEq(self, field1: str, field2: str) -> None: def addConditionFieldsEq(self, field1: str, field2: str) -> None:
self.addCondition(SqlConditionEq(SqlField(field1), SqlField(field2))); self.addCondition(SqlConditionEq(SqlField(field1), SqlField(field2)))
def addConditionEq(self, value1 : Optional[Union[SqlValue, bool]], value2 : Optional[Union[SqlValue, bool]]) -> None: def addConditionEq(self, value1: Union[SqlValue, bool, None], value2: Union[SqlValue, bool, None]) -> None:
self.addCondition(SqlConditionEq(value1, value2)); self.addCondition(SqlConditionEq(value1, value2))
def addConditionGe(self, value1: SqlValue, value2: SqlValue) -> None: def addConditionGe(self, value1: SqlValue, value2: SqlValue) -> None:
self.addCondition(SqlConditionGe(value1, value2)); self.addCondition(SqlConditionGe(value1, value2))
def addConditionFieldGe(self, field: str, value: SqlValue) -> None: def addConditionFieldGe(self, field: str, value: SqlValue) -> None:
self.addCondition(SqlConditionGe(SqlField(field), value)); self.addCondition(SqlConditionGe(SqlField(field), value))
def addConditionFieldsGe(self, field1: str, field2: str) -> None: def addConditionFieldsGe(self, field1: str, field2: str) -> None:
self.addCondition(SqlConditionGe(SqlField(field1), SqlField(field2))); self.addCondition(SqlConditionGe(SqlField(field1), SqlField(field2)))
def addConditionLe(self, value1: SqlValue, value2: SqlValue) -> None: def addConditionLe(self, value1: SqlValue, value2: SqlValue) -> None:
self.addCondition(SqlConditionLe(value1, value2)); self.addCondition(SqlConditionLe(value1, value2))
def addConditionFieldLe(self, field: str, value: SqlValue) -> None: def addConditionFieldLe(self, field: str, value: SqlValue) -> None:
self.addCondition(SqlConditionLe(SqlField(field), value)); self.addCondition(SqlConditionLe(SqlField(field), value))
def addConditionFieldsLe(self, field1: str, field2: str) -> None: def addConditionFieldsLe(self, field1: str, field2: str) -> None:
self.addCondition(SqlConditionLe(SqlField(field1), SqlField(field2))); self.addCondition(SqlConditionLe(SqlField(field1), SqlField(field2)))
def addConditionGt(self, value1: SqlValue, value2: SqlValue) -> None: def addConditionGt(self, value1: SqlValue, value2: SqlValue) -> None:
self.addCondition(SqlConditionGt(value1, value2)); self.addCondition(SqlConditionGt(value1, value2))
def addConditionFieldGt(self, field: str, value: SqlValue) -> None: def addConditionFieldGt(self, field: str, value: SqlValue) -> None:
self.addCondition(SqlConditionGt(SqlField(field), value)); self.addCondition(SqlConditionGt(SqlField(field), value))
def addConditionFieldsGt(self, field1: str, field2: str) -> None: def addConditionFieldsGt(self, field1: str, field2: str) -> None:
self.addCondition(SqlConditionGt(SqlField(field1), SqlField(field2))); self.addCondition(SqlConditionGt(SqlField(field1), SqlField(field2)))
def addConditionLt(self, value1: SqlValue, value2: SqlValue) -> None: def addConditionLt(self, value1: SqlValue, value2: SqlValue) -> None:
self.addCondition(SqlConditionLt(value1, value2)); self.addCondition(SqlConditionLt(value1, value2))
def addConditionFieldLt(self, field: str, value: SqlValue) -> None: def addConditionFieldLt(self, field: str, value: SqlValue) -> None:
self.addCondition(SqlConditionLt(SqlField(field), value)); self.addCondition(SqlConditionLt(SqlField(field), value))
def addConditionFieldsLt(self, field1: str, field2: str) -> None: def addConditionFieldsLt(self, field1: str, field2: str) -> None:
self.addCondition(SqlConditionLt(SqlField(field1), SqlField(field2))); self.addCondition(SqlConditionLt(SqlField(field1), SqlField(field2)))
def addConditionFieldIsNull(self, field: str) -> None: def addConditionFieldIsNull(self, field: str) -> None:
self.addCondition(SqlConditionFieldIsNull(field)); self.addCondition(SqlConditionFieldIsNull(field))
def addConditionFieldIsNotNull(self, field: str) -> None: def addConditionFieldIsNotNull(self, field: str) -> None:
self.addCondition(SqlConditionFieldIsNotNull(field)); self.addCondition(SqlConditionFieldIsNotNull(field))
def isEmpty(self) -> bool: def isEmpty(self) -> bool:
return not(self.elems); return not self.elems
def getCondition(self) -> str: def getCondition(self) -> str:
match (len(self.elems)): elemLen = len(self.elems)
case 0: if elemLen == 0:
return self.emptyCond; return self.emptyCond
case 1: elif elemLen == 1:
return self.elems[0].getCondition(); return self.elems[0].getCondition()
case l: else:
res = "(" + self.elems[0].getCondition(); res = "(" + self.elems[0].getCondition()
for i in range(1, l): for i in range(1, elemLen):
res += " {} {}".format(self.connector, self.elems[i].getCondition()) res += " {} {}".format(self.connector, self.elems[i].getCondition())
res += ")"; res += ")"
return res; return res
class SqlConditionDateTimeFieldInRange(SqlConditionPrepared): class SqlConditionDateTimeFieldInRange(SqlConditionPrepared):
@ -550,7 +578,7 @@ class SqlConditionDateTimeFieldInRange(SqlConditionPrepared):
:param datetimeVon: der untere Wert (einschließlich), None erlaubt beliebige Zeiten :param datetimeVon: der untere Wert (einschließlich), None erlaubt beliebige Zeiten
:param datetimeBis: der obere Wert (ausschließlich), None erlaubt beliebige Zeiten :param datetimeBis: der obere Wert (ausschließlich), None erlaubt beliebige Zeiten
""" """
def __init__(self, field : str, datetimeVon : datetime.datetime|None, datetimeBis : datetime.datetime|None): def __init__(self, field: str, datetimeVon: Optional[datetime.datetime], datetimeBis: Optional[datetime.datetime]):
cond = SqlConditionAnd() cond = SqlConditionAnd()
if not (datetimeVon is None): if not (datetimeVon is None):
cond.addConditionFieldGe(field, datetimeVon) cond.addConditionFieldGe(field, datetimeVon)
@ -558,6 +586,7 @@ class SqlConditionDateTimeFieldInRange(SqlConditionPrepared):
cond.addConditionFieldLt(field, datetimeBis) cond.addConditionFieldLt(field, datetimeBis)
super().__init__(str(cond)) super().__init__(str(cond))
class SqlConditionDateTimeFieldInMonth(SqlConditionPrepared): class SqlConditionDateTimeFieldInMonth(SqlConditionPrepared):
""" """
Liegt Datetime in einem bestimmten Monat? Liegt Datetime in einem bestimmten Monat?
@ -570,15 +599,17 @@ class SqlConditionDateTimeFieldInMonth(SqlConditionPrepared):
def __init__(self, field: str, year: int, month: int): def __init__(self, field: str, year: int, month: int):
if month == 12: if month == 12:
nyear = year+1 nyear = year+1
nmonth=1; nmonth = 1
else: else:
nyear = year nyear = year
nmonth=month+1; nmonth = month+1
cond = SqlConditionDateTimeFieldInRange(field, cond = SqlConditionDateTimeFieldInRange(
field,
datetime.datetime(year=year, month=month, day=1), datetime.datetime(year=year, month=month, day=1),
datetime.datetime(year=nyear, month=nmonth, day=1)) datetime.datetime(year=nyear, month=nmonth, day=1))
super().__init__(str(cond)) super().__init__(str(cond))
class SqlConditionDateTimeFieldInYear(SqlConditionPrepared): class SqlConditionDateTimeFieldInYear(SqlConditionPrepared):
""" """
Liegt Datetime in einem bestimmten Jahr? Liegt Datetime in einem bestimmten Jahr?
@ -589,11 +620,13 @@ class SqlConditionDateTimeFieldInYear(SqlConditionPrepared):
""" """
def __init__(self, field: str, year: int) -> None: def __init__(self, field: str, year: int) -> None:
nyear = year+1 nyear = year+1
cond = SqlConditionDateTimeFieldInRange(field, cond = SqlConditionDateTimeFieldInRange(
field,
datetime.datetime(year=year, month=1, day=1), datetime.datetime(year=year, month=1, day=1),
datetime.datetime(year=nyear, month=1, day=1)) datetime.datetime(year=nyear, month=1, day=1))
super().__init__(str(cond)) super().__init__(str(cond))
class SqlConditionDateTimeFieldInDay(SqlConditionPrepared): class SqlConditionDateTimeFieldInDay(SqlConditionPrepared):
""" """
Liegt Datetime in einem bestimmten Monat? Liegt Datetime in einem bestimmten Monat?
@ -606,16 +639,19 @@ class SqlConditionDateTimeFieldInDay(SqlConditionPrepared):
""" """
def __init__(self, field: str, year: int, month: int, day: int) -> None: def __init__(self, field: str, year: int, month: int, day: int) -> None:
d = datetime.datetime(year=year, month=month, day=day) d = datetime.datetime(year=year, month=month, day=day)
cond = SqlConditionDateTimeFieldInRange(field, cond = SqlConditionDateTimeFieldInRange(
field,
d, d,
d + datetime.timedelta(days=1)) d + datetime.timedelta(days=1))
super().__init__(str(cond)) super().__init__(str(cond))
class SqlConditionAnd(SqlConditionList): class SqlConditionAnd(SqlConditionList):
def __init__(self, *conds: Union[SqlCondition, str]) -> None: def __init__(self, *conds: Union[SqlCondition, str]) -> None:
super().__init__("AND", "(1=1)") super().__init__("AND", "(1=1)")
self.addConditions(*conds) self.addConditions(*conds)
class SqlConditionOr(SqlConditionList): class SqlConditionOr(SqlConditionList):
def __init__(self, *conds: Union[SqlCondition, str]) -> None: def __init__(self, *conds: Union[SqlCondition, str]) -> None:
super().__init__("OR", "(1=0)") super().__init__("OR", "(1=0)")
@ -645,10 +681,10 @@ class SqlJoin():
""" """
Liefert den Join als String Liefert den Join als String
""" """
return self.joinType + " " + self.table + " ON " + self.on.getCondition(); return self.joinType + " " + self.table + " ON " + self.on.getCondition()
def __str__(self) -> str: def __str__(self) -> str:
return self.getJoin(); return self.getJoin()
class SqlInnerJoin(SqlJoin): class SqlInnerJoin(SqlJoin):
@ -663,6 +699,7 @@ class SqlInnerJoin(SqlJoin):
def __init__(self, table: str, *conds: Union[SqlCondition, str]) -> None: def __init__(self, table: str, *conds: Union[SqlCondition, str]) -> None:
super().__init__("INNER JOIN", table, *conds) super().__init__("INNER JOIN", table, *conds)
class SqlLeftJoin(SqlJoin): class SqlLeftJoin(SqlJoin):
""" """
Ein Left-Join. Ein Left-Join.
@ -674,6 +711,7 @@ class SqlLeftJoin(SqlJoin):
def __init__(self, table: str, *conds: Union[SqlCondition, str]) -> None: def __init__(self, table: str, *conds: Union[SqlCondition, str]) -> None:
super().__init__("LEFT JOIN", table, *conds) super().__init__("LEFT JOIN", table, *conds)
class SqlStatementSelect(): class SqlStatementSelect():
""" """
Klasse, um einfache Select-Statements zu bauen. Klasse, um einfache Select-Statements zu bauen.
@ -690,38 +728,38 @@ class SqlStatementSelect():
self.top: int = 0 self.top: int = 0
"""wie viele Datensätze auswählen? 0 für alle""" """wie viele Datensätze auswählen? 0 für alle"""
self.where : SqlConditionList = SqlConditionAnd(); self.where: SqlConditionList = SqlConditionAnd()
"""die Bedingung, Default ist True""" """die Bedingung, Default ist True"""
self.fields: List[str] = [] self.fields: List[str] = []
"""Liste von auszuwählenden Feldern""" """Liste von auszuwählenden Feldern"""
self.addFields(*fields) self.addFields(*fields)
self.joins : List[SqlJoin|str] = [] self.joins: List[Union[SqlJoin, str]] = []
"""Joins mit extra Tabellen""" """Joins mit extra Tabellen"""
self.groupBy : List[str] = []; self.groupBy: List[str] = []
"""die Bedingung, Default ist True""" """die Bedingung, Default ist True"""
self.having : SqlConditionList = SqlConditionAnd(); self.having: SqlConditionList = SqlConditionAnd()
"""die Bedingung having, Default ist True""" """die Bedingung having, Default ist True"""
self.order: Optional[str] = None self.order: Optional[str] = None
"""Sortierung""" """Sortierung"""
def __str__(self) -> str: def __str__(self) -> str:
return self.getSql(); return self.getSql()
def addFields(self, *fields: str) -> None: def addFields(self, *fields: str) -> None:
"""Fügt ein oder mehrere Felder, also auszuwählende Werte zu einem SQL-Statement hinzu.""" """Fügt ein oder mehrere Felder, also auszuwählende Werte zu einem SQL-Statement hinzu."""
for f in fields: for f in fields:
if not (f == None): if not (f is None):
self.fields.append(f) self.fields.append(f)
def addGroupBy(self, *fields: str) -> None: def addGroupBy(self, *fields: str) -> None:
"""Fügt ein oder mehrere GroupBy Felder zu einem SQL-Statement hinzu.""" """Fügt ein oder mehrere GroupBy Felder zu einem SQL-Statement hinzu."""
for f in fields: for f in fields:
if not (f == None): if not (f is None):
self.groupBy.append(f) self.groupBy.append(f)
def setTop(self, t: int) -> None: def setTop(self, t: int) -> None:
@ -735,10 +773,10 @@ class SqlStatementSelect():
Dies kann im Vergleich zu 'addFields' Schreibarbeit erleitern. Dies kann im Vergleich zu 'addFields' Schreibarbeit erleitern.
""" """
for f in fields: for f in fields:
if not (f == None): if not (f is None):
self.fields.append(table + "." + str(f)) self.fields.append(table + "." + str(f))
def addJoin(self, j : SqlJoin|str) -> None: def addJoin(self, j: Union[SqlJoin, str]) -> None:
"""Fügt ein Join zum SQL-Statement hinzu. Beispiel: 'LEFT JOIN personal p ON t.UPDUSER = p.PERSONAL'""" """Fügt ein Join zum SQL-Statement hinzu. Beispiel: 'LEFT JOIN personal p ON t.UPDUSER = p.PERSONAL'"""
self.joins.append(j) self.joins.append(j)
@ -755,36 +793,35 @@ class SqlStatementSelect():
def getSql(self) -> str: def getSql(self) -> str:
"""Liefert das SQL-SELECT-Statement als String""" """Liefert das SQL-SELECT-Statement als String"""
def getFields() -> str: def getFields() -> str:
match (len(self.fields)): fieldsLen = len(self.fields)
case 0: if fieldsLen == 0:
return "*"; return "*"
case 1: elif fieldsLen == 1:
return str(self.fields[0]); return str(self.fields[0])
case l: else:
res = str(self.fields[0]); res = str(self.fields[0])
for i in range(1, l): for i in range(1, fieldsLen):
res += ", " + str(self.fields[i]); res += ", " + str(self.fields[i])
return res; return res
def getGroupBy() -> str: def getGroupBy() -> str:
match (len(self.groupBy)): groupByLen = len(self.groupBy)
case 0: if groupByLen == 0:
return ""; return ""
case l: else:
res = " GROUP BY " + str(self.fields[0]) res = " GROUP BY " + str(self.fields[0])
for i in range(1, l): for i in range(1, groupByLen):
res += ", " + str(self.fields[i]); res += ", " + str(self.fields[i])
if not (self.having.isEmpty()): if not (self.having.isEmpty()):
res += " HAVING " + str(self.having) res += " HAVING " + str(self.having)
return res; return res
def getJoins() -> str: def getJoins() -> str:
match (len(self.joins)): if (len(self.joins) == 0):
case 0:
return "" return ""
case l: else:
res = ""; res = ""
for i in range(0, l): for i in range(0, len(self.joins)):
res += " " + str(self.joins[i]) res += " " + str(self.joins[i])
return res return res
@ -795,7 +832,7 @@ class SqlStatementSelect():
return " WHERE " + str(self.where) return " WHERE " + str(self.where)
def getOrder() -> str: def getOrder() -> str:
if self.order == None: if self.order is None:
return "" return ""
else: else:
return " ORDER BY " + str(self.order) return " ORDER BY " + str(self.order)
@ -806,8 +843,7 @@ class SqlStatementSelect():
else: else:
return "TOP " + str(self.top) + " " return "TOP " + str(self.top) + " "
return "SELECT " + getTop() + getFields() + " FROM " + self.table + getJoins() + getWhere() + getGroupBy() + getOrder(); return "SELECT " + getTop() + getFields() + " FROM " + self.table + getJoins() + getWhere() + getGroupBy() + getOrder()
SqlStatement : TypeAlias = Union [SqlStatementSelect, str] SqlStatement = Union[SqlStatementSelect, str]

View File

@ -6,11 +6,10 @@
# license that can be found in the LICENSE file or at # license that can be found in the LICENSE file or at
# https://opensource.org/licenses/MIT. # https://opensource.org/licenses/MIT.
#-*- coding: utf-8 -*-
import pathlib import pathlib
import datetime import datetime
from typing import * from typing import Set, Union
def checkDirExists(dir: Union[str, pathlib.Path]) -> pathlib.Path: def checkDirExists(dir: Union[str, pathlib.Path]) -> pathlib.Path:
"""Prüft, ob ein Verzeichnis existiert. Ist dies nicht möglich, wird eine Exception geworfen. """Prüft, ob ein Verzeichnis existiert. Ist dies nicht möglich, wird eine Exception geworfen.
@ -26,19 +25,19 @@ def checkDirExists(dir : Union[str, pathlib.Path]) -> pathlib.Path:
dir = dir.resolve() dir = dir.resolve()
if not (dir.exists()): if not (dir.exists()):
raise Exception("Verzeichnis '" + str(dir) + "' nicht gefunden"); raise Exception("Verzeichnis '" + str(dir) + "' nicht gefunden")
if not (dir.is_dir()): if not (dir.is_dir()):
raise Exception("'" + str(dir) + "' ist kein Verzeichnis"); raise Exception("'" + str(dir) + "' ist kein Verzeichnis")
return dir; return dir
def formatDateTimeForAPplus(v: Union[datetime.datetime, datetime.date, datetime.time]) -> str: def formatDateTimeForAPplus(v: Union[datetime.datetime, datetime.date, datetime.time]) -> str:
"""Formatiert ein Datum oder eine Uhrzeit für APplus""" """Formatiert ein Datum oder eine Uhrzeit für APplus"""
if (v == None): if v is None:
return ""; return ""
elif isinstance(v, str): elif isinstance(v, str):
return v; return v
elif isinstance(v, datetime.datetime): elif isinstance(v, datetime.datetime):
return v.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3] return v.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
elif isinstance(v, datetime.date): elif isinstance(v, datetime.date):
@ -48,6 +47,7 @@ def formatDateTimeForAPplus(v : Union[datetime.datetime, datetime.date, datetime
else: else:
return str(v) return str(v)
def containsOnlyAllowedChars(charset: Set[str], s: str) -> bool: def containsOnlyAllowedChars(charset: Set[str], s: str) -> bool:
"""Enthält ein String nur erlaubte Zeichen?""" """Enthält ein String nur erlaubte Zeichen?"""
for c in s: for c in s:

View File

@ -7,10 +7,10 @@
# https://opensource.org/licenses/MIT. # https://opensource.org/licenses/MIT.
from PyAPplus64 import applus_db from PyAPplus64 import applus_db
import datetime
def test_DBTableIDs1() -> None: def test_DBTableIDs1() -> None:
ids = applus_db.DBTableIDs(); ids = applus_db.DBTableIDs()
assert (str(ids) == "{}") assert (str(ids) == "{}")
ids.add("t1", 1) ids.add("t1", 1)
assert (str(ids) == "{'T1': {1}}") assert (str(ids) == "{'T1': {1}}")

View File

@ -9,284 +9,350 @@
from PyAPplus64 import sql_utils from PyAPplus64 import sql_utils
import datetime import datetime
def test_normaliseDBField1() -> None: def test_normaliseDBField1() -> None:
assert (sql_utils.normaliseDBfield("aAa") == "AAA") assert (sql_utils.normaliseDBfield("aAa") == "AAA")
assert (sql_utils.normaliseDBfield("a#Aa") == "A#AA") assert (sql_utils.normaliseDBfield("a#Aa") == "A#AA")
assert (sql_utils.normaliseDBfield("2") == "2") assert (sql_utils.normaliseDBfield("2") == "2")
def test_normaliseDBFieldSet() -> None: def test_normaliseDBFieldSet() -> None:
assert (sql_utils.normaliseDBfieldSet(set()) == set()) assert (sql_utils.normaliseDBfieldSet(set()) == set())
assert (sql_utils.normaliseDBfieldSet({"aAa", "b", "c", "2"}) == {"2", "AAA", "B", "C"}) assert (sql_utils.normaliseDBfieldSet({"aAa", "b", "c", "2"}) == {"2", "AAA", "B", "C"})
def test_normaliseDBFieldList() -> None: def test_normaliseDBFieldList() -> None:
assert (sql_utils.normaliseDBfieldList([]) == []) assert (sql_utils.normaliseDBfieldList([]) == [])
assert (sql_utils.normaliseDBfieldList(["aAa", "b", "c", "2"]) == ["AAA", "B", "C", "2"]) assert (sql_utils.normaliseDBfieldList(["aAa", "b", "c", "2"]) == ["AAA", "B", "C", "2"])
def test_SqlField1() -> None: def test_SqlField1() -> None:
assert (str(sql_utils.SqlField("abc")) == "ABC") assert (str(sql_utils.SqlField("abc")) == "ABC")
def test_SqlField2() -> None: def test_SqlField2() -> None:
assert (str(sql_utils.SqlField("t.abc")) == "T.ABC") assert (str(sql_utils.SqlField("t.abc")) == "T.ABC")
def test_SqlParam() -> None: def test_SqlParam() -> None:
assert (str(sql_utils.sqlParam) == "?") assert (str(sql_utils.sqlParam) == "?")
def test_SqlDateTime() -> None: def test_SqlDateTime() -> None:
dt = datetime.datetime(year=2023, month=1, day=12, hour=9, minute=59, second=12, microsecond=2344) dt = datetime.datetime(year=2023, month=1, day=12, hour=9, minute=59, second=12, microsecond=2344)
assert (str(sql_utils.SqlDateTime(dt)) == "2023-01-12T09:59:12.002") assert (str(sql_utils.SqlDateTime(dt)) == "2023-01-12T09:59:12.002")
def test_SqlDate() -> None: def test_SqlDate() -> None:
dt = datetime.datetime(year=2023, month=1, day=12, hour=9, minute=59, second=12, microsecond=2344) dt = datetime.datetime(year=2023, month=1, day=12, hour=9, minute=59, second=12, microsecond=2344)
assert (str(sql_utils.SqlDate(dt)) == "20230112") assert (str(sql_utils.SqlDate(dt)) == "20230112")
def test_formatSqlValueString1() -> None: def test_formatSqlValueString1() -> None:
assert(sql_utils.formatSqlValueString("") == "''"); assert (sql_utils.formatSqlValueString("") == "''")
def test_formatSqlValueString2() -> None: def test_formatSqlValueString2() -> None:
assert(sql_utils.formatSqlValueString("abc") == "'abc'"); assert (sql_utils.formatSqlValueString("abc") == "'abc'")
def test_formatSqlValueString3() -> None: def test_formatSqlValueString3() -> None:
assert(sql_utils.formatSqlValueString("a b c") == "'a b c'"); assert (sql_utils.formatSqlValueString("a b c") == "'a b c'")
def test_formatSqlValueString4() -> None: def test_formatSqlValueString4() -> None:
assert(sql_utils.formatSqlValueString("a \"b\" c") == "'a \"b\" c'"); assert (sql_utils.formatSqlValueString("a \"b\" c") == "'a \"b\" c'")
def test_formatSqlValueString5() -> None: def test_formatSqlValueString5() -> None:
assert(sql_utils.formatSqlValueString("a 'b'\nc") == "'a ''b''\nc'"); assert (sql_utils.formatSqlValueString("a 'b'\nc") == "'a ''b''\nc'")
def test_formatSqlValue1() -> None: def test_formatSqlValue1() -> None:
assert(sql_utils.formatSqlValue(2) == "2"); assert (sql_utils.formatSqlValue(2) == "2")
def test_formatSqlValue2() -> None: def test_formatSqlValue2() -> None:
assert(sql_utils.formatSqlValue(2.4) == "2.4"); assert (sql_utils.formatSqlValue(2.4) == "2.4")
def test_formatSqlValue3() -> None: def test_formatSqlValue3() -> None:
assert(sql_utils.formatSqlValue("AA") == "'AA'"); assert (sql_utils.formatSqlValue("AA") == "'AA'")
def test_formatSqlValue4() -> None: def test_formatSqlValue4() -> None:
assert(sql_utils.formatSqlValue(sql_utils.SqlField("aa")) == "AA"); assert (sql_utils.formatSqlValue(sql_utils.SqlField("aa")) == "AA")
def test_formatSqlValue5() -> None: def test_formatSqlValue5() -> None:
assert(sql_utils.formatSqlValue(0) == "0"); assert (sql_utils.formatSqlValue(0) == "0")
def test_formatSqlValue6() -> None: def test_formatSqlValue6() -> None:
dt = datetime.datetime(year=2023, month=1, day=12, hour=9, minute=59, second=12, microsecond=2344) dt = datetime.datetime(year=2023, month=1, day=12, hour=9, minute=59, second=12, microsecond=2344)
assert(sql_utils.formatSqlValue(sql_utils.SqlDateTime(dt)) == "'2023-01-12T09:59:12.002'"); assert (sql_utils.formatSqlValue(sql_utils.SqlDateTime(dt)) == "'2023-01-12T09:59:12.002'")
def test_SqlConditionTrue() -> None: def test_SqlConditionTrue() -> None:
assert(str(sql_utils.SqlConditionTrue()) == "(1=1)"); assert (str(sql_utils.SqlConditionTrue()) == "(1=1)")
def test_SqlConditionFalse() -> None: def test_SqlConditionFalse() -> None:
assert(str(sql_utils.SqlConditionFalse()) == "(1=0)"); assert (str(sql_utils.SqlConditionFalse()) == "(1=0)")
def test_SqlConditionBool1() -> None: def test_SqlConditionBool1() -> None:
assert(str(sql_utils.SqlConditionBool(True)) == "(1=1)"); assert (str(sql_utils.SqlConditionBool(True)) == "(1=1)")
def test_SqlConditionBool2() -> None: def test_SqlConditionBool2() -> None:
assert(str(sql_utils.SqlConditionBool(False)) == "(1=0)"); assert (str(sql_utils.SqlConditionBool(False)) == "(1=0)")
def test_SqlConditionIsNull() -> None: def test_SqlConditionIsNull() -> None:
cond = sql_utils.SqlConditionIsNull("AA"); cond = sql_utils.SqlConditionIsNull("AA")
assert(str(cond) == "('AA' is null)"); assert (str(cond) == "('AA' is null)")
def test_SqlConditionIsNotNull() -> None: def test_SqlConditionIsNotNull() -> None:
cond = sql_utils.SqlConditionIsNotNull("AA"); cond = sql_utils.SqlConditionIsNotNull("AA")
assert(str(cond) == "('AA' is not null)"); assert (str(cond) == "('AA' is not null)")
def test_SqlConditionNot() -> None: def test_SqlConditionNot() -> None:
cond1 = sql_utils.SqlConditionIsNull("AA"); cond1 = sql_utils.SqlConditionIsNull("AA")
cond = sql_utils.SqlConditionNot(cond1); cond = sql_utils.SqlConditionNot(cond1)
assert(str(cond) == "(not ('AA' is null))"); assert (str(cond) == "(not ('AA' is null))")
def test_SqlConditionStringStartsWith() -> None: def test_SqlConditionStringStartsWith() -> None:
cond = sql_utils.SqlConditionStringStartsWith("f", "a'an") cond = sql_utils.SqlConditionStringStartsWith("f", "a'an")
assert(str(cond) == "(left(F, 4) = 'a''an')"); assert (str(cond) == "(left(F, 4) = 'a''an')")
def test_SqlConditionIn1() -> None: def test_SqlConditionIn1() -> None:
cond = sql_utils.SqlConditionIn(sql_utils.SqlField("f"), []) cond = sql_utils.SqlConditionIn(sql_utils.SqlField("f"), [])
assert(str(cond) == "(1=0)"); assert (str(cond) == "(1=0)")
def test_SqlConditionIn2() -> None: def test_SqlConditionIn2() -> None:
cond = sql_utils.SqlConditionIn(sql_utils.SqlField("f"), ["a"]) cond = sql_utils.SqlConditionIn(sql_utils.SqlField("f"), ["a"])
assert(str(cond) == "(F = 'a')"); assert (str(cond) == "(F = 'a')")
def test_SqlConditionIn3() -> None: def test_SqlConditionIn3() -> None:
cond = sql_utils.SqlConditionIn(sql_utils.SqlField("f"), ["a", "a'A", "b", "c"]) cond = sql_utils.SqlConditionIn(sql_utils.SqlField("f"), ["a", "a'A", "b", "c"])
assert(str(cond) == "(F in ('a', 'a''A', 'b', 'c'))"); assert (str(cond) == "(F in ('a', 'a''A', 'b', 'c'))")
def test_SqlConditionStringNotEmpty1() -> None: def test_SqlConditionStringNotEmpty1() -> None:
cond = sql_utils.SqlConditionFieldStringNotEmpty("f") cond = sql_utils.SqlConditionFieldStringNotEmpty("f")
assert(str(cond) == "(F is not null and F != '')"); assert (str(cond) == "(F is not null and F != '')")
def test_SqlConditionEq1() -> None: def test_SqlConditionEq1() -> None:
cond = sql_utils.SqlConditionEq("f1", None) cond = sql_utils.SqlConditionEq("f1", None)
assert(str(cond) == "('f1' is null)"); assert (str(cond) == "('f1' is null)")
def test_SqlConditionEq2() -> None: def test_SqlConditionEq2() -> None:
cond = sql_utils.SqlConditionEq(None, "f1") cond = sql_utils.SqlConditionEq(None, "f1")
assert(str(cond) == "('f1' is null)"); assert (str(cond) == "('f1' is null)")
def test_SqlConditionEq3() -> None: def test_SqlConditionEq3() -> None:
cond = sql_utils.SqlConditionEq(sql_utils.SqlField("f1"), sql_utils.SqlField("f2")) cond = sql_utils.SqlConditionEq(sql_utils.SqlField("f1"), sql_utils.SqlField("f2"))
assert(str(cond) == "(F1 = F2)"); assert (str(cond) == "(F1 = F2)")
def test_SqlConditionEq4() -> None: def test_SqlConditionEq4() -> None:
cond = sql_utils.SqlConditionEq(sql_utils.SqlField("f1"), "aa'a") cond = sql_utils.SqlConditionEq(sql_utils.SqlField("f1"), "aa'a")
assert(str(cond) == "(F1 = 'aa''a')"); assert (str(cond) == "(F1 = 'aa''a')")
def test_SqlConditionEq5() -> None: def test_SqlConditionEq5() -> None:
cond = sql_utils.SqlConditionEq(sql_utils.SqlField("f1"), 2) cond = sql_utils.SqlConditionEq(sql_utils.SqlField("f1"), 2)
assert(str(cond) == "(F1 = 2)"); assert (str(cond) == "(F1 = 2)")
def test_SqlConditionEq6() -> None: def test_SqlConditionEq6() -> None:
cond = sql_utils.SqlConditionEq(sql_utils.SqlField("f1"), True) cond = sql_utils.SqlConditionEq(sql_utils.SqlField("f1"), True)
assert(str(cond) == "(F1 = 1)"); assert (str(cond) == "(F1 = 1)")
def test_SqlConditionEq7() -> None: def test_SqlConditionEq7() -> None:
cond = sql_utils.SqlConditionEq(sql_utils.SqlField("f1"), False) cond = sql_utils.SqlConditionEq(sql_utils.SqlField("f1"), False)
assert(str(cond) == "(F1 = 0 OR F1 is null)"); assert (str(cond) == "(F1 = 0 OR F1 is null)")
def test_SqlConditionEq8() -> None: def test_SqlConditionEq8() -> None:
cond = sql_utils.SqlConditionEq(True, sql_utils.SqlField("f1")) cond = sql_utils.SqlConditionEq(True, sql_utils.SqlField("f1"))
assert(str(cond) == "(F1 = 1)"); assert (str(cond) == "(F1 = 1)")
def test_SqlConditionEq9() -> None: def test_SqlConditionEq9() -> None:
cond = sql_utils.SqlConditionEq(False, sql_utils.SqlField("f1")) cond = sql_utils.SqlConditionEq(False, sql_utils.SqlField("f1"))
assert(str(cond) == "(F1 = 0 OR F1 is null)"); assert (str(cond) == "(F1 = 0 OR F1 is null)")
def test_SqlConditionEq10() -> None: def test_SqlConditionEq10() -> None:
cond = sql_utils.SqlConditionEq(False, True) cond = sql_utils.SqlConditionEq(False, True)
assert(str(cond) == "(1=0)"); assert (str(cond) == "(1=0)")
def test_SqlConditionEq11() -> None: def test_SqlConditionEq11() -> None:
cond = sql_utils.SqlConditionEq(True, True) cond = sql_utils.SqlConditionEq(True, True)
assert(str(cond) == "(1=1)"); assert (str(cond) == "(1=1)")
def test_SqlConditionFieldEq1() -> None: def test_SqlConditionFieldEq1() -> None:
cond = sql_utils.SqlConditionFieldEq("f1", None) cond = sql_utils.SqlConditionFieldEq("f1", None)
assert(str(cond) == "(F1 is null)"); assert (str(cond) == "(F1 is null)")
def test_SqlConditionFieldEq2() -> None: def test_SqlConditionFieldEq2() -> None:
cond = sql_utils.SqlConditionFieldEq("f1", sql_utils.SqlField("f2")) cond = sql_utils.SqlConditionFieldEq("f1", sql_utils.SqlField("f2"))
assert(str(cond) == "(F1 = F2)"); assert (str(cond) == "(F1 = F2)")
def test_SqlConditionFieldEq3() -> None: def test_SqlConditionFieldEq3() -> None:
cond = sql_utils.SqlConditionFieldEq("f1", "aa'a") cond = sql_utils.SqlConditionFieldEq("f1", "aa'a")
assert(str(cond) == "(F1 = 'aa''a')"); assert (str(cond) == "(F1 = 'aa''a')")
def test_SqlConditionFieldEq4() -> None: def test_SqlConditionFieldEq4() -> None:
cond = sql_utils.SqlConditionFieldEq("f1", 2) cond = sql_utils.SqlConditionFieldEq("f1", 2)
assert(str(cond) == "(F1 = 2)"); assert (str(cond) == "(F1 = 2)")
def test_SqlConditionFieldEq5() -> None: def test_SqlConditionFieldEq5() -> None:
cond = sql_utils.SqlConditionFieldEq("f1", sql_utils.sqlParam) cond = sql_utils.SqlConditionFieldEq("f1", sql_utils.sqlParam)
assert(str(cond) == "(F1 = ?)"); assert (str(cond) == "(F1 = ?)")
def test_SqlConditionLt1() -> None: def test_SqlConditionLt1() -> None:
cond = sql_utils.SqlConditionLt(sql_utils.SqlField("f"), sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12))) cond = sql_utils.SqlConditionLt(sql_utils.SqlField("f"), sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12)))
assert(str(cond) == "(F < '20221212')"); assert (str(cond) == "(F < '20221212')")
def test_SqlConditionLt2() -> None: def test_SqlConditionLt2() -> None:
cond = sql_utils.SqlConditionLt(2, sql_utils.SqlField("f")) cond = sql_utils.SqlConditionLt(2, sql_utils.SqlField("f"))
assert(str(cond) == "(2 < F)"); assert (str(cond) == "(2 < F)")
def test_SqlConditionGt1() -> None: def test_SqlConditionGt1() -> None:
cond = sql_utils.SqlConditionGt(sql_utils.SqlField("f"), sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12))) cond = sql_utils.SqlConditionGt(sql_utils.SqlField("f"), sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12)))
assert(str(cond) == "(F > '20221212')"); assert (str(cond) == "(F > '20221212')")
def test_SqlConditionGt2() -> None: def test_SqlConditionGt2() -> None:
cond = sql_utils.SqlConditionGt(2, sql_utils.SqlField("f")) cond = sql_utils.SqlConditionGt(2, sql_utils.SqlField("f"))
assert(str(cond) == "(2 > F)"); assert (str(cond) == "(2 > F)")
def test_SqlConditionLe1() -> None: def test_SqlConditionLe1() -> None:
cond = sql_utils.SqlConditionLe(sql_utils.SqlField("f"), sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12))) cond = sql_utils.SqlConditionLe(sql_utils.SqlField("f"), sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12)))
assert(str(cond) == "(F <= '20221212')"); assert (str(cond) == "(F <= '20221212')")
def test_SqlConditionLe2() -> None: def test_SqlConditionLe2() -> None:
cond = sql_utils.SqlConditionLe(2, sql_utils.SqlField("f")) cond = sql_utils.SqlConditionLe(2, sql_utils.SqlField("f"))
assert(str(cond) == "(2 <= F)"); assert (str(cond) == "(2 <= F)")
def test_SqlConditionGe1() -> None: def test_SqlConditionGe1() -> None:
cond = sql_utils.SqlConditionGe(sql_utils.SqlField("f"), sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12))) cond = sql_utils.SqlConditionGe(sql_utils.SqlField("f"), sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12)))
assert(str(cond) == "(F >= '20221212')"); assert (str(cond) == "(F >= '20221212')")
def test_SqlConditionGe2() -> None: def test_SqlConditionGe2() -> None:
cond = sql_utils.SqlConditionGe(2, sql_utils.SqlField("f")) cond = sql_utils.SqlConditionGe(2, sql_utils.SqlField("f"))
assert(str(cond) == "(2 >= F)"); assert (str(cond) == "(2 >= F)")
def test_SqlConditionFieldLt1() -> None: def test_SqlConditionFieldLt1() -> None:
cond = sql_utils.SqlConditionFieldLt("f", sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12))) cond = sql_utils.SqlConditionFieldLt("f", sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12)))
assert(str(cond) == "(F < '20221212')"); assert (str(cond) == "(F < '20221212')")
def test_SqlConditionFieldLe1() -> None: def test_SqlConditionFieldLe1() -> None:
cond = sql_utils.SqlConditionFieldLe("f", sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12))) cond = sql_utils.SqlConditionFieldLe("f", sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12)))
assert(str(cond) == "(F <= '20221212')"); assert (str(cond) == "(F <= '20221212')")
def test_SqlConditionFieldGt1() -> None: def test_SqlConditionFieldGt1() -> None:
cond = sql_utils.SqlConditionFieldGt("f", sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12))) cond = sql_utils.SqlConditionFieldGt("f", sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12)))
assert(str(cond) == "(F > '20221212')"); assert (str(cond) == "(F > '20221212')")
def test_SqlConditionFieldGe1() -> None: def test_SqlConditionFieldGe1() -> None:
cond = sql_utils.SqlConditionFieldGe("f", sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12))) cond = sql_utils.SqlConditionFieldGe("f", sql_utils.SqlDate(datetime.date(year=2022, month=12, day=12)))
assert(str(cond) == "(F >= '20221212')"); assert (str(cond) == "(F >= '20221212')")
def test_SqlConditionAnd1() -> None: def test_SqlConditionAnd1() -> None:
conj = sql_utils.SqlConditionAnd(); conj = sql_utils.SqlConditionAnd()
assert(str(conj) == "(1=1)"); assert (str(conj) == "(1=1)")
def test_SqlConditionAnd2() -> None: def test_SqlConditionAnd2() -> None:
cond1 = sql_utils.SqlConditionPrepared("cond1"); cond1 = sql_utils.SqlConditionPrepared("cond1")
conj = sql_utils.SqlConditionAnd(); conj = sql_utils.SqlConditionAnd()
conj.addCondition(cond1) conj.addCondition(cond1)
assert(str(conj) == "cond1"); assert (str(conj) == "cond1")
def test_SqlConditionAnd3() -> None: def test_SqlConditionAnd3() -> None:
cond1 = sql_utils.SqlConditionPrepared("cond1"); cond1 = sql_utils.SqlConditionPrepared("cond1")
cond2 = sql_utils.SqlConditionPrepared("cond2"); cond2 = sql_utils.SqlConditionPrepared("cond2")
conj = sql_utils.SqlConditionAnd(); conj = sql_utils.SqlConditionAnd()
conj.addCondition(cond1) conj.addCondition(cond1)
conj.addCondition(cond2) conj.addCondition(cond2)
assert(str(conj) == "(cond1 AND cond2)"); assert (str(conj) == "(cond1 AND cond2)")
def test_SqlConditionAnd4() -> None: def test_SqlConditionAnd4() -> None:
cond1 = sql_utils.SqlConditionPrepared("cond1"); cond1 = sql_utils.SqlConditionPrepared("cond1")
cond2 = sql_utils.SqlConditionPrepared("cond2"); cond2 = sql_utils.SqlConditionPrepared("cond2")
cond3 = sql_utils.SqlConditionPrepared("cond3"); cond3 = sql_utils.SqlConditionPrepared("cond3")
conj = sql_utils.SqlConditionAnd(); conj = sql_utils.SqlConditionAnd()
conj.addCondition(cond1) conj.addCondition(cond1)
conj.addCondition(cond2) conj.addCondition(cond2)
conj.addCondition(cond3) conj.addCondition(cond3)
assert(str(conj) == "(cond1 AND cond2 AND cond3)"); assert (str(conj) == "(cond1 AND cond2 AND cond3)")
def test_SqlConditionOr1() -> None: def test_SqlConditionOr1() -> None:
conj = sql_utils.SqlConditionOr(); conj = sql_utils.SqlConditionOr()
assert(str(conj) == "(1=0)"); assert (str(conj) == "(1=0)")
def test_SqlConditionOr2() -> None: def test_SqlConditionOr2() -> None:
cond1 = sql_utils.SqlConditionPrepared("cond1"); cond1 = sql_utils.SqlConditionPrepared("cond1")
conj = sql_utils.SqlConditionOr(); conj = sql_utils.SqlConditionOr()
conj.addCondition(cond1) conj.addCondition(cond1)
assert(str(conj) == "cond1"); assert (str(conj) == "cond1")
def test_SqlConditionOr3() -> None: def test_SqlConditionOr3() -> None:
cond1 = sql_utils.SqlConditionPrepared("cond1"); cond1 = sql_utils.SqlConditionPrepared("cond1")
cond2 = sql_utils.SqlConditionPrepared("cond2"); cond2 = sql_utils.SqlConditionPrepared("cond2")
conj = sql_utils.SqlConditionOr(); conj = sql_utils.SqlConditionOr()
conj.addCondition(cond1) conj.addCondition(cond1)
conj.addCondition(cond2) conj.addCondition(cond2)
assert(str(conj) == "(cond1 OR cond2)"); assert (str(conj) == "(cond1 OR cond2)")
def test_SqlConditionOr4() -> None: def test_SqlConditionOr4() -> None:
cond1 = sql_utils.SqlConditionPrepared("cond1"); cond1 = sql_utils.SqlConditionPrepared("cond1")
cond2 = sql_utils.SqlConditionPrepared("cond2"); cond2 = sql_utils.SqlConditionPrepared("cond2")
cond3 = sql_utils.SqlConditionPrepared("cond3"); cond3 = sql_utils.SqlConditionPrepared("cond3")
conj = sql_utils.SqlConditionOr(); conj = sql_utils.SqlConditionOr()
conj.addCondition(cond1) conj.addCondition(cond1)
conj.addCondition(cond2) conj.addCondition(cond2)
conj.addCondition(cond3) conj.addCondition(cond3)
assert(str(conj) == "(cond1 OR cond2 OR cond3)"); assert (str(conj) == "(cond1 OR cond2 OR cond3)")
def test_SqlStatementSelect1() -> None: def test_SqlStatementSelect1() -> None:
sql = sql_utils.SqlStatementSelect("tabelle t") sql = sql_utils.SqlStatementSelect("tabelle t")
@ -323,6 +389,7 @@ def test_SqlStatementSelect2() -> None:
sql.addJoin("left join t3 on cond3") sql.addJoin("left join t3 on cond3")
assert (str(sql) == "SELECT * FROM t1 left join t2 on cond2 left join t3 on cond3") assert (str(sql) == "SELECT * FROM t1 left join t2 on cond2 left join t3 on cond3")
def test_SqlStatementSelect4() -> None: def test_SqlStatementSelect4() -> None:
sql = sql_utils.SqlStatementSelect("t") sql = sql_utils.SqlStatementSelect("t")
sql.where.addCondition("cond1") sql.where.addCondition("cond1")
@ -331,9 +398,10 @@ def test_SqlStatementSelect4() -> None:
sql.where.addCondition("cond2") sql.where.addCondition("cond2")
assert (str(sql) == "SELECT * FROM t WHERE ((cond1) AND (cond2))") assert (str(sql) == "SELECT * FROM t WHERE ((cond1) AND (cond2))")
def test_SqlStatementSelect5() -> None: def test_SqlStatementSelect5() -> None:
sql = sql_utils.SqlStatementSelect("t") sql = sql_utils.SqlStatementSelect("t")
cond = sql_utils.SqlConditionOr(); cond = sql_utils.SqlConditionOr()
sql.where.addCondition(cond) sql.where.addCondition(cond)
cond.addCondition("cond1") cond.addCondition("cond1")
assert (str(sql) == "SELECT * FROM t WHERE (cond1)") assert (str(sql) == "SELECT * FROM t WHERE (cond1)")
@ -341,9 +409,10 @@ def test_SqlStatementSelect5() -> None:
cond.addCondition("cond2") cond.addCondition("cond2")
assert (str(sql) == "SELECT * FROM t WHERE ((cond1) OR (cond2))") assert (str(sql) == "SELECT * FROM t WHERE ((cond1) OR (cond2))")
def test_SqlStatementSelect6() -> None: def test_SqlStatementSelect6() -> None:
sql = sql_utils.SqlStatementSelect("t") sql = sql_utils.SqlStatementSelect("t")
sql.where = sql_utils.SqlConditionOr(); sql.where = sql_utils.SqlConditionOr()
sql.where.addCondition("cond1") sql.where.addCondition("cond1")
assert (str(sql) == "SELECT * FROM t WHERE (cond1)") assert (str(sql) == "SELECT * FROM t WHERE (cond1)")