Beispiele

Im Verzeichnis examples finden sich Python Dateien, die die Verwendung von PyAPplus64 demonstrieren.

Config-Dateien

Viele Scripte teilen sich Einstellungen. Beispielsweise greifen fast alle Scripte irgendwie auf APplus zu und benötigen Informationen, mit welchem APP-Server, welchem Web-Server und welcher Datenbank sie sich verbinden sollen. Solche Informationen, insbesondere die Passwörter, werden nicht in jedem Script gespeichert, sondern nur in den Config-Dateien. Es bietet sich wohl meist an, 3 Konfigdateien zu erstellen, je eine für das Deploy-, das Test- und das Prod-System. Ein Beispiel ist im Unterverzeichnis examples/applus-server.yaml zu finden.

 1# Einstellung für die Verbindung mit dem APP-, Web- und DB-Server.
 2# Viele der Einstellungen sind im APplus Manager zu finden
 3
 4appserver : {
 5  server : "some-server",
 6  port : 2037,
 7  user : "asol.projects",
 8  env  : "default-umgebung" # hier wirklich Umgebung, nicht Mandant verwenden
 9}
10webserver : {
11  baseurl : "http://some-server/APplusProd6/",
12  user : null, # oft "ASOL.Projects", wenn nicht gesetzt, wird aktueller Windows-Nutzer verwendet
13  userDomain : null, # Domain für ASOL.PROJECTS
14  password : null # das Passwort
15}
16dbserver : {
17  server : "some-server",
18  db : "APplusProd6",
19  user : "SA",
20  password : "your-db-password"
21}

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 Datei wird in allen Scripten importiert, so dass das Config-Verzeichnis und die darin enthaltenen Configs einfach zur Verfügung stehen. Zudem werden in dieser Datei auch alle verwendeten Kombinationen aus System und Umgebung hinterlegt. So kann in Scripten auch eine Auswahl des Systems implementiert werden.

 1import pathlib
 2from PyAPplus64.applus import APplusServerConfigDescription
 3
 4basedir = basedir = pathlib.Path(__file__) # Adapt to your needs
 5configdir = basedir.joinpath("config")
 6
 7serverConfYamlDeploy = configdir.joinpath("applus-server-deploy.yaml")
 8serverConfYamlTest = configdir.joinpath("applus-server-test.yaml")
 9serverConfYamlProd = configdir.joinpath("applus-server-prod.yaml")
10
11
12serverConfDescProdEnv1 = APplusServerConfigDescription("Prod/Env1", serverConfYamlProd, env="Env1")
13serverConfDescProdEnv2 = APplusServerConfigDescription("Prod/Env2", serverConfYamlProd, env="Env2")
14serverConfDescTestEnv1 = APplusServerConfigDescription("Test/Env1", serverConfYamlTest, env="Env1")
15serverConfDescTestEnv2 = APplusServerConfigDescription("Test/Env2", serverConfYamlTest, env="Env2")
16serverConfDescDeploy = APplusServerConfigDescription("Deploy", serverConfYamlDeploy)
17
18serverConfDescs = [
19  serverConfDescProdEnv1,
20  serverConfDescProdEnv2,
21  serverConfDescTestEnv1,
22  serverConfDescTestEnv2,
23  serverConfDescDeploy
24]

read_settings.py

Einfaches Beispiel für Auslesen der SysConf und bestimmter Einstellungen.

 1# Einfaches Script, das verschiedene Werte des Servers ausliest.
 2# Dies sind SysConfig-Einstellungen, aber auch der aktuelle Mandant,
 3# Systemnamen, ...
 4
 5import pathlib
 6import PyAPplus64
 7import applus_configs
 8from typing import Optional, Union
 9
