Compare commits
3 Commits
77e472e016
...
b05b5de039
Author | SHA1 | Date | |
---|---|---|---|
b05b5de039 | |||
3566c9ba3e | |||
d88469e711 |
43
.github/workflows/python-package.yml
vendored
Normal file
43
.github/workflows/python-package.yml
vendored
Normal 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 .
|
9
Changelog.md
Normal file
9
Changelog.md
Normal file
@ -0,0 +1,9 @@
|
||||
# Changelog
|
||||
|
||||
## 06.05.2023 v1.0.1
|
||||
- Code-Cleanup mit Hilfe von flake8
|
||||
- Bugfix: neue Python 3.10 Syntax entfernt
|
||||
- kleinere Verbesserungen an Doku
|
||||
|
||||
## 04.05.2023 v1.0.0
|
||||
erste veröffentlichte Version
|
@ -58,7 +58,7 @@ der für die Umgebung korrekte Mandant automatisch verwendet wird.
|
||||
Anbindung eigener Tools
|
||||
-----------------------
|
||||
|
||||
Ursprünglich wurde `PyAPplus64` für die Anbindung einer APplus-Anpassung geschrieben. Dieses ist
|
||||
Ursprünglich wurde `PyAPplus64` für die Anbindung einer APplus-Anpassung geschrieben. Diese Anpassung ist
|
||||
als Windows-Service auf einem eigenen Rechner installiert und überwacht dort ein bestimmtes Verzeichnis.
|
||||
Bei Änderungen an Dateien in diesem Verzeichnis (Hinzufügen, Ändern, Löschen) werden die Dateien verarbeitet
|
||||
und die Ergebnisse an APplus gemeldet. Dafür werden DB-Operationen aber auch SOAP-Calls benutzt.
|
||||
|
@ -14,6 +14,8 @@ sys.path.append('../src/')
|
||||
project = 'PyAPplus64'
|
||||
copyright = '2023, Thomas Tuerk'
|
||||
author = 'Thomas Tuerk'
|
||||
version = '1.0.1'
|
||||
release = '1.0.1'
|
||||
|
||||
# -- General configuration ---------------------------------------------------
|
||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
|
||||
|
@ -17,7 +17,7 @@ das Deploy-, das Test- und das Prod-System. Ein Beispiel ist im Unterverzeichnis
|
||||
:linenos:
|
||||
|
||||
Damit nicht in jedem Script immer wieder neu die Konfig-Dateien ausgewählt werden müssen, werden die Konfigs für
|
||||
das Prod-, Test- und Deploy-System in ``examples/applus_configs.py`` hinterlegt. Diese wird in allen Scripten importiert,
|
||||
das Prod-, Test- und Deploy-System in ``examples/applus_configs.py`` hinterlegt. Diese Datei wird in allen Scripten importiert,
|
||||
so dass das Config-Verzeichnis und die darin enthaltenen Configs einfach zur Verfügung stehen.
|
||||
|
||||
.. literalinclude:: ../../examples/applus_configs.py
|
||||
@ -26,6 +26,15 @@ so dass das Config-Verzeichnis und die darin enthaltenen Configs einfach zur Ver
|
||||
:linenos:
|
||||
|
||||
|
||||
``read_settings.py``
|
||||
-----------------------
|
||||
Einfaches Beispiel für Auslesen der SysConf und bestimmter Einstellungen.
|
||||
|
||||
.. literalinclude:: ../../examples/read_settings.py
|
||||
:language: python
|
||||
:lines: 9-
|
||||
:linenos:
|
||||
|
||||
``check_dokumente.py``
|
||||
-----------------------
|
||||
Einfaches Beispiel für lesenden und schreibenden Zugriff auf APplus Datenbank.
|
||||
|
@ -10,6 +10,7 @@ import PyAPplus64
|
||||
import applus_configs
|
||||
import pathlib
|
||||
|
||||
|
||||
def main(confFile: pathlib.Path, outfile: str) -> None:
|
||||
server = PyAPplus64.applus.applusFromConfigFile(confFile)
|
||||
|
||||
|
@ -14,4 +14,3 @@ configdir = basedir.joinpath("config")
|
||||
serverConfYamlDeploy = configdir.joinpath("applus-server-deploy.yaml")
|
||||
serverConfYamlTest = configdir.joinpath("applus-server-test.yaml")
|
||||
serverConfYamlProd = configdir.joinpath("applus-server-prod.yaml")
|
||||
|
||||
|
@ -9,23 +9,29 @@
|
||||
import pathlib
|
||||
import PyAPplus64
|
||||
import applus_configs
|
||||
from typing import Optional
|
||||
|
||||
def main(confFile : pathlib.Path, docDir:str, updateDB:bool) -> None:
|
||||
|
||||
def main(confFile: pathlib.Path, updateDB: bool, docDir: Optional[str] = None) -> None:
|
||||
server = PyAPplus64.applus.applusFromConfigFile(confFile)
|
||||
|
||||
sql = PyAPplus64.sql_utils.SqlStatementSelect("ARTIKEL");
|
||||
sql.addFields("ID", "ARTIKEL", "DOCUMENTS");
|
||||
sql.where.addConditionFieldStringNotEmpty("DOCUMENTS");
|
||||
if docDir is None:
|
||||
docDir = str(server.scripttool.getInstallPathWebServer().joinpath("DocLib"))
|
||||
|
||||
sql = PyAPplus64.sql_utils.SqlStatementSelect("ARTIKEL")
|
||||
sql.addFields("ID", "ARTIKEL", "DOCUMENTS")
|
||||
sql.where.addConditionFieldStringNotEmpty("DOCUMENTS")
|
||||
|
||||
for row in server.dbQueryAll(sql):
|
||||
doc = pathlib.Path(docDir + row.DOCUMENTS);
|
||||
doc = pathlib.Path(docDir + row.DOCUMENTS)
|
||||
if not doc.exists():
|
||||
print("Bild '{}' für Artikel '{}' nicht gefunden".format(doc, row.ARTIKEL))
|
||||
|
||||
if updateDB:
|
||||
upd = server.mkUseXMLRowUpdate("ARTIKEL", row.ID);
|
||||
upd.addField("DOCUMENTS", None);
|
||||
upd.update();
|
||||
upd = server.mkUseXMLRowUpdate("ARTIKEL", row.ID)
|
||||
upd.addField("DOCUMENTS", None)
|
||||
upd.update()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(applus_configs.serverConfYamlTest, "somedir\\WebServer\\DocLib", False)
|
||||
main(applus_configs.serverConfYamlTest, False)
|
||||
|
@ -24,18 +24,19 @@ import PyAPplus64
|
||||
import applus_configs
|
||||
import logging
|
||||
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 = PyAPplus64.applus.applusFromConfigFile(confFile)
|
||||
|
||||
# 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
|
||||
dArtYaml = yaml.dump(dArt);
|
||||
print(dArtYaml);
|
||||
dArtYaml = yaml.dump(dArt)
|
||||
print(dArtYaml)
|
||||
dArt2 = yaml.load(dArtYaml, Loader=yaml.UnsafeLoader)
|
||||
|
||||
# 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):
|
||||
dArt.setFields({"artikel": artikelNeu})
|
||||
res = dArt.insert(server);
|
||||
print(res);
|
||||
res = dArt.insert(server)
|
||||
print(res)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@ -56,4 +57,3 @@ if __name__ == "__main__":
|
||||
# logger.setLevel(logging.ERROR)
|
||||
|
||||
main(applus_configs.serverConfYamlTest, "my-artikel", artikelNeu="my-artikel-copy")
|
||||
|
||||
|
@ -13,12 +13,13 @@ import PyAPplus64
|
||||
import applus_configs
|
||||
import pandas as pd # type: ignore
|
||||
import pathlib
|
||||
from typing import *
|
||||
from typing import Tuple, Union, Optional
|
||||
|
||||
|
||||
def ladeAlleWerkstattauftragMengenabweichungen(
|
||||
server: PyAPplus64.APplusServer,
|
||||
cond:PyAPplus64.SqlCondition|str|None=None) -> pd.DataFrame:
|
||||
sql = PyAPplus64.sql_utils.SqlStatementSelect("WAUFTRAG w");
|
||||
cond: Union[PyAPplus64.SqlCondition, str, None] = None) -> pd.DataFrame:
|
||||
sql = PyAPplus64.sql_utils.SqlStatementSelect("WAUFTRAG w")
|
||||
sql.addLeftJoin("personal p", "w.UPDUSER = p.PERSONAL")
|
||||
|
||||
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(cond)
|
||||
sql.order = "w.UPDDATE"
|
||||
dfOrg = PyAPplus64.pandas.pandasReadSql(server, sql);
|
||||
dfOrg = PyAPplus64.pandas.pandasReadSql(server, sql)
|
||||
|
||||
# Add Links
|
||||
df = dfOrg.copy();
|
||||
df = df.drop(columns=["ID"]);
|
||||
df = dfOrg.copy()
|
||||
df = df.drop(columns=["ID"])
|
||||
# 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: server.makeWebLinkWauftrag(
|
||||
bauftrag=r.BAUFTRAG, accessid=r.ID))
|
||||
df['BAUFTRAG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg,
|
||||
df['BAUFTRAG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
|
||||
dfOrg,
|
||||
lambda r: r.BAUFTRAG,
|
||||
lambda r: server.makeWebLinkBauftrag(bauftrag=r.BAUFTRAG))
|
||||
|
||||
@ -57,15 +60,15 @@ def ladeAlleWerkstattauftragMengenabweichungen(
|
||||
"UPDDATE": "geändert am",
|
||||
"UPDNAME": "geändert von"
|
||||
}
|
||||
df.rename(columns=colNames, inplace=True);
|
||||
df.rename(columns=colNames, inplace=True)
|
||||
|
||||
return df
|
||||
|
||||
|
||||
def ladeAlleWerkstattauftragPosMengenabweichungen(
|
||||
server: PyAPplus64.APplusServer,
|
||||
cond:PyAPplus64.SqlCondition|str|None=None) -> pd.DataFrame:
|
||||
sql = PyAPplus64.sql_utils.SqlStatementSelect("WAUFTRAGPOS w");
|
||||
cond: Union[PyAPplus64.SqlCondition, str, None] = None) -> pd.DataFrame:
|
||||
sql = PyAPplus64.sql_utils.SqlStatementSelect("WAUFTRAGPOS w")
|
||||
sql.addLeftJoin("personal p", "w.UPDUSER = p.PERSONAL")
|
||||
|
||||
sql.addFieldsTable("w", "ID", "BAUFTRAG", "POSITION", "AG")
|
||||
@ -78,19 +81,22 @@ def ladeAlleWerkstattauftragPosMengenabweichungen(
|
||||
sql.where.addCondition(cond)
|
||||
sql.order = "w.UPDDATE"
|
||||
|
||||
dfOrg = PyAPplus64.pandas.pandasReadSql(server, sql);
|
||||
dfOrg = PyAPplus64.pandas.pandasReadSql(server, sql)
|
||||
|
||||
# Add Links
|
||||
df = dfOrg.copy();
|
||||
df = df.drop(columns=["ID"]);
|
||||
df['POSITION'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg,
|
||||
df = dfOrg.copy()
|
||||
df = df.drop(columns=["ID"])
|
||||
df['POSITION'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
|
||||
dfOrg,
|
||||
lambda r: r.POSITION,
|
||||
lambda r: server.makeWebLinkWauftrag(
|
||||
bauftrag=r.BAUFTRAG, accessid=r.ID))
|
||||
df['BAUFTRAG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(dfOrg,
|
||||
df['BAUFTRAG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
|
||||
dfOrg,
|
||||
lambda r: 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: server.makeWebLinkWauftragPos(
|
||||
bauftrag=r.BAUFTRAG, position=r.POSITION, accessid=r.ID))
|
||||
@ -111,11 +117,12 @@ def ladeAlleWerkstattauftragPosMengenabweichungen(
|
||||
"UPDDATE": "geändert am",
|
||||
"UPDNAME": "geändert von"
|
||||
}
|
||||
df.rename(columns=colNames, inplace=True);
|
||||
df.rename(columns=colNames, inplace=True)
|
||||
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 month is None:
|
||||
return PyAPplus64.sql_utils.SqlConditionDateTimeFieldInYear(field, year)
|
||||
@ -124,52 +131,59 @@ def computeInYearMonthCond(field : str, year:int|None=None,
|
||||
else:
|
||||
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:
|
||||
return 'mengenabweichungen-all.xlsx';
|
||||
return 'mengenabweichungen-all.xlsx'
|
||||
else:
|
||||
if month is None:
|
||||
return 'mengenabweichungen-{:04d}.xlsx'.format(year);
|
||||
return 'mengenabweichungen-{:04d}.xlsx'.format(year)
|
||||
else:
|
||||
return 'mengenabweichungen-{:04d}-{:02d}.xlsx'.format(year, month);
|
||||
return 'mengenabweichungen-{:04d}-{:02d}.xlsx'.format(year, month)
|
||||
|
||||
|
||||
def _exportInternal(server: PyAPplus64.APplusServer, fn: str,
|
||||
cond: Union[PyAPplus64.SqlCondition, str, None]) -> int:
|
||||
df1 = ladeAlleWerkstattauftragMengenabweichungen(server, cond)
|
||||
df2 = ladeAlleWerkstattauftragPosMengenabweichungen(server, cond)
|
||||
print ("erzeuge " + fn);
|
||||
print("erzeuge " + fn)
|
||||
PyAPplus64.pandas.exportToExcel(fn, [(df1, "WAuftrag"), (df2, "WAuftrag-Pos")], addTable=True)
|
||||
return len(df1.index) + len(df2.index)
|
||||
|
||||
|
||||
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)
|
||||
return _exportInternal(server, fn, cond)
|
||||
|
||||
|
||||
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)
|
||||
fn = computeFileName(year=year, month=month)
|
||||
return _exportInternal(server, fn, cond)
|
||||
|
||||
|
||||
def computePreviousMonthYear(cyear: int, cmonth: int) -> Tuple[int, int]:
|
||||
if cmonth == 1:
|
||||
return (cyear-1, 12)
|
||||
else:
|
||||
return (cyear, cmonth-1);
|
||||
return (cyear, cmonth-1)
|
||||
|
||||
|
||||
def computeNextMonthYear(cyear: int, cmonth: int) -> Tuple[int, int]:
|
||||
if cmonth == 12:
|
||||
return (cyear+1, 1)
|
||||
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)
|
||||
|
||||
now = datetime.date.today()
|
||||
(cmonth, cyear) = (now.month, now.year)
|
||||
(pyear, pmonth) = computePreviousMonthYear(cyear, cmonth);
|
||||
(pyear, pmonth) = computePreviousMonthYear(cyear, cmonth)
|
||||
|
||||
# Ausgaben
|
||||
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() # alles
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(applus_configs.serverConfYamlTest)
|
||||
|
@ -12,9 +12,10 @@ import datetime
|
||||
import PyAPplus64
|
||||
import applus_configs
|
||||
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 == '':
|
||||
return (None, True)
|
||||
else:
|
||||
@ -24,12 +25,15 @@ def parseDate (dateS:str) -> Tuple[datetime.datetime|None, bool]:
|
||||
sg.popup_error("Fehler beim Parsen des Datums '{}'".format(dateS))
|
||||
return (None, False)
|
||||
|
||||
|
||||
def createFile(server: PyAPplus64.APplusServer, fileS: str, vonS: str, bisS: str) -> None:
|
||||
(von, vonOK) = parseDate(vonS)
|
||||
if not vonOK: return
|
||||
if not vonOK:
|
||||
return
|
||||
|
||||
(bis, bisOK) = parseDate(bisS)
|
||||
if not bisOK: return
|
||||
if not bisOK:
|
||||
return
|
||||
|
||||
if (fileS is None) or fileS == '':
|
||||
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))
|
||||
|
||||
|
||||
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)
|
||||
|
||||
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,
|
||||
target="Bis", format='%d.%m.%Y')],
|
||||
[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'),),
|
||||
default_extension=".xlsx")],
|
||||
[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)
|
||||
now = datetime.date.today()
|
||||
(cmonth, cyear) = (now.month, now.year)
|
||||
(pyear, pmonth) = mengenabweichung.computePreviousMonthYear(cyear, cmonth);
|
||||
(nyear, nmonth) = mengenabweichung.computeNextMonthYear(cyear, cmonth);
|
||||
(pyear, pmonth) = mengenabweichung.computePreviousMonthYear(cyear, cmonth)
|
||||
(nyear, nmonth) = mengenabweichung.computeNextMonthYear(cyear, cmonth)
|
||||
|
||||
while True:
|
||||
event, values = window.read()
|
||||
if event == sg.WIN_CLOSED or event == 'Beenden':
|
||||
break
|
||||
if event == 'Aktueller Monat':
|
||||
window['Von'].update(value="01.{:02d}.{:04d}".format(cmonth, cyear));
|
||||
window['Bis'].update(value="01.{:02d}.{:04d}".format(nmonth, nyear));
|
||||
window['Von'].update(value="01.{:02d}.{:04d}".format(cmonth, cyear))
|
||||
window['Bis'].update(value="01.{:02d}.{:04d}".format(nmonth, nyear))
|
||||
if event == 'Letzter Monat':
|
||||
window['Von'].update(value="01.{:02d}.{:04d}".format(pmonth, pyear));
|
||||
window['Bis'].update(value="01.{:02d}.{:04d}".format(cmonth, cyear));
|
||||
window['Von'].update(value="01.{:02d}.{:04d}".format(pmonth, pyear))
|
||||
window['Bis'].update(value="01.{:02d}.{:04d}".format(cmonth, cyear))
|
||||
if event == 'Aktuelles Jahr':
|
||||
window['Von'].update(value="01.01.{:04d}".format(cyear));
|
||||
window['Bis'].update(value="01.01.{:04d}".format(cyear+1));
|
||||
window['Von'].update(value="01.01.{:04d}".format(cyear))
|
||||
window['Bis'].update(value="01.01.{:04d}".format(cyear+1))
|
||||
if event == 'Letztes Jahr':
|
||||
window['Von'].update(value="01.01.{:04d}".format(cyear-1));
|
||||
window['Bis'].update(value="01.01.{:04d}".format(cyear));
|
||||
window['Von'].update(value="01.01.{:04d}".format(cyear-1))
|
||||
window['Bis'].update(value="01.01.{:04d}".format(cyear))
|
||||
if event == 'Speichern':
|
||||
try:
|
||||
createFile(server, values.get('File', None),
|
||||
values.get('Von', None), values.get('Bis', None))
|
||||
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()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(applus_configs.serverConfYamlProd)
|
||||
|
53
examples/read_settings.py
Normal file
53
examples/read_settings.py
Normal file
@ -0,0 +1,53 @@
|
||||
# Copyright (c) 2023 Thomas Tuerk (kontakt@thomas-tuerk.de)
|
||||
#
|
||||
# This file is part of PyAPplus64 (see https://www.thomas-tuerk.de/de/pyapplus64).
|
||||
#
|
||||
# Use of this source code is governed by an MIT-style
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
|
||||
# Einfaches Script, das verschiedene Werte des Servers ausliest.
|
||||
# Dies sind SysConfig-Einstellungen, aber auch der aktuelle Mandant,
|
||||
# Systemnamen, ...
|
||||
|
||||
import pathlib
|
||||
import PyAPplus64
|
||||
import applus_configs
|
||||
from typing import Optional, Union
|
||||
|
||||
|
||||
def main(confFile: Union[str, pathlib.Path], user: Optional[str] = None, env: Optional[str] = None) -> None:
|
||||
server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env)
|
||||
|
||||
print("\n\nSysConf Lookups:")
|
||||
|
||||
print(" Default Auftragsart:", server.sysconf.getString("STAMM", "DEFAULTAUFTRAGSART"))
|
||||
print(" Auftragsarten:")
|
||||
arten = server.sysconf.getList("STAMM", "AUFTRAGSART", sep='\n')
|
||||
if not arten:
|
||||
arten = []
|
||||
for a in arten:
|
||||
print(" - " + a)
|
||||
|
||||
print(" Firmen-Nr. automatisch vergeben:", server.sysconf.getBoolean("STAMM", "FIRMAAUTOMATIK"))
|
||||
print(" Anzahl Artikelstellen:", server.sysconf.getInt("STAMM", "ARTKLASSIFNRLAENGE"))
|
||||
|
||||
print("\n\nScriptTool:")
|
||||
|
||||
print(" CurrentDate:", server.scripttool.getCurrentDate())
|
||||
print(" CurrentTime:", server.scripttool.getCurrentTime())
|
||||
print(" CurrentDateTime:", server.scripttool.getCurrentDateTime())
|
||||
print(" LoginName:", server.scripttool.getLoginName())
|
||||
print(" UserName:", server.scripttool.getUserName())
|
||||
print(" UserFullName:", server.scripttool.getUserFullName())
|
||||
print(" SystemName:", server.scripttool.getSystemName())
|
||||
print(" Mandant:", server.scripttool.getMandant())
|
||||
print(" MandantName:", server.scripttool.getMandantName())
|
||||
print(" InstallPath:", server.scripttool.getInstallPath())
|
||||
print(" InstallPathAppServer:", server.scripttool.getInstallPathAppServer())
|
||||
print(" InstallPathWebServer:", server.scripttool.getInstallPathWebServer())
|
||||
print(" ServerInfo - Version:", server.scripttool.getServerInfo().find("version").text)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(applus_configs.serverConfYamlTest)
|
@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
||||
|
||||
[project]
|
||||
name = "PyAPplus64"
|
||||
version = "1.0.0"
|
||||
version = "1.0.1"
|
||||
authors = [
|
||||
{ name="Thomas Tuerk", email="kontakt@thomas-tuerk.de" },
|
||||
]
|
||||
|
@ -6,8 +6,6 @@
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
from . import applus_db
|
||||
from . import applus_server
|
||||
from . import applus_sysconf
|
||||
@ -18,13 +16,12 @@ import yaml
|
||||
import urllib.parse
|
||||
from zeep import Client
|
||||
import pyodbc # type: ignore
|
||||
from typing import *
|
||||
from typing import TYPE_CHECKING, Optional, Any, Callable, Dict, Sequence, Set, List
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import FileDescriptorOrPath
|
||||
|
||||
|
||||
|
||||
class APplusServer:
|
||||
"""
|
||||
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
|
||||
: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
|
||||
"""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.
|
||||
"""
|
||||
|
||||
|
||||
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"""
|
||||
|
||||
self.sysconf : applus_sysconf.APplusSysConf = applus_sysconf.APplusSysConf(self);
|
||||
self.sysconf: applus_sysconf.APplusSysConf = applus_sysconf.APplusSysConf(self)
|
||||
"""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"""
|
||||
|
||||
self.client_table = self.server_conn.getClient("p2core","Table");
|
||||
self.client_xml = self.server_conn.getClient("p2core","XML");
|
||||
self.client_table = self.server_conn.getClient("p2core", "Table")
|
||||
self.client_xml = self.server_conn.getClient("p2core", "XML")
|
||||
self.client_nummer = self.server_conn.getClient("p2system", "Nummer")
|
||||
|
||||
def reconnectDB(self) -> None:
|
||||
@ -86,13 +85,13 @@ class APplusServer:
|
||||
if raw:
|
||||
return str(sql)
|
||||
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,
|
||||
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
|
||||
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)
|
||||
|
||||
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:
|
||||
"""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."""
|
||||
sqlC = self.completeSQL(sql, raw=raw);
|
||||
sqlC = self.completeSQL(sql, raw=raw)
|
||||
applus_db.rawQuery(self.db_conn, sqlC, f, *args)
|
||||
|
||||
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."""
|
||||
sqlC = self.completeSQL(sql, raw=raw);
|
||||
sqlC = self.completeSQL(sql, raw=raw)
|
||||
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]]:
|
||||
"""Führt eine SQL Query aus, die maximal eine Zeile zurückliefern soll.
|
||||
Diese Zeile wird als Dictionary geliefert."""
|
||||
row = self.dbQuerySingleRow(sql, *args, raw=raw);
|
||||
row = self.dbQuerySingleRow(sql, *args, raw=raw)
|
||||
if row:
|
||||
return applus_db.row_to_dict(row);
|
||||
return applus_db.row_to_dict(row)
|
||||
else:
|
||||
return None
|
||||
|
||||
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.
|
||||
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)
|
||||
|
||||
def isDBTableKnown(self, table: str) -> bool:
|
||||
"""Prüft, ob eine Tabelle im System bekannt ist"""
|
||||
sql = "select count(*) from SYS.TABLES T where T.NAME=?"
|
||||
c = self.dbQuerySingleValue(sql, table);
|
||||
c = self.dbQuerySingleValue(sql, table)
|
||||
return (c > 0)
|
||||
|
||||
def getClient(self, package: str, name: str) -> Client:
|
||||
@ -146,7 +145,7 @@ class APplusServer:
|
||||
:return: den 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]:
|
||||
"""
|
||||
@ -158,14 +157,14 @@ class APplusServer:
|
||||
:rtype: {str}
|
||||
"""
|
||||
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")
|
||||
if not (isComputed == None):
|
||||
if not (isComputed is None):
|
||||
join.on.addConditionFieldEq("c.is_computed", isComputed)
|
||||
sql.addFields("C.NAME")
|
||||
|
||||
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]]:
|
||||
"""
|
||||
@ -176,11 +175,9 @@ class APplusServer:
|
||||
"""
|
||||
return applus_db.getUniqueFieldsOfTable(self.db_conn, table)
|
||||
|
||||
|
||||
def useXML(self, xml: str) -> Any:
|
||||
"""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:
|
||||
"""
|
||||
@ -209,13 +206,12 @@ class APplusServer:
|
||||
|
||||
return applus_usexml.UseXmlRowInsertOrUpdate(self, table)
|
||||
|
||||
|
||||
def mkUseXMLRowDelete(self, table: str, id: int) -> applus_usexml.UseXmlRowDelete:
|
||||
return applus_usexml.UseXmlRowDelete(self, table, id)
|
||||
|
||||
def execUseXMLRowDelete(self, table: str, id: int) -> None:
|
||||
delRow = self.mkUseXMLRowDelete(table, id)
|
||||
delRow.delete();
|
||||
delRow.delete()
|
||||
|
||||
def nextNumber(self, obj: str) -> str:
|
||||
"""
|
||||
@ -225,29 +221,28 @@ class APplusServer:
|
||||
|
||||
def makeWebLink(self, base: str, **kwargs: Any) -> str:
|
||||
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
|
||||
for arg, argv in kwargs.items():
|
||||
if not (argv == None):
|
||||
if not (argv is None):
|
||||
if firstArg:
|
||||
firstArg = False;
|
||||
firstArg = False
|
||||
url += "?"
|
||||
else:
|
||||
url += "&"
|
||||
url += arg + "=" + urllib.parse.quote(str(argv))
|
||||
return url;
|
||||
return url
|
||||
|
||||
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:
|
||||
return self.makeWebLink("wp/wauftragRec.aspx", **kwargs);
|
||||
return self.makeWebLink("wp/wauftragRec.aspx", **kwargs)
|
||||
|
||||
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:
|
||||
@ -268,8 +263,9 @@ def applusFromConfigDict(yamlDict:Dict[str, Any], user:Optional[str]=None, env:O
|
||||
server=yamlDict["dbserver"]["server"],
|
||||
database=yamlDict["dbserver"]["db"],
|
||||
user=yamlDict["dbserver"]["user"],
|
||||
password=yamlDict["dbserver"]["password"]);
|
||||
return APplusServer(dbparams, app_server, web_server);
|
||||
password=yamlDict["dbserver"]["password"])
|
||||
return APplusServer(dbparams, app_server, web_server)
|
||||
|
||||
|
||||
def applusFromConfigFile(yamlfile: 'FileDescriptorOrPath',
|
||||
user: Optional[str] = None, env: Optional[str] = None) -> APplusServer:
|
||||
@ -280,8 +276,8 @@ def applusFromConfigFile(yamlfile : 'FileDescriptorOrPath',
|
||||
|
||||
return applusFromConfigDict(yamlDict, user=user, env=env)
|
||||
|
||||
|
||||
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"""
|
||||
yamlDict = yaml.safe_load(yamlString)
|
||||
return applusFromConfigDict(yamlDict, user=user, env=env)
|
||||
|
||||
|
@ -6,16 +6,15 @@
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
import pyodbc # type: ignore
|
||||
import logging
|
||||
from .sql_utils import SqlStatement
|
||||
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:
|
||||
"""
|
||||
@ -24,11 +23,10 @@ class APplusDBSettings:
|
||||
|
||||
def __init__(self, server: str, database: str, user: str, password: str):
|
||||
self.server = server
|
||||
self.database = database;
|
||||
self.database = database
|
||||
self.user = user
|
||||
self.password = password
|
||||
|
||||
|
||||
def getConnectionString(self) -> str:
|
||||
"""Liefert den ODBC Connection-String für die Verbindung.
|
||||
:return: den Connection-String
|
||||
@ -45,17 +43,18 @@ class APplusDBSettings:
|
||||
return pyodbc.connect(self.getConnectionString())
|
||||
|
||||
|
||||
|
||||
def row_to_dict(row: pyodbc.Row) -> Dict[str, Any]:
|
||||
"""Konvertiert eine Zeile in ein Dictionary"""
|
||||
return dict(zip([t[0] for t in row.cursor_description], row))
|
||||
|
||||
|
||||
def _logSQLWithArgs(sql: SqlStatement, *args: Any) -> None:
|
||||
if args:
|
||||
logger.debug("executing '{}' with args {}".format(str(sql), str(args)))
|
||||
else:
|
||||
logger.debug("executing '{}'".format(str(sql)))
|
||||
|
||||
|
||||
def rawQueryAll(
|
||||
cnxn: pyodbc.Connection,
|
||||
sql: SqlStatement,
|
||||
@ -69,42 +68,46 @@ def rawQueryAll(
|
||||
with cnxn.cursor() as cursor:
|
||||
cursor.execute(str(sql), *args)
|
||||
|
||||
rows = cursor.fetchall();
|
||||
rows = cursor.fetchall()
|
||||
if apply is None:
|
||||
return rows
|
||||
else:
|
||||
res = []
|
||||
for r in rows:
|
||||
rr = apply(r)
|
||||
if not (rr == None):
|
||||
if not (rr is None):
|
||||
res.append(rr)
|
||||
return res
|
||||
|
||||
|
||||
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."""
|
||||
_logSQLWithArgs(sql, *args)
|
||||
with cnxn.cursor() as cursor:
|
||||
cursor.execute(str(sql), *args)
|
||||
for row in cursor:
|
||||
f(row);
|
||||
f(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."""
|
||||
_logSQLWithArgs(sql, *args)
|
||||
with cnxn.cursor() as cursor:
|
||||
cursor.execute(str(sql), *args)
|
||||
return cursor.fetchone();
|
||||
return cursor.fetchone()
|
||||
|
||||
|
||||
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."""
|
||||
_logSQLWithArgs(sql, *args)
|
||||
with cnxn.cursor() as cursor:
|
||||
cursor.execute(str(sql), *args)
|
||||
row = cursor.fetchone();
|
||||
row = cursor.fetchone()
|
||||
if row:
|
||||
return row[0];
|
||||
return row[0]
|
||||
else:
|
||||
return None;
|
||||
return None
|
||||
|
||||
|
||||
def getUniqueFieldsOfTable(cnxn: pyodbc.Connection, table: str) -> Dict[str, List[str]]:
|
||||
"""
|
||||
@ -149,7 +152,7 @@ class DBTableIDs():
|
||||
"""
|
||||
table = table.upper()
|
||||
if not (table in self.data):
|
||||
self.data[table] = set(ids);
|
||||
self.data[table] = set(ids)
|
||||
else:
|
||||
self.data[table].update(ids)
|
||||
|
||||
@ -167,5 +170,3 @@ class DBTableIDs():
|
||||
|
||||
def __str__(self) -> str:
|
||||
return str(self.data)
|
||||
|
||||
|
||||
|
@ -6,12 +6,12 @@
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
from .applus import APplusServer
|
||||
from . import sql_utils
|
||||
import lxml.etree as ET # type: ignore
|
||||
from typing import *
|
||||
from typing import Optional, Tuple, Set
|
||||
import pathlib
|
||||
|
||||
|
||||
class XMLDefinition:
|
||||
"""Repräsentation eines XML-Dokuments"""
|
||||
@ -31,10 +31,10 @@ class XMLDefinition:
|
||||
:rtype: Tuple[Set[str], bool]
|
||||
"""
|
||||
res: Set[str] = set()
|
||||
excl = True;
|
||||
excl = True
|
||||
dupl = self.root.find("duplicate")
|
||||
if (dupl is None):
|
||||
return (res, excl);
|
||||
return (res, excl)
|
||||
|
||||
exclS = dupl.get("type", default="exclude")
|
||||
excl = exclS.casefold() == "exclude"
|
||||
@ -80,6 +80,24 @@ class APplusScriptTool:
|
||||
def getSystemName(self) -> str:
|
||||
return self.client.service.getSystemName()
|
||||
|
||||
def getInstallPath(self) -> str:
|
||||
"""
|
||||
Liefert den Installionspfad des Appservers
|
||||
"""
|
||||
return self.client.service.getInstallPath()
|
||||
|
||||
def getInstallPathAppServer(self) -> pathlib.Path:
|
||||
"""
|
||||
Liefert den Installionspfad des Appservers als PathLib-Path
|
||||
"""
|
||||
return pathlib.Path(self.getInstallPath())
|
||||
|
||||
def getInstallPathWebServer(self) -> pathlib.Path:
|
||||
"""
|
||||
Liefert den Installionspfad des Webservers als PathLib-Path
|
||||
"""
|
||||
return self.getInstallPathAppServer().parents[0].joinpath("WebServer")
|
||||
|
||||
def getXMLDefinitionString(self, obj: str, mandant: str = "") -> str:
|
||||
"""
|
||||
Läd die XML-Defintion als String vom APPServer. Auch wenn kein XML-Dokument im Dateisystem gefunden wird,
|
||||
@ -103,7 +121,7 @@ class APplusScriptTool:
|
||||
:type obj: str
|
||||
:param mandant: der Mandant, dessen XML-Doku geladen werden soll, wenn "" wird der Standard-Mandant verwendet
|
||||
:type mandant: str optional
|
||||
:return: das gefundene und mittels ElementTree geparste XML-Dokument
|
||||
:return: das gefundene und geparste XML-Dokument
|
||||
:rtype: ET.Element
|
||||
"""
|
||||
return ET.fromstring(self.getXMLDefinitionString(obj, mandant=mandant))
|
||||
@ -118,22 +136,21 @@ class APplusScriptTool:
|
||||
:type obj: str
|
||||
:param mandant: der Mandant, dessen XML-Doku geladen werden soll, wenn "" wird der Standard-Mandant verwendet
|
||||
:type mandant: str optional
|
||||
:return: das gefundene und mittels ElementTree geparste XML-Dokument
|
||||
:return: das gefundene und geparste XML-Dokument
|
||||
:rtype: Optional[XMLDefinition]
|
||||
"""
|
||||
e = self.getXMLDefinition(obj, mandant=mandant);
|
||||
e = self.getXMLDefinition(obj, mandant=mandant)
|
||||
if e is None:
|
||||
return None
|
||||
|
||||
if e.find("md5") is None:
|
||||
return None;
|
||||
return None
|
||||
|
||||
o = e.find("object")
|
||||
if o is None:
|
||||
return None
|
||||
else:
|
||||
return XMLDefinition(o);
|
||||
|
||||
return XMLDefinition(o)
|
||||
|
||||
def getMandant(self) -> str:
|
||||
"""
|
||||
@ -146,3 +163,21 @@ class APplusScriptTool:
|
||||
Liefert den Namen des aktuellen Mandanten
|
||||
"""
|
||||
return self.client.service.getCurrentClientProperty("NAME")
|
||||
|
||||
def getServerInfoString(self) -> str:
|
||||
"""
|
||||
Liefert Informationen zum Server als String. Dieser String repräsentiert ein XML Dokument.
|
||||
|
||||
:return: das XML-Dokument als String
|
||||
:rtype: str
|
||||
"""
|
||||
return self.client.service.getP2plusServerInfo()
|
||||
|
||||
def getServerInfo(self) -> Optional[ET.Element]:
|
||||
"""
|
||||
Liefert Informationen zum Server als ein XML Dokument.
|
||||
|
||||
:return: das gefundene und geparste XML-Dokument
|
||||
:rtype: ET.Element
|
||||
"""
|
||||
return ET.fromstring(self.getServerInfoString())
|
||||
|
@ -6,8 +6,6 @@
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
from requests import Session # type: ignore
|
||||
from requests.auth import HTTPBasicAuth # type: ignore # or HTTPDigestAuth, or OAuth1, etc.
|
||||
from zeep import Client
|
||||
@ -27,17 +25,18 @@ class APplusAppServerSettings:
|
||||
self.user = user
|
||||
self.env = env
|
||||
|
||||
|
||||
class APplusWebServerSettings:
|
||||
"""
|
||||
Einstellungen, mit welchem APplus Web-Server sich verbunden werden soll.
|
||||
"""
|
||||
|
||||
def __init__(self, baseurl: Optional[str] = None):
|
||||
self.baseurl : Optional[str] = baseurl;
|
||||
self.baseurl: Optional[str] = baseurl
|
||||
try:
|
||||
assert (isinstance(self.baseurl, str))
|
||||
if not (self.baseurl == None) and not (self.baseurl[-1] == "/"):
|
||||
self.baseurl = self.baseurl + "/";
|
||||
if not (self.baseurl is None) and not (self.baseurl[-1] == "/"):
|
||||
self.baseurl = self.baseurl + "/"
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -49,7 +48,7 @@ class APplusServerConnection:
|
||||
:type settings: APplusAppServerSettings
|
||||
"""
|
||||
def __init__(self, settings: APplusAppServerSettings) -> None:
|
||||
userEnv = settings.user;
|
||||
userEnv = settings.user
|
||||
if (settings.env):
|
||||
userEnv += "|" + settings.env
|
||||
|
||||
@ -59,8 +58,8 @@ class APplusServerConnection:
|
||||
self.transport = Transport(cache=SqliteCache(), session=session)
|
||||
# self.transport = Transport(session=session)
|
||||
self.clientCache: Dict[str, Client] = {}
|
||||
self.settings=settings;
|
||||
self.appserverUrl = "http://" + settings.appserver + ":" + str(settings.appserverPort) + "/";
|
||||
self.settings = settings
|
||||
self.appserverUrl = "http://" + settings.appserver + ":" + str(settings.appserverPort) + "/"
|
||||
|
||||
def getClient(self, package: str, name: str) -> Client:
|
||||
"""Erzeugt einen zeep - Client.
|
||||
@ -77,11 +76,11 @@ class APplusServerConnection:
|
||||
:return: den Client
|
||||
:rtype: Client
|
||||
"""
|
||||
url = package+"/"+name;
|
||||
url = package+"/"+name
|
||||
try:
|
||||
return self.clientCache[url];
|
||||
return self.clientCache[url]
|
||||
except:
|
||||
fullClientUrl = self.appserverUrl + url + ".jws?wsdl"
|
||||
client = Client(fullClientUrl, transport=self.transport)
|
||||
self.clientCache[url] = client;
|
||||
return client;
|
||||
self.clientCache[url] = client
|
||||
return client
|
||||
|
@ -6,9 +6,7 @@
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
from typing import *
|
||||
from typing import TYPE_CHECKING, Optional, Dict, Any, Callable, Sequence
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .applus import APplusServer
|
||||
@ -28,32 +26,32 @@ class APplusSysConf:
|
||||
self.cache: Dict[str, type] = {}
|
||||
|
||||
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:
|
||||
cacheKey = module + "/" + name + "/" + ty;
|
||||
cacheKey = module + "/" + name + "/" + ty
|
||||
if useCache and cacheKey in self.cache:
|
||||
return self.cache[cacheKey]
|
||||
else:
|
||||
v = f(module, name);
|
||||
self.cache[cacheKey] = v;
|
||||
return v;
|
||||
v = f(module, name)
|
||||
self.cache[cacheKey] = v
|
||||
return v
|
||||
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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]]:
|
||||
s = self.getString(module, name, useCache=useCache);
|
||||
if (s == None or s == ""):
|
||||
s = self.getString(module, name, useCache=useCache)
|
||||
if (s is None or s == ""):
|
||||
return None
|
||||
|
||||
return s.split(sep);
|
||||
return s.split(sep)
|
||||
|
@ -6,26 +6,23 @@
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
import lxml.etree as ET # type: ignore
|
||||
from . import sql_utils
|
||||
import datetime
|
||||
from typing import *
|
||||
from typing import TYPE_CHECKING, Any, Dict, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .applus import APplusServer
|
||||
|
||||
|
||||
|
||||
def _formatValueForXMLRow(v: Any) -> str:
|
||||
"""Hilfsfunktion zum Formatieren eines Wertes für XML"""
|
||||
if (v is None):
|
||||
return "";
|
||||
return ""
|
||||
if isinstance(v, (int, float)):
|
||||
return str(v);
|
||||
return str(v)
|
||||
elif isinstance(v, str):
|
||||
return v;
|
||||
return v
|
||||
elif isinstance(v, datetime.datetime):
|
||||
return v.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||
elif isinstance(v, datetime.date):
|
||||
@ -71,7 +68,7 @@ class UseXmlRow:
|
||||
|
||||
def _buildXML(self) -> ET.Element:
|
||||
"""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():
|
||||
child = ET.Element(name)
|
||||
@ -80,7 +77,6 @@ class UseXmlRow:
|
||||
|
||||
return row
|
||||
|
||||
|
||||
def toprettyxml(self) -> str:
|
||||
"""
|
||||
Gibt das formatierte XML aus. Dieses kann per useXML an den AppServer übergeben werden.
|
||||
@ -93,7 +89,7 @@ class UseXmlRow:
|
||||
|
||||
if name is None:
|
||||
return None
|
||||
name = sql_utils.normaliseDBfield(name);
|
||||
name = sql_utils.normaliseDBfield(name)
|
||||
|
||||
if name in self.fields:
|
||||
return self.fields[name]
|
||||
@ -116,7 +112,7 @@ class UseXmlRow:
|
||||
return False
|
||||
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.
|
||||
|
||||
@ -129,7 +125,6 @@ class UseXmlRow:
|
||||
|
||||
self.fields[sql_utils.normaliseDBfield(name)] = value
|
||||
|
||||
|
||||
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
|
||||
@ -146,13 +141,12 @@ class UseXmlRow:
|
||||
:type ts: bytes
|
||||
"""
|
||||
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:
|
||||
self.addField("timestamp", ts.hex());
|
||||
self.addField("timestamp", ts.hex())
|
||||
else:
|
||||
raise Exception("kein Eintrag in Tabelle '" + self.table + " mit ID " + str(id) + " gefunden")
|
||||
|
||||
|
||||
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
|
||||
@ -164,14 +158,14 @@ class UseXmlRow:
|
||||
:type ts: bytes
|
||||
"""
|
||||
self.addField("id", id)
|
||||
self.addTimestampField(id, ts=ts);
|
||||
self.addTimestampField(id, ts=ts)
|
||||
|
||||
def exec(self) -> Any:
|
||||
"""
|
||||
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.
|
||||
"""
|
||||
return self.applus.useXML(self.toprettyxml());
|
||||
return self.applus.useXML(self.toprettyxml())
|
||||
|
||||
|
||||
class UseXmlRowInsert(UseXmlRow):
|
||||
@ -185,14 +179,14 @@ class UseXmlRowInsert(UseXmlRow):
|
||||
"""
|
||||
|
||||
def __init__(self, applus: 'APplusServer', table: str) -> None:
|
||||
super().__init__(applus, table, "insert");
|
||||
super().__init__(applus, table, "insert")
|
||||
|
||||
def insert(self) -> int:
|
||||
"""
|
||||
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`.
|
||||
"""
|
||||
return super().exec();
|
||||
return super().exec()
|
||||
|
||||
|
||||
class UseXmlRowDelete(UseXmlRow):
|
||||
@ -212,16 +206,15 @@ class UseXmlRowDelete(UseXmlRow):
|
||||
"""
|
||||
|
||||
def __init__(self, applus: 'APplusServer', table: str, id: int, ts: Optional[bytes] = None) -> None:
|
||||
super().__init__(applus, table, "delete");
|
||||
self.addTimestampIDFields(id, ts=ts);
|
||||
|
||||
super().__init__(applus, table, "delete")
|
||||
self.addTimestampIDFields(id, ts=ts)
|
||||
|
||||
def delete(self) -> None:
|
||||
"""
|
||||
Führt das delete aus. Evtl. wird dabei eine Exception geworfen.
|
||||
Dies ist eine Umbenennung von :meth:`exec`.
|
||||
"""
|
||||
super().exec();
|
||||
super().exec()
|
||||
|
||||
|
||||
class UseXmlRowUpdate(UseXmlRow):
|
||||
@ -240,17 +233,15 @@ class UseXmlRowUpdate(UseXmlRow):
|
||||
"""
|
||||
|
||||
def __init__(self, applus: 'APplusServer', table: str, id: int, ts: Optional[bytes] = None) -> None:
|
||||
super().__init__(applus, table, "update");
|
||||
self.addTimestampIDFields(id, ts=ts);
|
||||
|
||||
super().__init__(applus, table, "update")
|
||||
self.addTimestampIDFields(id, ts=ts)
|
||||
|
||||
def update(self) -> None:
|
||||
"""
|
||||
Führt das update aus. Evtl. wird dabei eine Exception geworfen.
|
||||
Dies ist eine Umbenennung von :meth:`exec`.
|
||||
"""
|
||||
super().exec();
|
||||
|
||||
super().exec()
|
||||
|
||||
|
||||
class UseXmlRowInsertOrUpdate(UseXmlRow):
|
||||
@ -268,20 +259,19 @@ class UseXmlRowInsertOrUpdate(UseXmlRow):
|
||||
"""
|
||||
|
||||
def __init__(self, applus: 'APplusServer', table: str) -> None:
|
||||
super().__init__(applus, table, "");
|
||||
super().__init__(applus, table, "")
|
||||
|
||||
|
||||
def checkExists(self) -> int|None:
|
||||
def checkExists(self) -> Optional[int]:
|
||||
"""
|
||||
Prüft, ob der Datensatz bereits in der DB existiert.
|
||||
Ist dies der Fall, wird die ID geliefert, sonst None
|
||||
"""
|
||||
|
||||
# Baue Bedingung
|
||||
cond = sql_utils.SqlConditionOr();
|
||||
cond = sql_utils.SqlConditionOr()
|
||||
for idx, fs in self.applus.getUniqueFieldsOfTable(self.table).items():
|
||||
if (self.checkFieldsSet(*fs)):
|
||||
condIdx = sql_utils.SqlConditionAnd();
|
||||
condIdx = sql_utils.SqlConditionAnd()
|
||||
for f in fs:
|
||||
condIdx.addConditionFieldEq(f, self.getField(f))
|
||||
cond.addCondition(condIdx)
|
||||
@ -296,7 +286,7 @@ class UseXmlRowInsertOrUpdate(UseXmlRow):
|
||||
r = UseXmlRowInsert(self.applus, self.table)
|
||||
for k, v in self.fields.items():
|
||||
r.addField(k, v)
|
||||
return r.insert();
|
||||
return r.insert()
|
||||
|
||||
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
|
||||
@ -311,7 +301,7 @@ class UseXmlRowInsertOrUpdate(UseXmlRow):
|
||||
r = UseXmlRowUpdate(self.applus, self.table, id, ts=ts)
|
||||
for k, v in self.fields.items():
|
||||
r.addField(k, v)
|
||||
r.update();
|
||||
r.update()
|
||||
return id
|
||||
|
||||
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.
|
||||
"""
|
||||
|
||||
id = self.checkExists();
|
||||
if id == None:
|
||||
id = self.checkExists()
|
||||
if id is None:
|
||||
return self.insert()
|
||||
else:
|
||||
return self.update(id=id)
|
||||
@ -332,4 +322,4 @@ class UseXmlRowInsertOrUpdate(UseXmlRow):
|
||||
Dies ist eine Umbenennung von :meth:`exec`.
|
||||
Es wird die ID des Eintrages geliefert
|
||||
"""
|
||||
return self.exec();
|
||||
return self.exec()
|
||||
|
@ -6,8 +6,6 @@
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
Dupliziert ein oder mehrere APplus Business-Objekte
|
||||
"""
|
||||
@ -19,9 +17,9 @@ from .applus import APplusServer
|
||||
import pyodbc # type: ignore
|
||||
import traceback
|
||||
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"})
|
||||
"""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]
|
||||
if (xmlDefs is None):
|
||||
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)
|
||||
else:
|
||||
(fields, excl) = xmlDefs.getDuplicate()
|
||||
@ -46,8 +44,7 @@ def getFieldsToCopyForTable(server : APplusServer, table : str, force:bool=True)
|
||||
return fields.difference(noCopyFields)
|
||||
|
||||
allFields = server.getTableFields(table, isComputed=False)
|
||||
return allFields.difference(fields).difference(noCopyFields);
|
||||
|
||||
return allFields.difference(fields).difference(noCopyFields)
|
||||
|
||||
|
||||
class FieldsToCopyForTableCache():
|
||||
@ -85,7 +82,7 @@ def initFieldsToCopyForTableCacheIfNeeded(server : APplusServer, cache : Optiona
|
||||
if cache is None:
|
||||
return FieldsToCopyForTableCache(server)
|
||||
else:
|
||||
return cache;
|
||||
return cache
|
||||
|
||||
|
||||
class DuplicateBusinessObject():
|
||||
@ -176,9 +173,9 @@ class DuplicateBusinessObject():
|
||||
nonlocal res
|
||||
insertRow: applus_usexml.UseXmlRow
|
||||
if do.allowUpdate:
|
||||
insertRow = server.mkUseXMLRowInsertOrUpdate(do.table);
|
||||
insertRow = server.mkUseXMLRowInsertOrUpdate(do.table)
|
||||
else:
|
||||
insertRow = server.mkUseXMLRowInsert(do.table);
|
||||
insertRow = server.mkUseXMLRowInsert(do.table)
|
||||
|
||||
for f, v in do.fields.items():
|
||||
insertRow.addField(f, v)
|
||||
@ -188,7 +185,7 @@ class DuplicateBusinessObject():
|
||||
res.add(do.table, id)
|
||||
return id
|
||||
except:
|
||||
msg = traceback.format_exc();
|
||||
msg = traceback.format_exc()
|
||||
logger.error("Exception inserting BusinessObjekt: %s\n%s", str(insertRow), msg)
|
||||
return None
|
||||
|
||||
@ -209,7 +206,7 @@ class DuplicateBusinessObject():
|
||||
|
||||
# load missing fields from DB
|
||||
if len(connectMissing) > 0:
|
||||
sql = sql_utils.SqlStatementSelect(do.table);
|
||||
sql = sql_utils.SqlStatementSelect(do.table)
|
||||
sql.where.addConditionFieldEq("id", doID)
|
||||
for fd in connectMissing:
|
||||
sql.addFields(fd)
|
||||
@ -224,7 +221,6 @@ class DuplicateBusinessObject():
|
||||
if not (id is None):
|
||||
insertDeps(so, id)
|
||||
|
||||
|
||||
def insertDeps(do: 'DuplicateBusinessObject', doID: int) -> None:
|
||||
for so in do.dependentObjs:
|
||||
insertDep(do, doID, so["dependentObj"], so["connection"])
|
||||
@ -235,7 +231,6 @@ class DuplicateBusinessObject():
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def setFields(self, upds: Dict[str, Any]) -> None:
|
||||
"""
|
||||
Setzt Felder des DuplicateBusinessObjektes und falls nötig seiner Unterobjekte.
|
||||
@ -257,14 +252,12 @@ class DuplicateBusinessObject():
|
||||
subupds[fs] = upds[fp]
|
||||
setFieldsInternal(su["dependentObj"], subupds)
|
||||
|
||||
|
||||
updsNorm: Dict[str, Any] = {}
|
||||
for f, v in upds.items():
|
||||
updsNorm[sql_utils.normaliseDBfield(f)] = v
|
||||
setFieldsInternal(self, updsNorm)
|
||||
|
||||
|
||||
|
||||
def _loadDBDuplicateBusinessObjectDict(
|
||||
server: APplusServer,
|
||||
table: str,
|
||||
@ -282,7 +275,7 @@ def _loadDBDuplicateBusinessObjectDict(
|
||||
:param cache: Cache, so dass benötigte Felder nicht immer wieder neu berechnet werden müssen
|
||||
:return: das neue DuplicateBusinessObject
|
||||
"""
|
||||
table = table.upper();
|
||||
table = table.upper()
|
||||
|
||||
def getFieldsToCopy() -> Set[str]:
|
||||
if cache is None:
|
||||
@ -290,20 +283,18 @@ def _loadDBDuplicateBusinessObjectDict(
|
||||
else:
|
||||
return cache.getFieldsToCopyForTable(table)
|
||||
|
||||
|
||||
def getFields() -> Tuple[Dict[str, Any], Dict[str, Any]]:
|
||||
ftc = getFieldsToCopy()
|
||||
fields = {}
|
||||
fieldsNotCopied = {}
|
||||
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:
|
||||
fields[f] = v
|
||||
else:
|
||||
fieldsNotCopied[f] = v
|
||||
return (fields, fieldsNotCopied)
|
||||
|
||||
|
||||
if (row is None):
|
||||
return None
|
||||
|
||||
@ -335,21 +326,22 @@ def loadDBDuplicateBusinessObject(
|
||||
:return: das neue DuplicateBusinessObject
|
||||
:rtype: Optional[DuplicateBusinessObject]
|
||||
"""
|
||||
table = table.upper();
|
||||
table = table.upper()
|
||||
|
||||
def getRow() -> pyodbc.Row:
|
||||
sql = sql_utils.SqlStatementSelect(table)
|
||||
sql.setTop(1)
|
||||
sql.where.addCondition(cond);
|
||||
sql.where.addCondition(cond)
|
||||
return server.dbQuerySingleRow(sql)
|
||||
|
||||
return _loadDBDuplicateBusinessObjectDict(server, table, getRow(), cache=cache, allowUpdate=allowUpdate);
|
||||
return _loadDBDuplicateBusinessObjectDict(server, table, getRow(), cache=cache, allowUpdate=allowUpdate)
|
||||
|
||||
|
||||
def loadDBDuplicateBusinessObjectSimpleCond(
|
||||
server: APplusServer,
|
||||
table: str,
|
||||
field: str,
|
||||
value : Optional[Union[sql_utils.SqlValue, bool]],
|
||||
value: Union[sql_utils.SqlValue, bool, None],
|
||||
cache: Optional[FieldsToCopyForTableCache] = None,
|
||||
allowUpdate: bool = False) -> Optional[DuplicateBusinessObject]:
|
||||
"""
|
||||
@ -402,11 +394,12 @@ def loadDBDuplicateBusinessObjects(
|
||||
sql.where.addCondition(cond)
|
||||
return server.dbQueryAll(sql, apply=processRow)
|
||||
|
||||
|
||||
def loadDBDuplicateBusinessObjectsSimpleCond(
|
||||
server: APplusServer,
|
||||
table: str,
|
||||
field: str,
|
||||
value : Optional[Union[sql_utils.SqlValue, bool]],
|
||||
value: Union[sql_utils.SqlValue, bool, None],
|
||||
cache: Optional[FieldsToCopyForTableCache] = None,
|
||||
allowUpdate: bool = False) -> Sequence[DuplicateBusinessObject]:
|
||||
"""
|
||||
@ -449,7 +442,7 @@ def loadDBDuplicateAPlan(
|
||||
:rtype: DuplicateBusinessObject
|
||||
"""
|
||||
|
||||
cache = initFieldsToCopyForTableCacheIfNeeded(server, cache);
|
||||
cache = initFieldsToCopyForTableCacheIfNeeded(server, cache)
|
||||
boMain = loadDBDuplicateBusinessObjectSimpleCond(server, "aplan", "APLAN", aplan, cache=cache)
|
||||
if boMain is None:
|
||||
return None
|
||||
@ -474,7 +467,7 @@ def loadDBDuplicateStueli(server : APplusServer, stueli : str, cache:Optional[Fi
|
||||
:rtype: Optional[DuplicateBusinessObject]
|
||||
"""
|
||||
|
||||
cache = initFieldsToCopyForTableCacheIfNeeded(server, cache);
|
||||
cache = initFieldsToCopyForTableCacheIfNeeded(server, cache)
|
||||
boMain = loadDBDuplicateBusinessObjectSimpleCond(server, "stueli", "stueli", stueli, cache=cache)
|
||||
if boMain is None:
|
||||
return None
|
||||
@ -484,6 +477,7 @@ def loadDBDuplicateStueli(server : APplusServer, stueli : str, cache:Optional[Fi
|
||||
|
||||
return boMain
|
||||
|
||||
|
||||
def addSachgruppeDependentObjects(
|
||||
do: DuplicateBusinessObject,
|
||||
server: APplusServer,
|
||||
@ -498,9 +492,9 @@ def addSachgruppeDependentObjects(
|
||||
:type cache: Optional[FieldsToCopyForTableCache]
|
||||
"""
|
||||
|
||||
cache = initFieldsToCopyForTableCacheIfNeeded(server, cache);
|
||||
cache = initFieldsToCopyForTableCacheIfNeeded(server, cache)
|
||||
klasse = do.fields.get(sql_utils.normaliseDBfield("SACHGRUPPENKLASSE"), None)
|
||||
if (klasse == None):
|
||||
if (klasse is None):
|
||||
# keine Klasse gesetzt, nichts zu kopieren
|
||||
return
|
||||
|
||||
@ -511,7 +505,7 @@ def addSachgruppeDependentObjects(
|
||||
sql.where.addConditionFieldEq("tabelle", do.table)
|
||||
return server.dbQueryAll(sql, apply=lambda r: r.sachgruppe)
|
||||
|
||||
gruppen = loadGruppen();
|
||||
gruppen = loadGruppen()
|
||||
|
||||
# Gruppe bearbeiten
|
||||
def processGruppen() -> None:
|
||||
@ -525,11 +519,9 @@ def addSachgruppeDependentObjects(
|
||||
for so in loadDBDuplicateBusinessObjects(server, "sachwert", cond, cache=cache, allowUpdate=True):
|
||||
do.addDependentBusinessObject(so, ("guid", "instanzguid"))
|
||||
|
||||
|
||||
processGruppen()
|
||||
|
||||
|
||||
|
||||
def loadDBDuplicateArtikel(
|
||||
server: APplusServer,
|
||||
artikel: str,
|
||||
@ -554,7 +546,7 @@ def loadDBDuplicateArtikel(
|
||||
:rtype: DuplicateBusinessObject
|
||||
"""
|
||||
|
||||
cache = initFieldsToCopyForTableCacheIfNeeded(server, cache);
|
||||
cache = initFieldsToCopyForTableCacheIfNeeded(server, cache)
|
||||
boArt = loadDBDuplicateBusinessObjectSimpleCond(server, "artikel", "ARTIKEL", artikel, cache=cache)
|
||||
if boArt is None:
|
||||
return None
|
||||
|
@ -8,14 +8,13 @@
|
||||
|
||||
"""Pandas Interface für PyAPplus64."""
|
||||
|
||||
from typing import Annotated as Ann
|
||||
import pandas as pd # type: ignore
|
||||
from pandas._typing import AggFuncType, FilePath, WriteExcelBuffer # type: ignore
|
||||
import sqlalchemy
|
||||
import traceback
|
||||
from .applus import APplusServer
|
||||
from .applus import sql_utils
|
||||
from typing import *
|
||||
from typing import Optional, Callable, Sequence, Tuple, Any, Union
|
||||
|
||||
|
||||
def createSqlAlchemyEngine(server: APplusServer) -> sqlalchemy.Engine:
|
||||
@ -36,32 +35,32 @@ def pandasReadSql(
|
||||
"""
|
||||
|
||||
if engine is None:
|
||||
engine = createSqlAlchemyEngine(server);
|
||||
engine = createSqlAlchemyEngine(server)
|
||||
with engine.connect() as 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.
|
||||
|
||||
:param genLink: Funktion, die Parameter aufgerufen wird und einen Link generiert
|
||||
"""
|
||||
org:str|int|float=""
|
||||
org2:str|int|float
|
||||
org: Union[str, int, float] = ""
|
||||
org2: Union[str, int, float]
|
||||
try:
|
||||
org = genOrg();
|
||||
org = genOrg()
|
||||
if not org:
|
||||
return org
|
||||
else:
|
||||
if isinstance(org, (int, float)):
|
||||
org2 = org;
|
||||
org2 = org
|
||||
else:
|
||||
org2 = "\"" + str(org).replace("\"", "\"\"") + "\""
|
||||
|
||||
return "=HYPERLINK(\"{}\", {})".format(genLink(), org2)
|
||||
except:
|
||||
msg = traceback.format_exc();
|
||||
msg = traceback.format_exc()
|
||||
print("Exception: {}".format(msg))
|
||||
return org
|
||||
|
||||
@ -78,14 +77,14 @@ def mkDataframeColumn(df : pd.DataFrame, makeValue : AggFuncType) -> pd.Series:
|
||||
try:
|
||||
return makeValue(r)
|
||||
except:
|
||||
msg = traceback.format_exc();
|
||||
msg = traceback.format_exc()
|
||||
print("Exception: {}".format(msg))
|
||||
return ""
|
||||
|
||||
if (len(df.index) > 0):
|
||||
return df.apply(mkValueWrapper, axis=1)
|
||||
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:
|
||||
@ -100,11 +99,11 @@ def mkHyperlinkDataframeColumn(df : pd.DataFrame, makeOrig : AggFuncType, makeLi
|
||||
if (len(df.index) > 0):
|
||||
return df.apply(lambda r: _createHyperLinkGeneral(lambda: makeOrig(r), lambda: makeLink(r)), axis=1)
|
||||
else:
|
||||
return df.apply(lambda r: "", axis=1);
|
||||
return df.apply(lambda r: "", axis=1)
|
||||
|
||||
|
||||
def exportToExcel(
|
||||
filename:FilePath | WriteExcelBuffer | pd.ExcelWriter,
|
||||
filename: Union[FilePath, WriteExcelBuffer, pd.ExcelWriter],
|
||||
dfs: Sequence[Tuple[pd.DataFrame, str]],
|
||||
addTable: bool = True) -> None:
|
||||
"""
|
||||
@ -126,6 +125,4 @@ def exportToExcel(
|
||||
ws.add_table(0, 0, max_row, max_col - 1, {'columns': column_settings})
|
||||
|
||||
# Spaltenbreiten anpassen
|
||||
ws.autofit();
|
||||
|
||||
|
||||
ws.autofit()
|
||||
|
@ -6,7 +6,6 @@
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
|
||||
#-*- coding: utf-8 -*-
|
||||
"""
|
||||
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
|
||||
@ -22,19 +21,22 @@ APplus. Oft ist es sinnvoll, solche Parameter zu verwenden.
|
||||
|
||||
from __future__ import annotations
|
||||
import datetime
|
||||
from typing import *
|
||||
from typing import Set, Sequence, Union, Optional, cast, List
|
||||
|
||||
|
||||
def normaliseDBfield(f: str) -> str:
|
||||
"""Normalisiert die Darstellung eines DB-Feldes"""
|
||||
return str(f).upper();
|
||||
return str(f).upper()
|
||||
|
||||
|
||||
def normaliseDBfieldSet(s: Set[str]) -> Set[str]:
|
||||
"""Normalisiert eine Menge von DB-Feldern"""
|
||||
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"""
|
||||
return [normaliseDBfield(f) for f in l]
|
||||
return [normaliseDBfield(f) for f in fields]
|
||||
|
||||
|
||||
class SqlField():
|
||||
@ -45,10 +47,11 @@ class SqlField():
|
||||
:type fn: str
|
||||
"""
|
||||
def __init__(self, fn: str):
|
||||
self.field = normaliseDBfield(fn);
|
||||
self.field = normaliseDBfield(fn)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.field;
|
||||
return self.field
|
||||
|
||||
|
||||
class SqlFixed():
|
||||
"""
|
||||
@ -58,10 +61,11 @@ class SqlFixed():
|
||||
:type s: str
|
||||
"""
|
||||
def __init__(self, s: str):
|
||||
self.s = str(s);
|
||||
self.s = str(s)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.s;
|
||||
return self.s
|
||||
|
||||
|
||||
class SqlDateTime():
|
||||
"""
|
||||
@ -71,13 +75,14 @@ class SqlDateTime():
|
||||
:type dt: Union[datetime.datetime, datetime.date]
|
||||
"""
|
||||
def __init__(self, dt: Union[datetime.datetime, datetime.date] = datetime.datetime.now()) -> None:
|
||||
self.value = dt;
|
||||
self.value = dt
|
||||
|
||||
def __str__(self) -> str:
|
||||
# %f formatiert mit 6 Stellen, also microseconds. Es werden aber nur
|
||||
# 3 Stellen unterstützt, daher werden 3 weggeworfen.
|
||||
return self.value.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]
|
||||
|
||||
|
||||
class SqlDate():
|
||||
"""
|
||||
Wrapper um DateTime, die die Formatierung erleichtern
|
||||
@ -86,10 +91,11 @@ class SqlDate():
|
||||
:type d: Union[datetime.datetime, datetime.date]
|
||||
"""
|
||||
def __init__(self, d: Union[datetime.datetime, datetime.date] = datetime.datetime.now()) -> None:
|
||||
self.value = d;
|
||||
self.value = d
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.value.strftime("%Y%m%d");
|
||||
return self.value.strftime("%Y%m%d")
|
||||
|
||||
|
||||
class SqlTime():
|
||||
"""
|
||||
@ -104,6 +110,7 @@ class SqlTime():
|
||||
def __str__(self) -> str:
|
||||
return self.value.strftime("%H:%M:%S.%f")[:-3]
|
||||
|
||||
|
||||
class SqlParam():
|
||||
"""Hilfsklasse, für einen Parameter (?)"""
|
||||
def __init__(self) -> None:
|
||||
@ -112,9 +119,11 @@ class SqlParam():
|
||||
def __str__(self) -> str:
|
||||
return "?"
|
||||
|
||||
|
||||
sqlParam = SqlParam()
|
||||
"""Da SqlParam keinen Zustand hat, reicht ein einzelner statischer Wert"""
|
||||
|
||||
|
||||
def formatSqlValueString(s: str) -> str:
|
||||
"""
|
||||
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):
|
||||
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"""
|
||||
|
||||
|
||||
def formatSqlValue(v: SqlValue) -> str:
|
||||
"""
|
||||
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
|
||||
"""
|
||||
|
||||
if (v == None):
|
||||
raise Exception("formatSqlValue: null not supported");
|
||||
if v is None:
|
||||
raise Exception("formatSqlValue: null not supported")
|
||||
|
||||
if isinstance(v, (int, float, SqlField)):
|
||||
return str(v);
|
||||
return str(v)
|
||||
elif isinstance(v, str):
|
||||
return formatSqlValueString(v);
|
||||
return formatSqlValueString(v)
|
||||
elif isinstance(v, datetime.datetime):
|
||||
return "'" + str(SqlDateTime(v)) + "'";
|
||||
return "'" + str(SqlDateTime(v)) + "'"
|
||||
elif isinstance(v, datetime.date):
|
||||
return "'" + str(SqlDate(v)) + "'";
|
||||
return "'" + str(SqlDate(v)) + "'"
|
||||
elif isinstance(v, datetime.time):
|
||||
return "'" + str(SqlTime(v)) + "'";
|
||||
return "'" + str(SqlTime(v)) + "'"
|
||||
elif isinstance(v, (SqlDateTime, SqlDate, SqlTime)):
|
||||
return "'" + str(v) + "'";
|
||||
return "'" + str(v) + "'"
|
||||
elif isinstance(v, (SqlParam, SqlFixed)):
|
||||
return str(v);
|
||||
return str(v)
|
||||
else:
|
||||
raise Exception("formatSqlValue: unsupported type {}".format(type(v)));
|
||||
raise Exception("formatSqlValue: unsupported type {}".format(type(v)))
|
||||
|
||||
|
||||
class SqlCondition():
|
||||
"""Eine abstrakte Sql-Bedingung. Unterklassen erledigen die eigentliche Arbeit."""
|
||||
@ -175,20 +186,21 @@ class SqlCondition():
|
||||
:return: die Bedingung
|
||||
:rtype: str
|
||||
"""
|
||||
raise Exception("Not implemented");
|
||||
raise Exception("Not implemented")
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.getCondition();
|
||||
return self.getCondition()
|
||||
|
||||
|
||||
class SqlConditionPrepared(SqlCondition):
|
||||
"""Eine einfache Sql-Bedingung, die immer einen festen String zurückgibt."""
|
||||
|
||||
def __init__(self, cond: Union[SqlCondition, str]):
|
||||
self.cond = str(cond);
|
||||
self.cond = str(cond)
|
||||
|
||||
def getCondition(self) -> str:
|
||||
return self.cond;
|
||||
return self.cond
|
||||
|
||||
|
||||
class SqlConditionTrue(SqlConditionPrepared):
|
||||
"""True-Bedingung"""
|
||||
@ -196,12 +208,14 @@ class SqlConditionTrue(SqlConditionPrepared):
|
||||
def __init__(self) -> None:
|
||||
super().__init__("(1=1)")
|
||||
|
||||
|
||||
class SqlConditionFalse(SqlConditionPrepared):
|
||||
"""False-Bedingung"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__("(1=0)")
|
||||
|
||||
|
||||
class SqlConditionBool(SqlConditionPrepared):
|
||||
"""Fixe True-oder-False Bedingung"""
|
||||
|
||||
@ -211,6 +225,7 @@ class SqlConditionBool(SqlConditionPrepared):
|
||||
else:
|
||||
super().__init__(SqlConditionFalse())
|
||||
|
||||
|
||||
class SqlConditionNot(SqlCondition):
|
||||
"""
|
||||
Negation einer anderen Bedingung
|
||||
@ -220,10 +235,10 @@ class SqlConditionNot(SqlCondition):
|
||||
"""
|
||||
|
||||
def __init__(self, cond: SqlCondition):
|
||||
self.cond = cond;
|
||||
self.cond = cond
|
||||
|
||||
def getCondition(self) -> str:
|
||||
return "(not {})".format(self.cond.getCondition());
|
||||
return "(not {})".format(self.cond.getCondition())
|
||||
|
||||
|
||||
class SqlConditionIsNull(SqlConditionPrepared):
|
||||
@ -237,10 +252,12 @@ class SqlConditionIsNull(SqlConditionPrepared):
|
||||
def __init__(self, v: SqlValue):
|
||||
super().__init__("({} is null)".format(formatSqlValue(v)))
|
||||
|
||||
|
||||
class SqlConditionFieldIsNull(SqlConditionIsNull):
|
||||
def __init__(self, field: str):
|
||||
super().__init__(SqlField(field))
|
||||
|
||||
|
||||
class SqlConditionIsNotNull(SqlConditionPrepared):
|
||||
"""
|
||||
Wert soll nicht null sein
|
||||
@ -252,10 +269,12 @@ class SqlConditionIsNotNull(SqlConditionPrepared):
|
||||
def __init__(self, v: SqlValue):
|
||||
super().__init__("({} is not null)".format(formatSqlValue(v)))
|
||||
|
||||
|
||||
class SqlConditionFieldIsNotNull(SqlConditionIsNotNull):
|
||||
def __init__(self, field: str):
|
||||
super().__init__(SqlField(field))
|
||||
|
||||
|
||||
class SqlConditionStringStartsWith(SqlConditionPrepared):
|
||||
"""
|
||||
Feld soll mit einem bestimmten String beginnen
|
||||
@ -267,9 +286,9 @@ class SqlConditionStringStartsWith(SqlConditionPrepared):
|
||||
"""
|
||||
|
||||
def __init__(self, field: str, value: str):
|
||||
cond = "";
|
||||
cond = ""
|
||||
if value:
|
||||
cond="(left({}, {}) = {})".format(normaliseDBfield(field), len(value), formatSqlValueString(value));
|
||||
cond = "(left({}, {}) = {})".format(normaliseDBfield(field), len(value), formatSqlValueString(value))
|
||||
else:
|
||||
cond = "(1=1)"
|
||||
super().__init__(cond)
|
||||
@ -285,8 +304,8 @@ class SqlConditionFieldStringNotEmpty(SqlConditionPrepared):
|
||||
"""
|
||||
|
||||
def __init__(self, field: str):
|
||||
field = normaliseDBfield(field);
|
||||
cond="({} is not null and {} != '')".format(field, field);
|
||||
field = normaliseDBfield(field)
|
||||
cond = "({} is not null and {} != '')".format(field, field)
|
||||
super().__init__(cond)
|
||||
|
||||
|
||||
@ -312,6 +331,7 @@ class SqlConditionIn(SqlConditionPrepared):
|
||||
cond = "({} in ({}))".format(formatSqlValue(value), valuesS)
|
||||
super().__init__(cond)
|
||||
|
||||
|
||||
class SqlConditionFieldIn(SqlConditionIn):
|
||||
def __init__(self, field: str, values: Sequence[SqlValue]):
|
||||
super().__init__(SqlField(field), values)
|
||||
@ -324,7 +344,7 @@ class SqlConditionEq(SqlConditionPrepared):
|
||||
:param value1: 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]
|
||||
if (value1 is None) and (value2 is None):
|
||||
cond = SqlConditionTrue()
|
||||
@ -340,13 +360,13 @@ class SqlConditionEq(SqlConditionPrepared):
|
||||
cond = SqlConditionIsNull(value1)
|
||||
else:
|
||||
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):
|
||||
value2 = cast(SqlValue, value2)
|
||||
if value1:
|
||||
cond = "({} = 1)".format(formatSqlValue(value2))
|
||||
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):
|
||||
value1 = cast(SqlValue, value1)
|
||||
if value2:
|
||||
@ -356,7 +376,7 @@ class SqlConditionEq(SqlConditionPrepared):
|
||||
else:
|
||||
value1 = cast(SqlValue, value1)
|
||||
value2 = cast(SqlValue, value2)
|
||||
cond = "({} = {})".format(formatSqlValue(value1), formatSqlValue(value2));
|
||||
cond = "({} = {})".format(formatSqlValue(value1), formatSqlValue(value2))
|
||||
super().__init__(cond)
|
||||
|
||||
|
||||
@ -372,10 +392,10 @@ class SqlConditionBinComp(SqlConditionPrepared):
|
||||
:type 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")
|
||||
|
||||
cond = "({} {} {})".format(formatSqlValue(value1), op, formatSqlValue(value2));
|
||||
cond = "({} {} {})".format(formatSqlValue(value1), op, formatSqlValue(value2))
|
||||
super().__init__(cond)
|
||||
|
||||
|
||||
@ -389,6 +409,7 @@ class SqlConditionLt(SqlConditionBinComp):
|
||||
def __init__(self, value1: SqlValue, value2: SqlValue):
|
||||
super().__init__("<", value1, value2)
|
||||
|
||||
|
||||
class SqlConditionLe(SqlConditionBinComp):
|
||||
"""
|
||||
Bedingung der Form 'value1 <= value2'
|
||||
@ -399,6 +420,7 @@ class SqlConditionLe(SqlConditionBinComp):
|
||||
def __init__(self, value1: SqlValue, value2: SqlValue):
|
||||
super().__init__("<=", value1, value2)
|
||||
|
||||
|
||||
class SqlConditionGt(SqlConditionBinComp):
|
||||
"""
|
||||
Bedingung der Form 'value1 > value2'
|
||||
@ -409,6 +431,7 @@ class SqlConditionGt(SqlConditionBinComp):
|
||||
def __init__(self, value1: SqlValue, value2: SqlValue):
|
||||
super().__init__(">", value1, value2)
|
||||
|
||||
|
||||
class SqlConditionGe(SqlConditionBinComp):
|
||||
"""
|
||||
Bedingung der Form 'value1 >= value2'
|
||||
@ -421,25 +444,30 @@ class SqlConditionGe(SqlConditionBinComp):
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class SqlConditionFieldLt(SqlConditionLt):
|
||||
def __init__(self, field: str, value: SqlValue):
|
||||
super().__init__(SqlField(field), value)
|
||||
|
||||
|
||||
class SqlConditionFieldLe(SqlConditionLe):
|
||||
def __init__(self, field: str, value: SqlValue):
|
||||
super().__init__(SqlField(field), value)
|
||||
|
||||
|
||||
class SqlConditionFieldGt(SqlConditionGt):
|
||||
def __init__(self, field: str, value: SqlValue):
|
||||
super().__init__(SqlField(field), value)
|
||||
|
||||
|
||||
class SqlConditionFieldGe(SqlConditionGe):
|
||||
def __init__(self, field: str, value: SqlValue):
|
||||
super().__init__(SqlField(field), value)
|
||||
|
||||
|
||||
class SqlConditionList(SqlCondition):
|
||||
"""
|
||||
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):
|
||||
self.connector : str = connector;
|
||||
self.emptyCond : str = emptyCond;
|
||||
self.connector: str = connector
|
||||
self.emptyCond: str = emptyCond
|
||||
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):
|
||||
return
|
||||
if not (isinstance(cond, SqlCondition)):
|
||||
cond = SqlConditionPrepared("("+str(cond)+")");
|
||||
self.elems.append(cond);
|
||||
cond = SqlConditionPrepared("("+str(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:
|
||||
self.addCondition(cond)
|
||||
|
||||
def addConditionFieldStringNotEmpty(self, field: str) -> None:
|
||||
self.addCondition(SqlConditionFieldStringNotEmpty(field));
|
||||
self.addCondition(SqlConditionFieldStringNotEmpty(field))
|
||||
|
||||
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:
|
||||
self.addCondition(SqlConditionFieldEq(field, value));
|
||||
def addConditionFieldEq(self, field: str, value: Union[SqlValue, bool, None]) -> None:
|
||||
self.addCondition(SqlConditionFieldEq(field, value))
|
||||
|
||||
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:
|
||||
self.addCondition(SqlConditionEq(value1, value2));
|
||||
def addConditionEq(self, value1: Union[SqlValue, bool, None], value2: Union[SqlValue, bool, None]) -> None:
|
||||
self.addCondition(SqlConditionEq(value1, value2))
|
||||
|
||||
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:
|
||||
self.addCondition(SqlConditionGe(SqlField(field), value));
|
||||
self.addCondition(SqlConditionGe(SqlField(field), value))
|
||||
|
||||
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:
|
||||
self.addCondition(SqlConditionLe(value1, value2));
|
||||
self.addCondition(SqlConditionLe(value1, value2))
|
||||
|
||||
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:
|
||||
self.addCondition(SqlConditionLe(SqlField(field1), SqlField(field2)));
|
||||
self.addCondition(SqlConditionLe(SqlField(field1), SqlField(field2)))
|
||||
|
||||
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:
|
||||
self.addCondition(SqlConditionGt(SqlField(field), value));
|
||||
self.addCondition(SqlConditionGt(SqlField(field), value))
|
||||
|
||||
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:
|
||||
self.addCondition(SqlConditionLt(value1, value2));
|
||||
self.addCondition(SqlConditionLt(value1, value2))
|
||||
|
||||
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:
|
||||
self.addCondition(SqlConditionLt(SqlField(field1), SqlField(field2)));
|
||||
self.addCondition(SqlConditionLt(SqlField(field1), SqlField(field2)))
|
||||
|
||||
def addConditionFieldIsNull(self, field: str) -> None:
|
||||
self.addCondition(SqlConditionFieldIsNull(field));
|
||||
self.addCondition(SqlConditionFieldIsNull(field))
|
||||
|
||||
def addConditionFieldIsNotNull(self, field: str) -> None:
|
||||
self.addCondition(SqlConditionFieldIsNotNull(field));
|
||||
self.addCondition(SqlConditionFieldIsNotNull(field))
|
||||
|
||||
def isEmpty(self) -> bool:
|
||||
return not(self.elems);
|
||||
return not self.elems
|
||||
|
||||
def getCondition(self) -> str:
|
||||
match (len(self.elems)):
|
||||
case 0:
|
||||
return self.emptyCond;
|
||||
case 1:
|
||||
return self.elems[0].getCondition();
|
||||
case l:
|
||||
res = "(" + self.elems[0].getCondition();
|
||||
for i in range(1, l):
|
||||
elemLen = len(self.elems)
|
||||
if elemLen == 0:
|
||||
return self.emptyCond
|
||||
elif elemLen == 1:
|
||||
return self.elems[0].getCondition()
|
||||
else:
|
||||
res = "(" + self.elems[0].getCondition()
|
||||
for i in range(1, elemLen):
|
||||
res += " {} {}".format(self.connector, self.elems[i].getCondition())
|
||||
res += ")";
|
||||
return res;
|
||||
res += ")"
|
||||
return res
|
||||
|
||||
|
||||
class SqlConditionDateTimeFieldInRange(SqlConditionPrepared):
|
||||
@ -550,7 +578,7 @@ class SqlConditionDateTimeFieldInRange(SqlConditionPrepared):
|
||||
:param datetimeVon: der untere Wert (einschließ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()
|
||||
if not (datetimeVon is None):
|
||||
cond.addConditionFieldGe(field, datetimeVon)
|
||||
@ -558,6 +586,7 @@ class SqlConditionDateTimeFieldInRange(SqlConditionPrepared):
|
||||
cond.addConditionFieldLt(field, datetimeBis)
|
||||
super().__init__(str(cond))
|
||||
|
||||
|
||||
class SqlConditionDateTimeFieldInMonth(SqlConditionPrepared):
|
||||
"""
|
||||
Liegt Datetime in einem bestimmten Monat?
|
||||
@ -570,15 +599,17 @@ class SqlConditionDateTimeFieldInMonth(SqlConditionPrepared):
|
||||
def __init__(self, field: str, year: int, month: int):
|
||||
if month == 12:
|
||||
nyear = year+1
|
||||
nmonth=1;
|
||||
nmonth = 1
|
||||
else:
|
||||
nyear = year
|
||||
nmonth=month+1;
|
||||
cond = SqlConditionDateTimeFieldInRange(field,
|
||||
nmonth = month+1
|
||||
cond = SqlConditionDateTimeFieldInRange(
|
||||
field,
|
||||
datetime.datetime(year=year, month=month, day=1),
|
||||
datetime.datetime(year=nyear, month=nmonth, day=1))
|
||||
super().__init__(str(cond))
|
||||
|
||||
|
||||
class SqlConditionDateTimeFieldInYear(SqlConditionPrepared):
|
||||
"""
|
||||
Liegt Datetime in einem bestimmten Jahr?
|
||||
@ -589,11 +620,13 @@ class SqlConditionDateTimeFieldInYear(SqlConditionPrepared):
|
||||
"""
|
||||
def __init__(self, field: str, year: int) -> None:
|
||||
nyear = year+1
|
||||
cond = SqlConditionDateTimeFieldInRange(field,
|
||||
cond = SqlConditionDateTimeFieldInRange(
|
||||
field,
|
||||
datetime.datetime(year=year, month=1, day=1),
|
||||
datetime.datetime(year=nyear, month=1, day=1))
|
||||
super().__init__(str(cond))
|
||||
|
||||
|
||||
class SqlConditionDateTimeFieldInDay(SqlConditionPrepared):
|
||||
"""
|
||||
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:
|
||||
d = datetime.datetime(year=year, month=month, day=day)
|
||||
cond = SqlConditionDateTimeFieldInRange(field,
|
||||
cond = SqlConditionDateTimeFieldInRange(
|
||||
field,
|
||||
d,
|
||||
d + datetime.timedelta(days=1))
|
||||
super().__init__(str(cond))
|
||||
|
||||
|
||||
class SqlConditionAnd(SqlConditionList):
|
||||
def __init__(self, *conds: Union[SqlCondition, str]) -> None:
|
||||
super().__init__("AND", "(1=1)")
|
||||
self.addConditions(*conds)
|
||||
|
||||
|
||||
class SqlConditionOr(SqlConditionList):
|
||||
def __init__(self, *conds: Union[SqlCondition, str]) -> None:
|
||||
super().__init__("OR", "(1=0)")
|
||||
@ -645,10 +681,10 @@ class SqlJoin():
|
||||
"""
|
||||
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:
|
||||
return self.getJoin();
|
||||
return self.getJoin()
|
||||
|
||||
|
||||
class SqlInnerJoin(SqlJoin):
|
||||
@ -663,6 +699,7 @@ class SqlInnerJoin(SqlJoin):
|
||||
def __init__(self, table: str, *conds: Union[SqlCondition, str]) -> None:
|
||||
super().__init__("INNER JOIN", table, *conds)
|
||||
|
||||
|
||||
class SqlLeftJoin(SqlJoin):
|
||||
"""
|
||||
Ein Left-Join.
|
||||
@ -674,6 +711,7 @@ class SqlLeftJoin(SqlJoin):
|
||||
def __init__(self, table: str, *conds: Union[SqlCondition, str]) -> None:
|
||||
super().__init__("LEFT JOIN", table, *conds)
|
||||
|
||||
|
||||
class SqlStatementSelect():
|
||||
"""
|
||||
Klasse, um einfache Select-Statements zu bauen.
|
||||
@ -690,38 +728,38 @@ class SqlStatementSelect():
|
||||
self.top: int = 0
|
||||
"""wie viele Datensätze auswählen? 0 für alle"""
|
||||
|
||||
self.where : SqlConditionList = SqlConditionAnd();
|
||||
self.where: SqlConditionList = SqlConditionAnd()
|
||||
"""die Bedingung, Default ist True"""
|
||||
|
||||
self.fields: List[str] = []
|
||||
"""Liste von auszuwählenden Feldern"""
|
||||
self.addFields(*fields)
|
||||
|
||||
self.joins : List[SqlJoin|str] = []
|
||||
self.joins: List[Union[SqlJoin, str]] = []
|
||||
"""Joins mit extra Tabellen"""
|
||||
|
||||
self.groupBy : List[str] = [];
|
||||
self.groupBy: List[str] = []
|
||||
"""die Bedingung, Default ist True"""
|
||||
|
||||
self.having : SqlConditionList = SqlConditionAnd();
|
||||
self.having: SqlConditionList = SqlConditionAnd()
|
||||
"""die Bedingung having, Default ist True"""
|
||||
|
||||
self.order: Optional[str] = None
|
||||
"""Sortierung"""
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.getSql();
|
||||
return self.getSql()
|
||||
|
||||
def addFields(self, *fields: str) -> None:
|
||||
"""Fügt ein oder mehrere Felder, also auszuwählende Werte zu einem SQL-Statement hinzu."""
|
||||
for f in fields:
|
||||
if not (f == None):
|
||||
if not (f is None):
|
||||
self.fields.append(f)
|
||||
|
||||
def addGroupBy(self, *fields: str) -> None:
|
||||
"""Fügt ein oder mehrere GroupBy Felder zu einem SQL-Statement hinzu."""
|
||||
for f in fields:
|
||||
if not (f == None):
|
||||
if not (f is None):
|
||||
self.groupBy.append(f)
|
||||
|
||||
def setTop(self, t: int) -> None:
|
||||
@ -735,10 +773,10 @@ class SqlStatementSelect():
|
||||
Dies kann im Vergleich zu 'addFields' Schreibarbeit erleitern.
|
||||
"""
|
||||
for f in fields:
|
||||
if not (f == None):
|
||||
if not (f is None):
|
||||
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'"""
|
||||
self.joins.append(j)
|
||||
|
||||
@ -755,36 +793,35 @@ class SqlStatementSelect():
|
||||
def getSql(self) -> str:
|
||||
"""Liefert das SQL-SELECT-Statement als String"""
|
||||
def getFields() -> str:
|
||||
match (len(self.fields)):
|
||||
case 0:
|
||||
return "*";
|
||||
case 1:
|
||||
return str(self.fields[0]);
|
||||
case l:
|
||||
res = str(self.fields[0]);
|
||||
for i in range(1, l):
|
||||
res += ", " + str(self.fields[i]);
|
||||
return res;
|
||||
fieldsLen = len(self.fields)
|
||||
if fieldsLen == 0:
|
||||
return "*"
|
||||
elif fieldsLen == 1:
|
||||
return str(self.fields[0])
|
||||
else:
|
||||
res = str(self.fields[0])
|
||||
for i in range(1, fieldsLen):
|
||||
res += ", " + str(self.fields[i])
|
||||
return res
|
||||
|
||||
def getGroupBy() -> str:
|
||||
match (len(self.groupBy)):
|
||||
case 0:
|
||||
return "";
|
||||
case l:
|
||||
groupByLen = len(self.groupBy)
|
||||
if groupByLen == 0:
|
||||
return ""
|
||||
else:
|
||||
res = " GROUP BY " + str(self.fields[0])
|
||||
for i in range(1, l):
|
||||
res += ", " + str(self.fields[i]);
|
||||
for i in range(1, groupByLen):
|
||||
res += ", " + str(self.fields[i])
|
||||
if not (self.having.isEmpty()):
|
||||
res += " HAVING " + str(self.having)
|
||||
return res;
|
||||
return res
|
||||
|
||||
def getJoins() -> str:
|
||||
match (len(self.joins)):
|
||||
case 0:
|
||||
if (len(self.joins) == 0):
|
||||
return ""
|
||||
case l:
|
||||
res = "";
|
||||
for i in range(0, l):
|
||||
else:
|
||||
res = ""
|
||||
for i in range(0, len(self.joins)):
|
||||
res += " " + str(self.joins[i])
|
||||
return res
|
||||
|
||||
@ -795,7 +832,7 @@ class SqlStatementSelect():
|
||||
return " WHERE " + str(self.where)
|
||||
|
||||
def getOrder() -> str:
|
||||
if self.order == None:
|
||||
if self.order is None:
|
||||
return ""
|
||||
else:
|
||||
return " ORDER BY " + str(self.order)
|
||||
@ -806,8 +843,7 @@ class SqlStatementSelect():
|
||||
else:
|
||||
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]
|
||||
|
@ -6,11 +6,10 @@
|
||||
# license that can be found in the LICENSE file or at
|
||||
# https://opensource.org/licenses/MIT.
|
||||
|
||||
#-*- coding: utf-8 -*-
|
||||
|
||||
import pathlib
|
||||
import datetime
|
||||
from typing import *
|
||||
from typing import Set, Union
|
||||
|
||||
|
||||
def checkDirExists(dir: Union[str, pathlib.Path]) -> pathlib.Path:
|
||||
"""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()
|
||||
if not (dir.exists()):
|
||||
raise Exception("Verzeichnis '" + str(dir) + "' nicht gefunden");
|
||||
raise Exception("Verzeichnis '" + str(dir) + "' nicht gefunden")
|
||||
|
||||
if not (dir.is_dir()):
|
||||
raise Exception("'" + str(dir) + "' ist kein Verzeichnis");
|
||||
return dir;
|
||||
raise Exception("'" + str(dir) + "' ist kein Verzeichnis")
|
||||
return dir
|
||||
|
||||
|
||||
def formatDateTimeForAPplus(v: Union[datetime.datetime, datetime.date, datetime.time]) -> str:
|
||||
"""Formatiert ein Datum oder eine Uhrzeit für APplus"""
|
||||
if (v == None):
|
||||
return "";
|
||||
if v is None:
|
||||
return ""
|
||||
elif isinstance(v, str):
|
||||
return v;
|
||||
return v
|
||||
elif isinstance(v, datetime.datetime):
|
||||
return v.strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||
elif isinstance(v, datetime.date):
|
||||
@ -48,6 +47,7 @@ def formatDateTimeForAPplus(v : Union[datetime.datetime, datetime.date, datetime
|
||||
else:
|
||||
return str(v)
|
||||
|
||||
|
||||
def containsOnlyAllowedChars(charset: Set[str], s: str) -> bool:
|
||||
"""Enthält ein String nur erlaubte Zeichen?"""
|
||||
for c in s:
|
||||
|
@ -7,10 +7,10 @@
|
||||
# https://opensource.org/licenses/MIT.
|
||||
|
||||
from PyAPplus64 import applus_db
|
||||
import datetime
|
||||
|
||||
|
||||
def test_DBTableIDs1() -> None:
|
||||
ids = applus_db.DBTableIDs();
|
||||
ids = applus_db.DBTableIDs()
|
||||
assert (str(ids) == "{}")
|
||||
ids.add("t1", 1)
|
||||
assert (str(ids) == "{'T1': {1}}")
|
||||
|
@ -9,284 +9,350 @@
|
||||
from PyAPplus64 import sql_utils
|
||||
import datetime
|
||||
|
||||
|
||||
def test_normaliseDBField1() -> None:
|
||||
assert (sql_utils.normaliseDBfield("aAa") == "AAA")
|
||||
assert (sql_utils.normaliseDBfield("a#Aa") == "A#AA")
|
||||
assert (sql_utils.normaliseDBfield("2") == "2")
|
||||
|
||||
|
||||
def test_normaliseDBFieldSet() -> None:
|
||||
assert (sql_utils.normaliseDBfieldSet(set()) == set())
|
||||
assert (sql_utils.normaliseDBfieldSet({"aAa", "b", "c", "2"}) == {"2", "AAA", "B", "C"})
|
||||
|
||||
|
||||
def test_normaliseDBFieldList() -> None:
|
||||
assert (sql_utils.normaliseDBfieldList([]) == [])
|
||||
assert (sql_utils.normaliseDBfieldList(["aAa", "b", "c", "2"]) == ["AAA", "B", "C", "2"])
|
||||
|
||||
|
||||
def test_SqlField1() -> None:
|
||||
assert (str(sql_utils.SqlField("abc")) == "ABC")
|
||||
|
||||
|
||||
def test_SqlField2() -> None:
|
||||
assert (str(sql_utils.SqlField("t.abc")) == "T.ABC")
|
||||
|
||||
|
||||
def test_SqlParam() -> None:
|
||||
assert (str(sql_utils.sqlParam) == "?")
|
||||
|
||||
|
||||
def test_SqlDateTime() -> None:
|
||||
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")
|
||||
|
||||
|
||||
def test_SqlDate() -> None:
|
||||
dt = datetime.datetime(year=2023, month=1, day=12, hour=9, minute=59, second=12, microsecond=2344)
|
||||
assert (str(sql_utils.SqlDate(dt)) == "20230112")
|
||||
|
||||
|
||||
def test_formatSqlValueString1() -> None:
|
||||
assert(sql_utils.formatSqlValueString("") == "''");
|
||||
assert (sql_utils.formatSqlValueString("") == "''")
|
||||
|
||||
|
||||
def test_formatSqlValueString2() -> None:
|
||||
assert(sql_utils.formatSqlValueString("abc") == "'abc'");
|
||||
assert (sql_utils.formatSqlValueString("abc") == "'abc'")
|
||||
|
||||
|
||||
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:
|
||||
assert(sql_utils.formatSqlValueString("a \"b\" c") == "'a \"b\" c'");
|
||||
assert (sql_utils.formatSqlValueString("a \"b\" c") == "'a \"b\" c'")
|
||||
|
||||
|
||||
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:
|
||||
assert(sql_utils.formatSqlValue(2) == "2");
|
||||
assert (sql_utils.formatSqlValue(2) == "2")
|
||||
|
||||
|
||||
def test_formatSqlValue2() -> None:
|
||||
assert(sql_utils.formatSqlValue(2.4) == "2.4");
|
||||
assert (sql_utils.formatSqlValue(2.4) == "2.4")
|
||||
|
||||
|
||||
def test_formatSqlValue3() -> None:
|
||||
assert(sql_utils.formatSqlValue("AA") == "'AA'");
|
||||
assert (sql_utils.formatSqlValue("AA") == "'AA'")
|
||||
|
||||
|
||||
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:
|
||||
assert(sql_utils.formatSqlValue(0) == "0");
|
||||
assert (sql_utils.formatSqlValue(0) == "0")
|
||||
|
||||
|
||||
def test_formatSqlValue6() -> None:
|
||||
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:
|
||||
assert(str(sql_utils.SqlConditionTrue()) == "(1=1)");
|
||||
assert (str(sql_utils.SqlConditionTrue()) == "(1=1)")
|
||||
|
||||
|
||||
def test_SqlConditionFalse() -> None:
|
||||
assert(str(sql_utils.SqlConditionFalse()) == "(1=0)");
|
||||
assert (str(sql_utils.SqlConditionFalse()) == "(1=0)")
|
||||
|
||||
|
||||
def test_SqlConditionBool1() -> None:
|
||||
assert(str(sql_utils.SqlConditionBool(True)) == "(1=1)");
|
||||
assert (str(sql_utils.SqlConditionBool(True)) == "(1=1)")
|
||||
|
||||
|
||||
def test_SqlConditionBool2() -> None:
|
||||
assert(str(sql_utils.SqlConditionBool(False)) == "(1=0)");
|
||||
assert (str(sql_utils.SqlConditionBool(False)) == "(1=0)")
|
||||
|
||||
|
||||
def test_SqlConditionIsNull() -> None:
|
||||
cond = sql_utils.SqlConditionIsNull("AA");
|
||||
assert(str(cond) == "('AA' is null)");
|
||||
cond = sql_utils.SqlConditionIsNull("AA")
|
||||
assert (str(cond) == "('AA' is null)")
|
||||
|
||||
|
||||
def test_SqlConditionIsNotNull() -> None:
|
||||
cond = sql_utils.SqlConditionIsNotNull("AA");
|
||||
assert(str(cond) == "('AA' is not null)");
|
||||
cond = sql_utils.SqlConditionIsNotNull("AA")
|
||||
assert (str(cond) == "('AA' is not null)")
|
||||
|
||||
|
||||
def test_SqlConditionNot() -> None:
|
||||
cond1 = sql_utils.SqlConditionIsNull("AA");
|
||||
cond = sql_utils.SqlConditionNot(cond1);
|
||||
assert(str(cond) == "(not ('AA' is null))");
|
||||
cond1 = sql_utils.SqlConditionIsNull("AA")
|
||||
cond = sql_utils.SqlConditionNot(cond1)
|
||||
assert (str(cond) == "(not ('AA' is null))")
|
||||
|
||||
|
||||
def test_SqlConditionStringStartsWith() -> None:
|
||||
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:
|
||||
cond = sql_utils.SqlConditionIn(sql_utils.SqlField("f"), [])
|
||||
assert(str(cond) == "(1=0)");
|
||||
assert (str(cond) == "(1=0)")
|
||||
|
||||
|
||||
def test_SqlConditionIn2() -> None:
|
||||
cond = sql_utils.SqlConditionIn(sql_utils.SqlField("f"), ["a"])
|
||||
assert(str(cond) == "(F = 'a')");
|
||||
assert (str(cond) == "(F = 'a')")
|
||||
|
||||
|
||||
def test_SqlConditionIn3() -> None:
|
||||
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:
|
||||
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:
|
||||
cond = sql_utils.SqlConditionEq("f1", None)
|
||||
assert(str(cond) == "('f1' is null)");
|
||||
assert (str(cond) == "('f1' is null)")
|
||||
|
||||
|
||||
def test_SqlConditionEq2() -> None:
|
||||
cond = sql_utils.SqlConditionEq(None, "f1")
|
||||
assert(str(cond) == "('f1' is null)");
|
||||
assert (str(cond) == "('f1' is null)")
|
||||
|
||||
|
||||
def test_SqlConditionEq3() -> None:
|
||||
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:
|
||||
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:
|
||||
cond = sql_utils.SqlConditionEq(sql_utils.SqlField("f1"), 2)
|
||||
assert(str(cond) == "(F1 = 2)");
|
||||
assert (str(cond) == "(F1 = 2)")
|
||||
|
||||
|
||||
def test_SqlConditionEq6() -> None:
|
||||
cond = sql_utils.SqlConditionEq(sql_utils.SqlField("f1"), True)
|
||||
assert(str(cond) == "(F1 = 1)");
|
||||
assert (str(cond) == "(F1 = 1)")
|
||||
|
||||
|
||||
def test_SqlConditionEq7() -> None:
|
||||
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:
|
||||
cond = sql_utils.SqlConditionEq(True, sql_utils.SqlField("f1"))
|
||||
assert(str(cond) == "(F1 = 1)");
|
||||
assert (str(cond) == "(F1 = 1)")
|
||||
|
||||
|
||||
def test_SqlConditionEq9() -> None:
|
||||
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:
|
||||
cond = sql_utils.SqlConditionEq(False, True)
|
||||
assert(str(cond) == "(1=0)");
|
||||
assert (str(cond) == "(1=0)")
|
||||
|
||||
|
||||
def test_SqlConditionEq11() -> None:
|
||||
cond = sql_utils.SqlConditionEq(True, True)
|
||||
assert(str(cond) == "(1=1)");
|
||||
assert (str(cond) == "(1=1)")
|
||||
|
||||
|
||||
def test_SqlConditionFieldEq1() -> None:
|
||||
cond = sql_utils.SqlConditionFieldEq("f1", None)
|
||||
assert(str(cond) == "(F1 is null)");
|
||||
assert (str(cond) == "(F1 is null)")
|
||||
|
||||
|
||||
def test_SqlConditionFieldEq2() -> None:
|
||||
cond = sql_utils.SqlConditionFieldEq("f1", sql_utils.SqlField("f2"))
|
||||
assert(str(cond) == "(F1 = F2)");
|
||||
assert (str(cond) == "(F1 = F2)")
|
||||
|
||||
|
||||
def test_SqlConditionFieldEq3() -> None:
|
||||
cond = sql_utils.SqlConditionFieldEq("f1", "aa'a")
|
||||
assert(str(cond) == "(F1 = 'aa''a')");
|
||||
assert (str(cond) == "(F1 = 'aa''a')")
|
||||
|
||||
|
||||
def test_SqlConditionFieldEq4() -> None:
|
||||
cond = sql_utils.SqlConditionFieldEq("f1", 2)
|
||||
assert(str(cond) == "(F1 = 2)");
|
||||
assert (str(cond) == "(F1 = 2)")
|
||||
|
||||
|
||||
def test_SqlConditionFieldEq5() -> None:
|
||||
cond = sql_utils.SqlConditionFieldEq("f1", sql_utils.sqlParam)
|
||||
assert(str(cond) == "(F1 = ?)");
|
||||
assert (str(cond) == "(F1 = ?)")
|
||||
|
||||
|
||||
def test_SqlConditionLt1() -> None:
|
||||
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:
|
||||
cond = sql_utils.SqlConditionLt(2, sql_utils.SqlField("f"))
|
||||
assert(str(cond) == "(2 < F)");
|
||||
assert (str(cond) == "(2 < F)")
|
||||
|
||||
|
||||
def test_SqlConditionGt1() -> None:
|
||||
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:
|
||||
cond = sql_utils.SqlConditionGt(2, sql_utils.SqlField("f"))
|
||||
assert(str(cond) == "(2 > F)");
|
||||
assert (str(cond) == "(2 > F)")
|
||||
|
||||
|
||||
def test_SqlConditionLe1() -> None:
|
||||
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:
|
||||
cond = sql_utils.SqlConditionLe(2, sql_utils.SqlField("f"))
|
||||
assert(str(cond) == "(2 <= F)");
|
||||
assert (str(cond) == "(2 <= F)")
|
||||
|
||||
|
||||
def test_SqlConditionGe1() -> None:
|
||||
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:
|
||||
cond = sql_utils.SqlConditionGe(2, sql_utils.SqlField("f"))
|
||||
assert(str(cond) == "(2 >= F)");
|
||||
assert (str(cond) == "(2 >= F)")
|
||||
|
||||
|
||||
def test_SqlConditionFieldLt1() -> None:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
conj = sql_utils.SqlConditionAnd();
|
||||
assert(str(conj) == "(1=1)");
|
||||
conj = sql_utils.SqlConditionAnd()
|
||||
assert (str(conj) == "(1=1)")
|
||||
|
||||
|
||||
def test_SqlConditionAnd2() -> None:
|
||||
cond1 = sql_utils.SqlConditionPrepared("cond1");
|
||||
conj = sql_utils.SqlConditionAnd();
|
||||
cond1 = sql_utils.SqlConditionPrepared("cond1")
|
||||
conj = sql_utils.SqlConditionAnd()
|
||||
conj.addCondition(cond1)
|
||||
assert(str(conj) == "cond1");
|
||||
assert (str(conj) == "cond1")
|
||||
|
||||
|
||||
def test_SqlConditionAnd3() -> None:
|
||||
cond1 = sql_utils.SqlConditionPrepared("cond1");
|
||||
cond2 = sql_utils.SqlConditionPrepared("cond2");
|
||||
conj = sql_utils.SqlConditionAnd();
|
||||
cond1 = sql_utils.SqlConditionPrepared("cond1")
|
||||
cond2 = sql_utils.SqlConditionPrepared("cond2")
|
||||
conj = sql_utils.SqlConditionAnd()
|
||||
conj.addCondition(cond1)
|
||||
conj.addCondition(cond2)
|
||||
assert(str(conj) == "(cond1 AND cond2)");
|
||||
assert (str(conj) == "(cond1 AND cond2)")
|
||||
|
||||
|
||||
def test_SqlConditionAnd4() -> None:
|
||||
cond1 = sql_utils.SqlConditionPrepared("cond1");
|
||||
cond2 = sql_utils.SqlConditionPrepared("cond2");
|
||||
cond3 = sql_utils.SqlConditionPrepared("cond3");
|
||||
conj = sql_utils.SqlConditionAnd();
|
||||
cond1 = sql_utils.SqlConditionPrepared("cond1")
|
||||
cond2 = sql_utils.SqlConditionPrepared("cond2")
|
||||
cond3 = sql_utils.SqlConditionPrepared("cond3")
|
||||
conj = sql_utils.SqlConditionAnd()
|
||||
conj.addCondition(cond1)
|
||||
conj.addCondition(cond2)
|
||||
conj.addCondition(cond3)
|
||||
assert(str(conj) == "(cond1 AND cond2 AND cond3)");
|
||||
assert (str(conj) == "(cond1 AND cond2 AND cond3)")
|
||||
|
||||
|
||||
def test_SqlConditionOr1() -> None:
|
||||
conj = sql_utils.SqlConditionOr();
|
||||
assert(str(conj) == "(1=0)");
|
||||
conj = sql_utils.SqlConditionOr()
|
||||
assert (str(conj) == "(1=0)")
|
||||
|
||||
|
||||
def test_SqlConditionOr2() -> None:
|
||||
cond1 = sql_utils.SqlConditionPrepared("cond1");
|
||||
conj = sql_utils.SqlConditionOr();
|
||||
cond1 = sql_utils.SqlConditionPrepared("cond1")
|
||||
conj = sql_utils.SqlConditionOr()
|
||||
conj.addCondition(cond1)
|
||||
assert(str(conj) == "cond1");
|
||||
assert (str(conj) == "cond1")
|
||||
|
||||
|
||||
def test_SqlConditionOr3() -> None:
|
||||
cond1 = sql_utils.SqlConditionPrepared("cond1");
|
||||
cond2 = sql_utils.SqlConditionPrepared("cond2");
|
||||
conj = sql_utils.SqlConditionOr();
|
||||
cond1 = sql_utils.SqlConditionPrepared("cond1")
|
||||
cond2 = sql_utils.SqlConditionPrepared("cond2")
|
||||
conj = sql_utils.SqlConditionOr()
|
||||
conj.addCondition(cond1)
|
||||
conj.addCondition(cond2)
|
||||
assert(str(conj) == "(cond1 OR cond2)");
|
||||
assert (str(conj) == "(cond1 OR cond2)")
|
||||
|
||||
|
||||
def test_SqlConditionOr4() -> None:
|
||||
cond1 = sql_utils.SqlConditionPrepared("cond1");
|
||||
cond2 = sql_utils.SqlConditionPrepared("cond2");
|
||||
cond3 = sql_utils.SqlConditionPrepared("cond3");
|
||||
conj = sql_utils.SqlConditionOr();
|
||||
cond1 = sql_utils.SqlConditionPrepared("cond1")
|
||||
cond2 = sql_utils.SqlConditionPrepared("cond2")
|
||||
cond3 = sql_utils.SqlConditionPrepared("cond3")
|
||||
conj = sql_utils.SqlConditionOr()
|
||||
conj.addCondition(cond1)
|
||||
conj.addCondition(cond2)
|
||||
conj.addCondition(cond3)
|
||||
assert(str(conj) == "(cond1 OR cond2 OR cond3)");
|
||||
assert (str(conj) == "(cond1 OR cond2 OR cond3)")
|
||||
|
||||
|
||||
def test_SqlStatementSelect1() -> None:
|
||||
sql = sql_utils.SqlStatementSelect("tabelle t")
|
||||
@ -323,6 +389,7 @@ def test_SqlStatementSelect2() -> None:
|
||||
sql.addJoin("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:
|
||||
sql = sql_utils.SqlStatementSelect("t")
|
||||
sql.where.addCondition("cond1")
|
||||
@ -331,9 +398,10 @@ def test_SqlStatementSelect4() -> None:
|
||||
sql.where.addCondition("cond2")
|
||||
assert (str(sql) == "SELECT * FROM t WHERE ((cond1) AND (cond2))")
|
||||
|
||||
|
||||
def test_SqlStatementSelect5() -> None:
|
||||
sql = sql_utils.SqlStatementSelect("t")
|
||||
cond = sql_utils.SqlConditionOr();
|
||||
cond = sql_utils.SqlConditionOr()
|
||||
sql.where.addCondition(cond)
|
||||
cond.addCondition("cond1")
|
||||
assert (str(sql) == "SELECT * FROM t WHERE (cond1)")
|
||||
@ -341,9 +409,10 @@ def test_SqlStatementSelect5() -> None:
|
||||
cond.addCondition("cond2")
|
||||
assert (str(sql) == "SELECT * FROM t WHERE ((cond1) OR (cond2))")
|
||||
|
||||
|
||||
def test_SqlStatementSelect6() -> None:
|
||||
sql = sql_utils.SqlStatementSelect("t")
|
||||
sql.where = sql_utils.SqlConditionOr();
|
||||
sql.where = sql_utils.SqlConditionOr()
|
||||
sql.where.addCondition("cond1")
|
||||
assert (str(sql) == "SELECT * FROM t WHERE (cond1)")
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user