10
11def main(confFile: Union[str, pathlib.Path], user: Optional[str] = None, env: Optional[str] = None) -> None:
12    server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env)
13
14    print("\n\nSysConf Lookups:")
15
16    print("  Default Auftragsart:", server.sysconf.getString("STAMM", "DEFAULTAUFTRAGSART"))
17    print("  Auftragsarten:")
18    arten = server.sysconf.getList("STAMM", "AUFTRAGSART", sep='\n')
19    if not arten:
20        arten = []
21    for a in arten:
22        print("    - " + a)
23
24    print("  Firmen-Nr. automatisch vergeben:", server.sysconf.getBoolean("STAMM", "FIRMAAUTOMATIK"))
25    print("  Anzahl Artikelstellen:", server.sysconf.getInt("STAMM", "ARTKLASSIFNRLAENGE"))
26
27    print("\n\nScriptTool:")
28
29    print("  CurrentDate:", server.scripttool.getCurrentDate())
30    print("  CurrentTime:", server.scripttool.getCurrentTime())
31    print("  CurrentDateTime:", server.scripttool.getCurrentDateTime())
32    print("  LoginName:", server.scripttool.getLoginName())
33    print("  UserName:", server.scripttool.getUserName())
34    print("  UserFullName:", server.scripttool.getUserFullName())
35    print("  SystemName:", server.scripttool.getSystemName())
36    print("  Mandant:", server.scripttool.getMandant())
37    print("  MandantName:", server.scripttool.getMandantName())
38    print("  InstallPath:", server.scripttool.getInstallPath())
39    print("  InstallPathAppServer:", server.scripttool.getInstallPathAppServer())
40    print("  InstallPathWebServer:", server.scripttool.getInstallPathWebServer())
41    print("  ServerInfo - Version:", server.scripttool.getServerInfo().find("version").text)
42
43    client = server.getWebClient("dbenv/dbenv.asmx")
44    print("WEB Environment:", client.service.getEnvironment())
45
46if __name__ == "__main__":
47    main(applus_configs.serverConfYamlTest)

check_dokumente.py

Einfaches Beispiel für lesenden und schreibenden Zugriff auf APplus Datenbank.

 1import pathlib
 2import PyAPplus64
 3import applus_configs
 4from typing import Optional
 5
 6
 7def main(confFile: pathlib.Path, updateDB: bool, docDir: Optional[str] = None) -> None:
 8    server = PyAPplus64.applus.applusFromConfigFile(confFile)
 9
10    if docDir is None:
11        docDir = str(server.scripttool.getInstallPathWebServer().joinpath("DocLib"))
12
13    sql = PyAPplus64.sql_utils.SqlStatementSelect("ARTIKEL")
14    sql.addFields("ID", "ARTIKEL", "DOCUMENTS")
15    sql.where.addConditionFieldStringNotEmpty("DOCUMENTS")
16
17    for row in server.dbQueryAll(sql):
18        doc = pathlib.Path(docDir + row.DOCUMENTS)
19        if not doc.exists():
20            print("Bild '{}' für Artikel '{}' nicht gefunden".format(doc, row.ARTIKEL))
21
22            if updateDB:
23                upd = server.mkUseXMLRowUpdate("ARTIKEL", row.ID)
24                upd.addField("DOCUMENTS", None)
25                upd.update()
26
27
28if __name__ == "__main__":
29    main(applus_configs.serverConfYamlTest, False)

adhoc_report.py

Sehr einfaches Beispiel zur Erstellung einer Excel-Tabelle aus einer SQL-Abfrage.

 1import PyAPplus64
 2import applus_configs
 3import pathlib
 4
 5
 6def main(confFile: pathlib.Path, outfile: str) -> None:
 7    server = PyAPplus64.applus.applusFromConfigFile(confFile)
 8
 9    # Einfache SQL-Anfrage
10    sql1 = ("select Material, count(*) as Anzahl from ARTIKEL "
11            "group by MATERIAL having MATERIAL is not null "
12            "order by Anzahl desc")
13    df1 = PyAPplus64.pandas.pandasReadSql(server, sql1)
14
15    # Sql Select-Statements können auch über SqlStatementSelect zusammengebaut
16    # werden. Die ist bei vielen, komplizierten Bedingungen teilweise hilfreich.
17    sql2 = PyAPplus64.SqlStatementSelect("ARTIKEL")
18    sql2.addFields("Material", "count(*) as Anzahl")
19    sql2.addGroupBy("MATERIAL")
20    sql2.having.addConditionFieldIsNotNull("MATERIAL")
21    sql2.order = "Anzahl desc"
22    df2 = PyAPplus64.pandas.pandasReadSql(server, sql2)
23
24    # Ausgabe als Excel mit 2 Blättern
25    PyAPplus64.pandas.exportToExcel(outfile, [(df1, "Materialien"), (df2, "Materialien 2")], addTable=True)
26
27
28if __name__ == "__main__":
29    main(applus_configs.serverConfYamlTest, "myout.xlsx")

mengenabweichung.py

Etwas komplizierteres Beispiel zur Erstellung einer Excel-Datei aus SQL-Abfragen.

  1# Erzeugt Excel-Tabellen mit Werkstattaufträgen und Werkstattauftragspositionen mit Mengenabweichungen
  2
  3import datetime
  4import PyAPplus64
  5import applus_configs
  6import pandas as pd  # type: ignore
  7import pathlib
  8from typing import Tuple, Union, Optional
  9
 10
 11def ladeAlleWerkstattauftragMengenabweichungen(
 12        server: PyAPplus64.APplusServer,
 13        cond: Union[PyAPplus64.SqlCondition, str, None] = None) -> pd.DataFrame:
 14    sql = PyAPplus64.sql_utils.SqlStatementSelect("WAUFTRAG w")
 15    sql.addLeftJoin("personal p", "w.UPDUSER = p.PERSONAL")
 16
 17    sql.addFieldsTable("w", "ID", "BAUFTRAG", "POSITION")
 18    sql.addFields("(w.MENGE-w.MENGE_IST) as MENGENABWEICHUNG")
 19    sql.addFieldsTable("w", "MENGE", "MENGE_IST",
 20                       "APLAN as ARTIKEL", "NAME as ARTIKELNAME")
 21    sql.addFields("w.UPDDATE", "p.NAME as UPDNAME")
 22
 23    sql.where.addConditionFieldGe("w.STATUS", 5)
 24    sql.where.addCondition("abs(w.MENGE-w.MENGE_IST) > 0.001")
 25    sql.where.addCondition(cond)
 26    sql.order = "w.UPDDATE"
 27    dfOrg = PyAPplus64.pandas.pandasReadSql(server, sql)
 28
 29    # Add Links
 30    df = dfOrg.copy()
 31    df = df.drop(columns=["ID"])
 32    # df = df[['POSITION', 'BAUFTRAG', 'MENGE']] # reorder / filter columns
 33
 34    df['POSITION'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
 35                        dfOrg,
 36                        lambda r: r.POSITION,
 37                        lambda r: server.makeWebLinkWauftrag(
 38                            bauftrag=r.BAUFTRAG, accessid=r.ID))
 39    df['BAUFTRAG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
 40                        dfOrg,
 41                        lambda r: r.BAUFTRAG,
 42                        lambda r: server.makeWebLinkBauftrag(bauftrag=r.BAUFTRAG))
 43
 44    colNames = {
 45        "BAUFTRAG": "Betriebsauftrag",
 46        "POSITION": "Pos",
 47        "MENGENABWEICHUNG": "Mengenabweichung",
 48        "MENGE": "Menge",
 49        "MENGE_IST": "Menge-Ist",
 50        "ARTIKEL": "Artikel",
 51        "ARTIKELNAME": "Artikel-Name",
 52        "UPDDATE": "geändert am",
 53        "UPDNAME": "geändert von"
 54    }
 55    df.rename(columns=colNames, inplace=True)
 56
 57    return df
 58
 59
 60def ladeAlleWerkstattauftragPosMengenabweichungen(
 61        server: PyAPplus64.APplusServer,
 62        cond: Union[PyAPplus64.SqlCondition, str, None] = None) -> pd.DataFrame:
 63    sql = PyAPplus64.sql_utils.SqlStatementSelect("WAUFTRAGPOS w")
 64    sql.addLeftJoin("personal p", "w.UPDUSER = p.PERSONAL")
 65
 66    sql.addFieldsTable("w", "ID", "BAUFTRAG", "POSITION", "AG")
 67    sql.addFields("(w.MENGE-w.MENGE_IST) as MENGENABWEICHUNG")
 68    sql.addFieldsTable("w", "MENGE", "MENGE_IST", "APLAN as ARTIKEL")
 69    sql.addFields("w.UPDDATE", "p.NAME as UPDNAME")
 70
 71    sql.where.addConditionFieldEq("w.STATUS", 4)
 72    sql.where.addCondition("abs(w.MENGE-w.MENGE_IST) > 0.001")
 73    sql.where.addCondition(cond)
 74    sql.order = "w.UPDDATE"
 75
 76    dfOrg = PyAPplus64.pandas.pandasReadSql(server, sql)
 77
 78    # Add Links
 79    df = dfOrg.copy()
 80    df = df.drop(columns=["ID"])
 81    df['POSITION'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
 82                        dfOrg,
 83                        lambda r: r.POSITION,
 84                        lambda r: server.makeWebLinkWauftrag(
 85                            bauftrag=r.BAUFTRAG, accessid=r.ID))
 86    df['BAUFTRAG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
 87                        dfOrg,
 88                        lambda r: r.BAUFTRAG,
 89                        lambda r: server.makeWebLinkBauftrag(bauftrag=r.BAUFTRAG))
 90    df['AG'] = PyAPplus64.pandas.mkHyperlinkDataframeColumn(
 91                        dfOrg,
 92                        lambda r: r.AG,
 93                        lambda r: server.makeWebLinkWauftragPos(
 94                            bauftrag=r.BAUFTRAG, position=r.POSITION, accessid=r.ID))
 95
 96    # Demo zum Hinzufügen einer berechneten Spalte
 97    # df['BAUFPOSAG'] = PyAPplus64.pandas.mkDataframeColumn(dfOrg,
 98    #                     lambda r: "{}.{} AG {}".format(r.BAUFTRAG, r.POSITION, r.AG))
 99
100    # Rename Columns
101    colNames = {
102        "BAUFTRAG": "Betriebsauftrag",
103        "POSITION": "Pos",
104        "AG": "AG",
105        "MENGENABWEICHUNG": "Mengenabweichung",
106        "MENGE": "Menge",
107        "MENGE_IST": "Menge-Ist",
108        "ARTIKEL": "Artikel",
109        "UPDDATE": "geändert am",
110        "UPDNAME": "geändert von"
111    }
112    df.rename(columns=colNames, inplace=True)
113    return df
114
115
116def computeInYearMonthCond(field: str, year: Optional[int] = None,
117                           month: Optional[int] = None) -> Optional[PyAPplus64.SqlCondition]:
118    if not (year is None):
119        if month is None:
120            return PyAPplus64.sql_utils.SqlConditionDateTimeFieldInYear(field, year)
121        else:
122            return PyAPplus64.sql_utils.SqlConditionDateTimeFieldInMonth(field, year, month)
123    else:
124        return None
125
126
127def computeFileName(year: Optional[int] = None, month: Optional[int] = None) -> str:
128    if year is None:
129        return 'mengenabweichungen-all.xlsx'
130    else:
131        if month is None:
132            return 'mengenabweichungen-{:04d}.xlsx'.format(year)
133        else:
134            return 'mengenabweichungen-{:04d}-{:02d}.xlsx'.format(year, month)
135
136
137def _exportInternal(server: PyAPplus64.APplusServer, fn: str,
138                    cond: Union[PyAPplus64.SqlCondition, str, None]) -> int:
139    df1 = ladeAlleWerkstattauftragMengenabweichungen(server, cond)
140    df2 = ladeAlleWerkstattauftragPosMengenabweichungen(server, cond)
141    print("erzeuge " + fn)
142    PyAPplus64.pandas.exportToExcel(fn, [(df1, "WAuftrag"), (df2, "WAuftrag-Pos")], addTable=True)
143    return len(df1.index) + len(df2.index)
144
145
146def exportVonBis(server: PyAPplus64.APplusServer, fn: str,
147                 von: Optional[datetime.datetime], bis: Optional[datetime.datetime]) -> int:
148    cond = PyAPplus64.sql_utils.SqlConditionDateTimeFieldInRange("w.UPDDATE", von, bis)
149    return _exportInternal(server, fn, cond)
150
151
152def exportYearMonth(server: PyAPplus64.APplusServer,
153                    year: Optional[int] = None, month: Optional[int] = None) -> int:
154    cond = computeInYearMonthCond("w.UPDDATE", year=year, month=month)
155    fn = computeFileName(year=year, month=month)
156    return _exportInternal(server, fn, cond)
157
158
159def computePreviousMonthYear(cyear: int, cmonth: int) -> Tuple[int, int]:
160    if cmonth == 1:
161        return (cyear-1, 12)
162    else:
163        return (cyear, cmonth-1)
164
165
166def computeNextMonthYear(cyear: int, cmonth: int) -> Tuple[int, int]:
167    if cmonth == 12:
168        return (cyear+1, 1)
169    else:
170        return (cyear, cmonth+1)
171
172
173def main(confFile: Union[str, pathlib.Path], user: Optional[str] = None, env: Optional[str] = None) -> None:
174    server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env)
175
176    now = datetime.date.today()
177    (cmonth, cyear) = (now.month, now.year)
178    (pyear, pmonth) = computePreviousMonthYear(cyear, cmonth)
179
180    # Ausgaben
181    exportYearMonth(server, cyear, cmonth)  # Aktueller Monat
182    exportYearMonth(server, pyear, pmonth)  # Vorheriger Monat
183    # export(cyear) # aktuelles Jahr
184    # export(cyear-1) # letztes Jahr
185    # export() # alles
186
187
188if __name__ == "__main__":
189    main(applus_configs.serverConfYamlTest)

mengenabweichung_gui.py

Beispiel für eine sehr einfache GUI, die die Eingabe einfacher Parameter erlaubt. Die GUI wird um die Erzeugung von Excel-Dateien mit Mengenabweichungen gebaut.

 1import PySimpleGUI as sg  # type: ignore
 2import mengenabweichung
 3import datetime
 4import PyAPplus64
 5import applus_configs
 6import pathlib
 7from typing import Tuple, Optional, Union
 8
 9
10def parseDate(dateS: str) -> Tuple[Optional[datetime.datetime], bool]:
11    if dateS is None or dateS == '':
12        return (None, True)
13    else:
14        try:
15            return (datetime.datetime.strptime(dateS, '%d.%m.%Y'), True)
16        except:
17            sg.popup_error("Fehler beim Parsen des Datums '{}'".format(dateS))
18            return (None, False)
19
20
21def createFile(server: PyAPplus64.APplusServer, fileS: str, vonS: str, bisS: str) -> None:
22    (von, vonOK) = parseDate(vonS)
23    if not vonOK:
24        return
25
26    (bis, bisOK) = parseDate(bisS)
27    if not bisOK:
28        return
29
30    if (fileS is None) or fileS == '':
31        sg.popup_error("Es wurde keine Ausgabedatei ausgewählt.")
32        return
33    else:
34        file = pathlib.Path(fileS)
35
36    c = mengenabweichung.exportVonBis(server, file.as_posix(), von, bis)
37    sg.popup_ok("{} Datensätze erfolgreich in Datei '{}' geschrieben.".format(c, file))
38
39
40def main(confFile: Union[str, pathlib.Path], user: Optional[str] = None, env: Optional[str] = None) -> None:
41    server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env)
42
43    layout = [
44        [sg.Text(('Bitte geben Sie an, für welchen Zeitraum die '
45                  'Mengenabweichungen ausgegeben werden sollen:'))],
46        [sg.Text('Von (einschließlich)', size=(15, 1)), sg.InputText(key='Von'),
47         sg.CalendarButton("Kalender", close_when_date_chosen=True,
48                           target="Von", format='%d.%m.%Y')],
49        [sg.Text('Bis (ausschließlich)', size=(15, 1)), sg.InputText(key='Bis'),
50         sg.CalendarButton("Kalender", close_when_date_chosen=True,
51                           target="Bis", format='%d.%m.%Y')],
52        [sg.Text('Ausgabedatei', size=(15, 1)), sg.InputText(key='File'),
53         sg.FileSaveAs(button_text="wählen",
54                       target="File",
55                       file_types=(('Excel Files', '*.xlsx'),),
56                       default_extension=".xlsx")],
57        [sg.Button("Aktueller Monat"), sg.Button("Letzter Monat"),
58         sg.Button("Aktuelles Jahr"), sg.Button("Letztes Jahr")],
59        [sg.Button("Speichern"), sg.Button("Beenden")]
60    ]
61
62    systemName = server.scripttool.getSystemName() + "/" + server.scripttool.getMandant()
63    window = sg.Window("Mengenabweichung " + systemName, layout)
64    now = datetime.date.today()
65    (cmonth, cyear) = (now.month, now.year)
66    (pyear, pmonth) = mengenabweichung.computePreviousMonthYear(cyear, cmonth)
67    (nyear, nmonth) = mengenabweichung.computeNextMonthYear(cyear, cmonth)
68
69    while True:
70        event, values = window.read()
71        if event == sg.WIN_CLOSED or event == 'Beenden':
72            break
73        if event == 'Aktueller Monat':
74            window['Von'].update(value="01.{:02d}.{:04d}".format(cmonth, cyear))
75            window['Bis'].update(value="01.{:02d}.{:04d}".format(nmonth, nyear))
76        if event == 'Letzter Monat':
77            window['Von'].update(value="01.{:02d}.{:04d}".format(pmonth, pyear))
78            window['Bis'].update(value="01.{:02d}.{:04d}".format(cmonth, cyear))
79        if event == 'Aktuelles Jahr':
80            window['Von'].update(value="01.01.{:04d}".format(cyear))
81            window['Bis'].update(value="01.01.{:04d}".format(cyear+1))
82        if event == 'Letztes Jahr':
83            window['Von'].update(value="01.01.{:04d}".format(cyear-1))
84            window['Bis'].update(value="01.01.{:04d}".format(cyear))
85        if event == 'Speichern':
86            try:
87                createFile(server, values.get('File', None),
88                           values.get('Von', None), values.get('Bis', None))
89            except Exception as e:
90                sg.popup_error_with_traceback("Beim Erzeugen der Excel-Datei trat ein Fehler auf:", e)
91
92    window.close()
93
94
95if __name__ == "__main__":
96    main(applus_configs.serverConfYamlProd)

complete_sql.pyw

Beispiel, wie ein einfacher APP-Server Aufruf über eine GUI zur Verfügung gestellt und mittels Python-Bibliotheken erweitert werden kann. Zudem wird demonstriert, wie eine Auswahl verschiedenere Systeme und Umgebungen realisiert werden kann.

 1import PySimpleGUI as sg  # type: ignore
 2import PyAPplus64
 3import applus_configs
 4import pathlib
 5from typing import Tuple, Optional, Union
 6
 7try:
 8    import sqlparse
 9except:
10    pass    
11
12try:
13    import sqlfmt.api
14except:
15    pass    
16
17def prettyPrintSQL(format, sql):
18    try:
19        if format == "sqlfmt":
20            mode = sqlfmt.api.Mode(dialect_name="ClickHouse")
21            sqlPretty = sqlfmt.api.format_string(sql, mode)
22            return sqlPretty.replace("N '", "N'") # fix String Constants
23        elif format == "sqlparse-2":
24            return sqlparse.format(sql, reindent=True, keyword_case='upper')        
25        elif format == "sqlparse":
26            return sqlparse.format(sql, reindent_aligned=True, keyword_case='upper')        
27        else:
28            return sql
29    except e:
30        print (str(e))
31        return sql
32
33def main() -> None:
34    monospaceFont = ("Courier New", 12)
35    sysenvs = applus_configs.serverConfDescs[:];
36    sysenvs.append("-");
37    layout = [
38        [sg.Button("Vervollständigen"), sg.Button("aus Clipboard", key="import"), sg.Button("nach Clipboard", key="export"), sg.Button("zurücksetzen", key="clear"), sg.Button("Beenden"),
39         sg.Text('System/Umgebung:'), sg.Combo(sysenvs, default_value="-", key="sysenv", readonly=True), sg.Text('Formatierung:'), sg.Combo(["-", "sqlfmt", "sqlparse", "sqlparse-2"], default_value="sqlparse", key="formatieren", readonly=True)
40         ],
41        [sg.Text('Eingabe-SQL')],
42        [sg.Multiline(key='input', size=(150, 20), font=monospaceFont)],
43        [sg.Text('Ausgabe-SQL')],
44        [sg.Multiline(key='output', size=(150, 20), font=monospaceFont, horizontal_scroll=True)]
45    ]
46
47    # server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env)  
48    # systemName = server.scripttool.getSystemName() + "/" + server.scripttool.getMandant()
49    window = sg.Window("Complete SQL", layout)
50    oldSys = None
51    while True:
52        event, values = window.read()
53        if event == sg.WIN_CLOSED or event == 'Beenden':
54            break
55        elif event == 'clear':
56             window['input'].update("")
57             window['output'].update("")
58        elif event == 'import':
59            try:
60                window['input'].update(window.TKroot.clipboard_get())
61            except:
62                window['input'].update("")
63            window['output'].update("")
64        elif event == 'export':
65            try:
66                window.TKroot.clipboard_clear()
67                window.TKroot.clipboard_append(window['output'].get())             
68            except:
69                pass
70        elif event == 'Vervollständigen':
71            sqlIn = window['input'].get()
72            try:
73                if sqlIn:
74                    sys = window['sysenv'].get()
75                    if sys != oldSys:
76                        oldSys = sys
77                        if sys and sys != "-":
78                            server = sys.connect()                                
79                        else:
80                            server = None
81                    if server:
82                        sqlOut = server.completeSQL(sqlIn)
83                    else:
84                        sqlOut = sqlIn                       
85                    sqlOut = prettyPrintSQL(window['formatieren'].get(), sqlOut)
86                else:
87                    sqlOut = ""
88            except Exception as e:
89                sqlOut = "ERROR: " + str(e)
90                sg.popup_error_with_traceback("Fehler bei Vervollständigung", e)
91            window['output'].update(value=sqlOut)
92
93    window.close()
94
95
96if __name__ == "__main__":
97    main()

importViewUDF.py

Folgende Scripte erlauben den einfachen Import von DB-Anpass-Dateien, Views und UDFs über den Windows-Explorer. Werden Verknüpfungen zu den Scripten importViewUDFDeploy.pyw und importViewUDFTest.pyw in %appdata%\Microsoft\Windows\SendTo abgelegt, so können eine oder mehrerer solcher Dateien mittels _Kontextmenü (Rechtsklick) - Senden an_ an APplus zur Verarbeitung übergeben werden. Dabei ist es wichtig, dass sich die Dateien im für den jeweiligen Typ passenden Verzeichnis befinden.

 1import PySimpleGUI as sg  # type: ignore
 2import pathlib
 3import PyAPplus64
 4from PyAPplus64 import applus
 5from PyAPplus64 import sql_utils
 6import applus_configs
 7import traceback
 8import pathlib
 9import sys
10
11def importViewsUDFs(server, views, udfs, dbanpass):
12  res = ""
13  try:    
14    if views or udfs:
15      for env in server.scripttool.getAllEnvironments():
16        res = res + server.importUdfsAndViews(env, views, udfs);
17        res = res + "\n\n";
18
19    for xml in dbanpass:
20      res = res + "Verarbeite " + xml + "\n"
21      xmlRes = server.updateDatabase(xml);
22      if (xmlRes == ""): xmlRes = "OK";
23      res = res + xmlRes
24      res = res + "\n\n"
25
26    sg.popup_scrolled("Importiere", res)
27  except:
28    sg.popup_error("Fehler", traceback.format_exc())
29
30def importIntoSystem(server, system):
31  try:
32    if (len(sys.argv) < 2):
33      sg.popup_error("Keine Datei zum Import übergeben")
34      return
35
36    views = []
37    udfs = []
38    dbanpass = []
39    errors = ""
40
41
42    for i in range (1, len(sys.argv)):
43      arg = pathlib.Path(sys.argv[i])
44      if arg == server.scripttool.getInstallPathAppServer().joinpath("Database", "View", arg.stem + ".sql"):
45        views.append(arg.stem)
46      elif arg == server.scripttool.getInstallPathAppServer().joinpath("Database", "UDF", arg.stem + ".sql"):
47        udfs.append(arg.stem)
48      elif arg == server.scripttool.getInstallPathAppServer().joinpath("DBChange", arg.stem + ".xml"):
49        dbanpass.append(arg.stem + ".xml")
50      else:
51        errors = errors + "  - " + str(arg) + "\n";
52
53    if len(errors) > 0:
54      msg = "Folgende Dateien sind keine View, UDF oder DB-Anpass-Dateien des "+system+"-Systems:\n" + errors;
55      sg.popup_error("Fehler", msg);
56    if views or udfs or dbanpass:
57      importViewsUDFs(server, views, udfs, dbanpass)
58      
59  except:
60    sg.popup_error("Fehler", traceback.format_exc())
61
62if __name__ == "__main__":
63  server = PyAPplus64.applusFromConfigFile(applus_configs.serverConfYamlDeploy)
64  importIntoSystem(server, "Deploy");
65  
66
67

Wrapper für Deploy-System:

1import importViewUDF
2import applus_configs
3import PyAPplus64
4
5if __name__ == "__main__":
6  server = PyAPplus64.applusFromConfigFile(applus_configs.serverConfYamlDeploy)
7  importViewUDF.importIntoSystem(server, "Deploy")

Wrapper für Test-System:

1import importViewUDF
2import applus_configs
3import PyAPplus64
4
5if __name__ == "__main__":
6  server = PyAPplus64.applusFromConfigFile(applus_configs.serverConfYamlTest)
7  importViewUDF.importIntoSystem(server, "Test")

copy_artikel.py

Beispiel, wie Artikel inklusive Arbeitsplan und Stückliste dupliziert werden kann.

 1import pathlib
 2import PyAPplus64
 3import applus_configs
 4import logging
 5import yaml
 6from typing import Optional
 7
 8
 9def main(confFile: pathlib.Path, artikel: str, artikelNeu: Optional[str] = None) -> None:
10    # Server verbinden
11    server = PyAPplus64.applus.applusFromConfigFile(confFile)
12
13    # DuplicateBusinessObject für Artikel erstellen
14    dArt = PyAPplus64.duplicate.loadDBDuplicateArtikel(server, artikel)
15
16    # DuplicateBusinessObject zur Demonstration in YAML konvertieren und zurück
17    dArtYaml = yaml.dump(dArt)
18    print(dArtYaml)
19    dArt2 = yaml.load(dArtYaml, Loader=yaml.UnsafeLoader)
20
21    # Neue Artikel-Nummer bestimmen und DuplicateBusinessObject in DB schreiben
22    # Man könnte hier genauso gut einen anderen Server verwenden
23    if (artikelNeu is None):
24        artikelNeu = server.nextNumber("Artikel")
25
26    if not (dArt is None):
27        dArt.setFields({"artikel": artikelNeu})
28        res = dArt.insert(server)
29        print(res)
30
31
32if __name__ == "__main__":
33    # Logger Einrichten
34    logging.basicConfig(level=logging.INFO)
35    # logger = logging.getLogger("PyAPplus64.applus_db");
36    # logger.setLevel(logging.ERROR)
37
38    main(applus_configs.serverConfYamlTest, "my-artikel", artikelNeu="my-artikel-copy")