Verbindungsaufbau nur wenn nötig
This commit is contained in:
		@@ -46,3 +46,9 @@ Pandas / SqlAlchemy / xlsxwriter
 | 
				
			|||||||
Sollen Excel-Dateien mit Pandas erzeugt, werden, so muss Pandas, SqlAlchemy und xlsxwriter installiert sein
 | 
					Sollen Excel-Dateien mit Pandas erzeugt, werden, so muss Pandas, SqlAlchemy und xlsxwriter installiert sein
 | 
				
			||||||
(`python -m pip install pandas sqlalchemy xlsxwriter`).
 | 
					(`python -m pip install pandas sqlalchemy xlsxwriter`).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PySimpleGUI und andere 
 | 
				
			||||||
 | 
					----------------------
 | 
				
			||||||
 | 
					Einige Beispiele benutzen PySimpleGUI (``python -m pip install pysimplegui``)
 | 
				
			||||||
 | 
					sowie teilweise spezielle Bibliotheken etwa zum Pretty-Printing von SQL (``python -m pip install sqlparse sqlfmt``). Dies
 | 
				
			||||||
 | 
					sind aber Abhängigkeiten von Beispielen, nicht der Bibliothek selbst.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,11 @@ typische Anwendungsfälle
 | 
				
			|||||||
einfache Admin-Aufgaben
 | 
					einfache Admin-Aufgaben
 | 
				
			||||||
-----------------------
 | 
					-----------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Selten auftretende Admin-Aufgaben lassen sich gut mittels Python-Scripten automatisieren.
 | 
					Selten auftretende Admin-Aufgaben lassen sich gut mittels Python-Scripten
 | 
				
			||||||
Es ist sehr einfach möglich, auf die DB, aber auch auf SOAP-Schnittstelle zuzugreifen.
 | 
					automatisieren. Es ist sehr einfach möglich, auf die DB, aber auch auf
 | 
				
			||||||
Ich habe dies vor allem für Wartungsarbeiten an Anpassungstabellen, die für eigene Erweiterungen
 | 
					SOAP-Schnittstelle der APP-Serverse zuzugreifen. Zudem ist rudimentärer Zugriff
 | 
				
			||||||
entwickelt wurden, genutzt. 
 | 
					auf ASMX-Seiten implementiert. Ich habe dies vor allem für Wartungsarbeiten an
 | 
				
			||||||
 | 
					Anpassungstabellen genutzt, die für eigene Erweiterungen entwickelt wurden. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Als triviales Beispiel sucht folgender Code alle `DOCUMENTS` Einträge in
 | 
					Als triviales Beispiel sucht folgender Code alle `DOCUMENTS` Einträge in
 | 
				
			||||||
Artikeln (angezeigt als `Bild` in `ArtikelRec.aspx`), für die Datei, auf die
 | 
					Artikeln (angezeigt als `Bild` in `ArtikelRec.aspx`), für die Datei, auf die
 | 
				
			||||||
@@ -88,7 +89,7 @@ Dank der Bibliothek `zeep` ist es auch sehr einfach möglich, auf beliebige SOAP
 | 
				
			|||||||
Beispielsweise kann auf die Sys-Config auch händisch, d.h. durch direkten Aufruf einer SOAP-Methode
 | 
					Beispielsweise kann auf die Sys-Config auch händisch, d.h. durch direkten Aufruf einer SOAP-Methode
 | 
				
			||||||
des APP-Servers zugegriffen werden::
 | 
					des APP-Servers zugegriffen werden::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   client = server.server_conn.getAppClient("p2system", "SysConf");
 | 
					   client = server.getAppClient("p2system", "SysConf");
 | 
				
			||||||
   print (client.service.getString("STAMM", "MYLAND"))
 | 
					   print (client.service.getString("STAMM", "MYLAND"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,8 +14,8 @@ sys.path.append('../src/')
 | 
				
			|||||||
project = 'PyAPplus64'
 | 
					project = 'PyAPplus64'
 | 
				
			||||||
copyright = '2023, Thomas Tuerk'
 | 
					copyright = '2023, Thomas Tuerk'
 | 
				
			||||||
author = 'Thomas Tuerk'
 | 
					author = 'Thomas Tuerk'
 | 
				
			||||||
version = '1.1.1'
 | 
					version = '1.1.2'
 | 
				
			||||||
release = '1.1.1'
 | 
					release = '1.1.2'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# -- General configuration ---------------------------------------------------
 | 
					# -- General configuration ---------------------------------------------------
 | 
				
			||||||
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
 | 
					# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,7 +18,8 @@ das Deploy-, das Test- und das Prod-System. Ein Beispiel ist im Unterverzeichnis
 | 
				
			|||||||
   
 | 
					   
 | 
				
			||||||
Damit nicht in jedem Script immer wieder neu die Konfig-Dateien ausgewählt werden müssen, werden die Konfigs für 
 | 
					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,
 | 
					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.
 | 
					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. 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. literalinclude:: ../../examples/applus_configs.py
 | 
					.. literalinclude:: ../../examples/applus_configs.py
 | 
				
			||||||
   :language: python
 | 
					   :language: python
 | 
				
			||||||
@@ -74,6 +75,44 @@ Die GUI wird um die Erzeugung von Excel-Dateien mit Mengenabweichungen gebaut.
 | 
				
			|||||||
   :lines: 9-
 | 
					   :lines: 9-
 | 
				
			||||||
   :linenos:
 | 
					   :linenos:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. literalinclude:: ../../examples/complete_sql.pyw
 | 
				
			||||||
 | 
					   :language: python
 | 
				
			||||||
 | 
					   :lines: 9-
 | 
				
			||||||
 | 
					   :linenos:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					``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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. literalinclude:: ../../examples/importViewUDF.py
 | 
				
			||||||
 | 
					   :language: python
 | 
				
			||||||
 | 
					   :lines: 9-
 | 
				
			||||||
 | 
					   :linenos:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Wrapper für Deploy-System:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. literalinclude:: ../../examples/importViewUDFDeploy.pyw
 | 
				
			||||||
 | 
					   :language: python
 | 
				
			||||||
 | 
					   :lines: 9-
 | 
				
			||||||
 | 
					   :linenos:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Wrapper für Test-System:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. literalinclude:: ../../examples/importViewUDFTest.pyw
 | 
				
			||||||
 | 
					   :language: python
 | 
				
			||||||
 | 
					   :lines: 9-
 | 
				
			||||||
 | 
					   :linenos:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
``copy_artikel.py``
 | 
					``copy_artikel.py``
 | 
				
			||||||
-----------------------
 | 
					-----------------------
 | 
				
			||||||
Beispiel, wie Artikel inklusive Arbeitsplan und Stückliste dupliziert werden kann.
 | 
					Beispiel, wie Artikel inklusive Arbeitsplan und Stückliste dupliziert werden kann.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,10 +7,26 @@
 | 
				
			|||||||
# https://opensource.org/licenses/MIT.
 | 
					# https://opensource.org/licenses/MIT.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import pathlib
 | 
					import pathlib
 | 
				
			||||||
 | 
					from PyAPplus64.applus import APplusServerConfigDescription
 | 
				
			||||||
 | 
					
 | 
				
			||||||
basedir = pathlib.Path(__file__)
 | 
					basedir = basedir = pathlib.Path(__file__) # Adapt to your needs
 | 
				
			||||||
configdir = basedir.joinpath("config")
 | 
					configdir = basedir.joinpath("config")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
serverConfYamlDeploy = configdir.joinpath("applus-server-deploy.yaml")
 | 
					serverConfYamlDeploy = configdir.joinpath("applus-server-deploy.yaml")
 | 
				
			||||||
serverConfYamlTest = configdir.joinpath("applus-server-test.yaml")
 | 
					serverConfYamlTest = configdir.joinpath("applus-server-test.yaml")
 | 
				
			||||||
serverConfYamlProd = configdir.joinpath("applus-server-prod.yaml")
 | 
					serverConfYamlProd = configdir.joinpath("applus-server-prod.yaml")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					serverConfDescProdEnv1 = APplusServerConfigDescription("Prod/Env1", serverConfYamlProd, env="Env1")
 | 
				
			||||||
 | 
					serverConfDescProdEnv2 = APplusServerConfigDescription("Prod/Env2", serverConfYamlProd, env="Env2")
 | 
				
			||||||
 | 
					serverConfDescTestEnv1 = APplusServerConfigDescription("Test/Env1", serverConfYamlTest, env="Env1")
 | 
				
			||||||
 | 
					serverConfDescTestEnv2 = APplusServerConfigDescription("Test/Env2", serverConfYamlTest, env="Env2")
 | 
				
			||||||
 | 
					serverConfDescDeploy = APplusServerConfigDescription("Deploy", serverConfYamlDeploy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					serverConfDescs = [
 | 
				
			||||||
 | 
					  serverConfDescProdEnv1,
 | 
				
			||||||
 | 
					  serverConfDescProdEnv2,
 | 
				
			||||||
 | 
					  serverConfDescTestEnv1,
 | 
				
			||||||
 | 
					  serverConfDescTestEnv2,
 | 
				
			||||||
 | 
					  serverConfDescDeploy
 | 
				
			||||||
 | 
					]
 | 
				
			||||||
							
								
								
									
										105
									
								
								examples/complete_sql.pyw
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								examples/complete_sql.pyw
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,105 @@
 | 
				
			|||||||
 | 
					# 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import PySimpleGUI as sg  # type: ignore
 | 
				
			||||||
 | 
					import PyAPplus64
 | 
				
			||||||
 | 
					import applus_configs
 | 
				
			||||||
 | 
					import pathlib
 | 
				
			||||||
 | 
					from typing import Tuple, Optional, Union
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    import sqlparse
 | 
				
			||||||
 | 
					except:
 | 
				
			||||||
 | 
					    pass    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					try:
 | 
				
			||||||
 | 
					    import sqlfmt.api
 | 
				
			||||||
 | 
					except:
 | 
				
			||||||
 | 
					    pass    
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def prettyPrintSQL(format, sql):
 | 
				
			||||||
 | 
					    try:
 | 
				
			||||||
 | 
					        if format == "sqlfmt":
 | 
				
			||||||
 | 
					            mode = sqlfmt.api.Mode(dialect_name="ClickHouse")
 | 
				
			||||||
 | 
					            sqlPretty = sqlfmt.api.format_string(sql, mode)
 | 
				
			||||||
 | 
					            return sqlPretty.replace("N '", "N'") # fix String Constants
 | 
				
			||||||
 | 
					        elif format == "sqlparse-2":
 | 
				
			||||||
 | 
					            return sqlparse.format(sql, reindent=True, keyword_case='upper')        
 | 
				
			||||||
 | 
					        elif format == "sqlparse":
 | 
				
			||||||
 | 
					            return sqlparse.format(sql, reindent_aligned=True, keyword_case='upper')        
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            return sql
 | 
				
			||||||
 | 
					    except e:
 | 
				
			||||||
 | 
					        print (str(e))
 | 
				
			||||||
 | 
					        return sql
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def main() -> None:
 | 
				
			||||||
 | 
					    monospaceFont = ("Courier New", 12)
 | 
				
			||||||
 | 
					    sysenvs = applus_configs.serverConfDescs[:];
 | 
				
			||||||
 | 
					    sysenvs.append("-");
 | 
				
			||||||
 | 
					    layout = [
 | 
				
			||||||
 | 
					        [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"),
 | 
				
			||||||
 | 
					         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)
 | 
				
			||||||
 | 
					         ],
 | 
				
			||||||
 | 
					        [sg.Text('Eingabe-SQL')],
 | 
				
			||||||
 | 
					        [sg.Multiline(key='input', size=(150, 20), font=monospaceFont)],
 | 
				
			||||||
 | 
					        [sg.Text('Ausgabe-SQL')],
 | 
				
			||||||
 | 
					        [sg.Multiline(key='output', size=(150, 20), font=monospaceFont, horizontal_scroll=True)]
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # server = PyAPplus64.applusFromConfigFile(confFile, user=user, env=env)  
 | 
				
			||||||
 | 
					    # systemName = server.scripttool.getSystemName() + "/" + server.scripttool.getMandant()
 | 
				
			||||||
 | 
					    window = sg.Window("Complete SQL", layout)
 | 
				
			||||||
 | 
					    oldSys = None
 | 
				
			||||||
 | 
					    while True:
 | 
				
			||||||
 | 
					        event, values = window.read()
 | 
				
			||||||
 | 
					        if event == sg.WIN_CLOSED or event == 'Beenden':
 | 
				
			||||||
 | 
					            break
 | 
				
			||||||
 | 
					        elif event == 'clear':
 | 
				
			||||||
 | 
					             window['input'].update("")
 | 
				
			||||||
 | 
					             window['output'].update("")
 | 
				
			||||||
 | 
					        elif event == 'import':
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                window['input'].update(window.TKroot.clipboard_get())
 | 
				
			||||||
 | 
					            except:
 | 
				
			||||||
 | 
					                window['input'].update("")
 | 
				
			||||||
 | 
					            window['output'].update("")
 | 
				
			||||||
 | 
					        elif event == 'export':
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                window.TKroot.clipboard_clear()
 | 
				
			||||||
 | 
					                window.TKroot.clipboard_append(window['output'].get())             
 | 
				
			||||||
 | 
					            except:
 | 
				
			||||||
 | 
					                pass
 | 
				
			||||||
 | 
					        elif event == 'Vervollständigen':
 | 
				
			||||||
 | 
					            sqlIn = window['input'].get()
 | 
				
			||||||
 | 
					            try:
 | 
				
			||||||
 | 
					                if sqlIn:
 | 
				
			||||||
 | 
					                    sys = window['sysenv'].get()
 | 
				
			||||||
 | 
					                    if sys != oldSys:
 | 
				
			||||||
 | 
					                        oldSys = sys
 | 
				
			||||||
 | 
					                        if sys and sys != "-":
 | 
				
			||||||
 | 
					                            server = sys.connect()                                
 | 
				
			||||||
 | 
					                        else:
 | 
				
			||||||
 | 
					                            server = None
 | 
				
			||||||
 | 
					                    if server:
 | 
				
			||||||
 | 
					                        sqlOut = server.completeSQL(sqlIn)
 | 
				
			||||||
 | 
					                    else:
 | 
				
			||||||
 | 
					                        sqlOut = sqlIn                       
 | 
				
			||||||
 | 
					                    sqlOut = prettyPrintSQL(window['formatieren'].get(), sqlOut)
 | 
				
			||||||
 | 
					                else:
 | 
				
			||||||
 | 
					                    sqlOut = ""
 | 
				
			||||||
 | 
					            except Exception as e:
 | 
				
			||||||
 | 
					                sqlOut = "ERROR: " + str(e)
 | 
				
			||||||
 | 
					                sg.popup_error_with_traceback("Fehler bei Vervollständigung", e)
 | 
				
			||||||
 | 
					            window['output'].update(value=sqlOut)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    window.close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					    main()
 | 
				
			||||||
							
								
								
									
										75
									
								
								examples/importViewUDF.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								examples/importViewUDF.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					# 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import PySimpleGUI as sg  # type: ignore
 | 
				
			||||||
 | 
					import pathlib
 | 
				
			||||||
 | 
					import PyAPplus64
 | 
				
			||||||
 | 
					from PyAPplus64 import applus
 | 
				
			||||||
 | 
					from PyAPplus64 import sql_utils
 | 
				
			||||||
 | 
					import applus_configs
 | 
				
			||||||
 | 
					import traceback
 | 
				
			||||||
 | 
					import pathlib
 | 
				
			||||||
 | 
					import sys
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def importViewsUDFs(server, views, udfs, dbanpass):
 | 
				
			||||||
 | 
					  res = ""
 | 
				
			||||||
 | 
					  try:    
 | 
				
			||||||
 | 
					    if views or udfs:
 | 
				
			||||||
 | 
					      for env in server.scripttool.getAllEnvironments():
 | 
				
			||||||
 | 
					        res = res + server.importUdfsAndViews(env, views, udfs);
 | 
				
			||||||
 | 
					        res = res + "\n\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for xml in dbanpass:
 | 
				
			||||||
 | 
					      res = res + "Verarbeite " + xml + "\n"
 | 
				
			||||||
 | 
					      xmlRes = server.updateDatabase(xml);
 | 
				
			||||||
 | 
					      if (xmlRes == ""): xmlRes = "OK";
 | 
				
			||||||
 | 
					      res = res + xmlRes
 | 
				
			||||||
 | 
					      res = res + "\n\n"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    sg.popup_scrolled("Importiere", res)
 | 
				
			||||||
 | 
					  except:
 | 
				
			||||||
 | 
					    sg.popup_error("Fehler", traceback.format_exc())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def importIntoSystem(server, system):
 | 
				
			||||||
 | 
					  try:
 | 
				
			||||||
 | 
					    if (len(sys.argv) < 2):
 | 
				
			||||||
 | 
					      sg.popup_error("Keine Datei zum Import übergeben")
 | 
				
			||||||
 | 
					      return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    views = []
 | 
				
			||||||
 | 
					    udfs = []
 | 
				
			||||||
 | 
					    dbanpass = []
 | 
				
			||||||
 | 
					    errors = ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for i in range (1, len(sys.argv)):
 | 
				
			||||||
 | 
					      arg = pathlib.Path(sys.argv[i])
 | 
				
			||||||
 | 
					      if arg == server.scripttool.getInstallPathAppServer().joinpath("Database", "View", arg.stem + ".sql"):
 | 
				
			||||||
 | 
					        views.append(arg.stem)
 | 
				
			||||||
 | 
					      elif arg == server.scripttool.getInstallPathAppServer().joinpath("Database", "UDF", arg.stem + ".sql"):
 | 
				
			||||||
 | 
					        udfs.append(arg.stem)
 | 
				
			||||||
 | 
					      elif arg == server.scripttool.getInstallPathAppServer().joinpath("DBChange", arg.stem + ".xml"):
 | 
				
			||||||
 | 
					        dbanpass.append(arg.stem + ".xml")
 | 
				
			||||||
 | 
					      else:
 | 
				
			||||||
 | 
					        errors = errors + "  - " + str(arg) + "\n";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if len(errors) > 0:
 | 
				
			||||||
 | 
					      msg = "Folgende Dateien sind keine View, UDF oder DB-Anpass-Dateien des "+system+"-Systems:\n" + errors;
 | 
				
			||||||
 | 
					      sg.popup_error("Fehler", msg);
 | 
				
			||||||
 | 
					    if views or udfs or dbanpass:
 | 
				
			||||||
 | 
					      importViewsUDFs(server, views, udfs, dbanpass)
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					  except:
 | 
				
			||||||
 | 
					    sg.popup_error("Fehler", traceback.format_exc())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					  server = PyAPplus64.applusFromConfigFile(applus_configs.serverConfYamlDeploy)
 | 
				
			||||||
 | 
					  importIntoSystem(server, "Deploy");
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										15
									
								
								examples/importViewUDFDeploy.pyw
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								examples/importViewUDFDeploy.pyw
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					# 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import importViewUDF
 | 
				
			||||||
 | 
					import applus_configs
 | 
				
			||||||
 | 
					import PyAPplus64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					  server = PyAPplus64.applusFromConfigFile(applus_configs.serverConfYamlDeploy)
 | 
				
			||||||
 | 
					  importViewUDF.importIntoSystem(server, "Deploy")
 | 
				
			||||||
							
								
								
									
										15
									
								
								examples/importViewUDFTest.pyw
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								examples/importViewUDFTest.pyw
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					# 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import importViewUDF
 | 
				
			||||||
 | 
					import applus_configs
 | 
				
			||||||
 | 
					import PyAPplus64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if __name__ == "__main__":
 | 
				
			||||||
 | 
					  server = PyAPplus64.applusFromConfigFile(applus_configs.serverConfYamlTest)
 | 
				
			||||||
 | 
					  importViewUDF.importIntoSystem(server, "Test")
 | 
				
			||||||
@@ -48,8 +48,8 @@ def main(confFile: Union[str, pathlib.Path], user: Optional[str] = None, env: Op
 | 
				
			|||||||
    print("  InstallPathWebServer:", server.scripttool.getInstallPathWebServer())
 | 
					    print("  InstallPathWebServer:", server.scripttool.getInstallPathWebServer())
 | 
				
			||||||
    print("  ServerInfo - Version:", server.scripttool.getServerInfo().find("version").text)
 | 
					    print("  ServerInfo - Version:", server.scripttool.getServerInfo().find("version").text)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    client = server.getWebClient("masterdata/artikel.asmx")
 | 
					    client = server.getWebClient("dbenv/dbenv.asmx")
 | 
				
			||||||
    print("ARTIKEL-ASMX Date:", client.service.getServerDate())
 | 
					    print("WEB Environment:", client.service.getEnvironment())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if __name__ == "__main__":
 | 
					if __name__ == "__main__":
 | 
				
			||||||
    main(applus_configs.serverConfYamlTest)
 | 
					    main(applus_configs.serverConfYamlTest)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
[project]
 | 
					[project]
 | 
				
			||||||
name = "PyAPplus64"
 | 
					name = "PyAPplus64"
 | 
				
			||||||
version = "1.1.1"
 | 
					version = "1.1.2"
 | 
				
			||||||
authors = [
 | 
					authors = [
 | 
				
			||||||
  { name="Thomas Tuerk", email="kontakt@thomas-tuerk.de" },
 | 
					  { name="Thomas Tuerk", email="kontakt@thomas-tuerk.de" },
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
@@ -24,7 +24,10 @@ dependencies = [
 | 
				
			|||||||
  'SQLAlchemy',
 | 
					  'SQLAlchemy',
 | 
				
			||||||
  'pandas',
 | 
					  'pandas',
 | 
				
			||||||
  'XlsxWriter',
 | 
					  'XlsxWriter',
 | 
				
			||||||
  'zeep'
 | 
					  'zeep',
 | 
				
			||||||
 | 
					  'pysimplegui',
 | 
				
			||||||
 | 
					  'sqlparse',
 | 
				
			||||||
 | 
					  'sqlfmt'
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[project.urls]
 | 
					[project.urls]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ from . import applus_scripttool
 | 
				
			|||||||
from . import applus_usexml
 | 
					from . import applus_usexml
 | 
				
			||||||
from . import sql_utils
 | 
					from . import sql_utils
 | 
				
			||||||
import yaml
 | 
					import yaml
 | 
				
			||||||
 | 
					import json
 | 
				
			||||||
import urllib.parse
 | 
					import urllib.parse
 | 
				
			||||||
from zeep import Client
 | 
					from zeep import Client
 | 
				
			||||||
import pyodbc  # type: ignore
 | 
					import pyodbc  # type: ignore
 | 
				
			||||||
@@ -44,12 +45,8 @@ class APplusServer:
 | 
				
			|||||||
        self.server_settings : applus_server.APplusServerSettings = server_settings
 | 
					        self.server_settings : applus_server.APplusServerSettings = server_settings
 | 
				
			||||||
        """Einstellung für die Verbindung zum APP- und Webserver"""
 | 
					        """Einstellung für die Verbindung zum APP- und Webserver"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.db_conn = db_settings.connect()
 | 
					        self._db_conn_pool = list()
 | 
				
			||||||
        """
 | 
					        """Eine Liste bestehender DB-Verbindungen"""
 | 
				
			||||||
        Eine pyodbc-Connection zur APplus DB. Diese muss genutzt werden, wenn mehrere Operationen in einer Transaktion
 | 
					 | 
				
			||||||
        genutzt werden sollen. Ansonsten sind die Hilfsmethoden wie :meth:`APplusServer.dbQuery` zu bevorzugen.
 | 
					 | 
				
			||||||
        Diese Connection kann in Verbindung mit den Funktionen aus :mod:`PyAPplus64.applus_db` genutzt werden.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.server_conn: applus_server.APplusServerConnection = applus_server.APplusServerConnection(server_settings)
 | 
					        self.server_conn: applus_server.APplusServerConnection = applus_server.APplusServerConnection(server_settings)
 | 
				
			||||||
        """erlaubt den Zugriff auf den AppServer"""
 | 
					        """erlaubt den Zugriff auf den AppServer"""
 | 
				
			||||||
@@ -63,18 +60,61 @@ class APplusServer:
 | 
				
			|||||||
        self.scripttool: applus_scripttool.APplusScriptTool = applus_scripttool.APplusScriptTool(self)
 | 
					        self.scripttool: applus_scripttool.APplusScriptTool = applus_scripttool.APplusScriptTool(self)
 | 
				
			||||||
        """erlaubt den einfachen Zugriff auf Funktionen des ScriptTools"""
 | 
					        """erlaubt den einfachen Zugriff auf Funktionen des ScriptTools"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.client_table = self.server_conn.getAppClient("p2core", "Table")
 | 
					        self._client_table = None
 | 
				
			||||||
        self.client_xml = self.server_conn.getAppClient("p2core", "XML")
 | 
					        self._client_xml = None
 | 
				
			||||||
        self.client_nummer = self.server_conn.getAppClient("p2system", "Nummer")
 | 
					        self._client_nummer = None
 | 
				
			||||||
        self.client_adaptdb = self.server_conn.getAppClient("p2dbtools", "AdaptDB");
 | 
					        self._client_adaptdb= None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def client_table(self) -> Client:
 | 
				
			||||||
 | 
					        if not self._client_table:
 | 
				
			||||||
 | 
					          self._client_table = self.getAppClient("p2core", "Table")
 | 
				
			||||||
 | 
					        return self._client_table
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def client_xml(self) -> Client:
 | 
				
			||||||
 | 
					        if not self._client_xml:
 | 
				
			||||||
 | 
					          self._client_xml = self.getAppClient("p2core", "XML")
 | 
				
			||||||
 | 
					        return self._client_xml
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def client_nummer(self) -> Client:
 | 
				
			||||||
 | 
					        if not self._client_nummer:
 | 
				
			||||||
 | 
					          self._client_nummer = self.getAppClient("p2system", "Nummer")
 | 
				
			||||||
 | 
					        return self._client_nummer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def client_adaptdb(self) -> Client:
 | 
				
			||||||
 | 
					        if not self._client_adaptdb:
 | 
				
			||||||
 | 
					          self._client_adaptdb = self.getAppClient("p2dbtools", "AdaptDB")
 | 
				
			||||||
 | 
					        return self._client_adaptdb
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getDBConnection(self) -> pyodbc.Connection:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Liefert eine pyodbc-Connection zur APplus DB. Diese muss genutzt werden, wenn mehrere Operationen in einer Transaktion
 | 
				
			||||||
 | 
					        genutzt werden sollen. Ansonsten sind die Hilfsmethoden wie :meth:`APplusServer.dbQuery` zu bevorzugen.
 | 
				
			||||||
 | 
					        Diese Connection kann in Verbindung mit den Funktionen aus :mod:`PyAPplus64.applus_db` genutzt werden.
 | 
				
			||||||
 | 
					        Die Verbindung sollte nach Benutzung wieder freigegeben oder geschlossen werden.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if self._db_conn_pool:
 | 
				
			||||||
 | 
					            return self._db_conn_pool.pop()
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            conn = self.db_settings.connect()
 | 
				
			||||||
 | 
					            self._db_conn_pool.append(conn)
 | 
				
			||||||
 | 
					            return conn
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def releaseDBConnection(self, conn : pyodbc.Connection) -> None:
 | 
				
			||||||
 | 
					        """Gibt eine DB-Connection zur Wiederverwendung frei"""
 | 
				
			||||||
 | 
					        self._db_conn_pool.append(conn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def reconnectDB(self) -> None:
 | 
					    def reconnectDB(self) -> None:
 | 
				
			||||||
 | 
					        for conn in self._db_conn_pool:
 | 
				
			||||||
            try:
 | 
					            try:
 | 
				
			||||||
            self.db_conn.close()
 | 
					                conn.close()
 | 
				
			||||||
            except:
 | 
					            except:
 | 
				
			||||||
                pass
 | 
					                pass
 | 
				
			||||||
        self.db_conn = self.db_settings.connect()
 | 
					        self._db_conn_pool = list()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def completeSQL(self, sql: sql_utils.SqlStatement, raw: bool = False) -> str:
 | 
					    def completeSQL(self, sql: sql_utils.SqlStatement, raw: bool = False) -> str:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
@@ -97,7 +137,11 @@ class APplusServer:
 | 
				
			|||||||
        """Führt eine SQL Query aus und liefert alle Zeilen zurück. Das SQL wird zunächst
 | 
					        """Führt eine SQL Query aus und liefert alle Zeilen zurück. Das SQL wird zunächst
 | 
				
			||||||
           vom Server angepasst, so dass z.B. Mandanteninformation hinzugefügt werden."""
 | 
					           vom Server angepasst, so dass z.B. Mandanteninformation hinzugefügt werden."""
 | 
				
			||||||
        sqlC = self.completeSQL(sql, raw=raw)
 | 
					        sqlC = self.completeSQL(sql, raw=raw)
 | 
				
			||||||
        return applus_db.rawQueryAll(self.db_conn, sqlC, *args, apply=apply)
 | 
					        conn = self.getDBConnection()
 | 
				
			||||||
 | 
					        res = applus_db.rawQueryAll(conn, sqlC, *args, apply=apply)
 | 
				
			||||||
 | 
					        self.releaseDBConnection(conn)
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dbQuerySingleValues(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Sequence[Any]:
 | 
					    def dbQuerySingleValues(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Sequence[Any]:
 | 
				
			||||||
        """Führt eine SQL Query aus, die nur eine Spalte zurückliefern soll."""
 | 
					        """Führt eine SQL Query aus, die nur eine Spalte zurückliefern soll."""
 | 
				
			||||||
@@ -107,12 +151,19 @@ class APplusServer:
 | 
				
			|||||||
        """Führt eine SQL Query aus und führt für jede Zeile die übergeben Funktion aus.
 | 
					        """Führt eine SQL Query aus und führt für jede Zeile die übergeben Funktion aus.
 | 
				
			||||||
           Das SQL wird zunächst vom Server angepasst, so dass z.B. Mandanteninformation hinzugefügt werden."""
 | 
					           Das SQL wird zunächst vom Server angepasst, so dass z.B. Mandanteninformation hinzugefügt werden."""
 | 
				
			||||||
        sqlC = self.completeSQL(sql, raw=raw)
 | 
					        sqlC = self.completeSQL(sql, raw=raw)
 | 
				
			||||||
        applus_db.rawQuery(self.db_conn, sqlC, f, *args)
 | 
					        conn = self.getDBConnection()
 | 
				
			||||||
 | 
					        res = applus_db.rawQuery(conn, sqlC, f, *args)
 | 
				
			||||||
 | 
					        self.releaseDBConnection(conn)
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dbQuerySingleRow(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Optional[pyodbc.Row]:
 | 
					    def dbQuerySingleRow(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Optional[pyodbc.Row]:
 | 
				
			||||||
        """Führt eine SQL Query aus, die maximal eine Zeile zurückliefern soll. Diese Zeile wird geliefert."""
 | 
					        """Führt eine SQL Query aus, die maximal eine Zeile zurückliefern soll. Diese Zeile wird geliefert."""
 | 
				
			||||||
        sqlC = self.completeSQL(sql, raw=raw)
 | 
					        sqlC = self.completeSQL(sql, raw=raw)
 | 
				
			||||||
        return applus_db.rawQuerySingleRow(self.db_conn, sqlC, *args)
 | 
					        conn = self.getDBConnection()
 | 
				
			||||||
 | 
					        res = applus_db.rawQuerySingleRow(conn, sqlC, *args)
 | 
				
			||||||
 | 
					        self.releaseDBConnection(conn)
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dbQuerySingleRowDict(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Optional[Dict[str, Any]]:
 | 
					    def dbQuerySingleRowDict(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Optional[Dict[str, Any]]:
 | 
				
			||||||
        """Führt eine SQL Query aus, die maximal eine Zeile zurückliefern soll.
 | 
					        """Führt eine SQL Query aus, die maximal eine Zeile zurückliefern soll.
 | 
				
			||||||
@@ -127,13 +178,19 @@ class APplusServer:
 | 
				
			|||||||
        """Führt eine SQL Query aus, die maximal einen Wert zurückliefern soll.
 | 
					        """Führt eine SQL Query aus, die maximal einen Wert zurückliefern soll.
 | 
				
			||||||
           Dieser Wert oder None wird geliefert."""
 | 
					           Dieser Wert oder None wird geliefert."""
 | 
				
			||||||
        sqlC = self.completeSQL(sql, raw=raw)
 | 
					        sqlC = self.completeSQL(sql, raw=raw)
 | 
				
			||||||
        return applus_db.rawQuerySingleValue(self.db_conn, sqlC, *args)
 | 
					        conn = self.getDBConnection()
 | 
				
			||||||
 | 
					        res = applus_db.rawQuerySingleValue(conn, sqlC, *args)
 | 
				
			||||||
 | 
					        self.releaseDBConnection(conn)
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def dbExecute(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Any:
 | 
					    def dbExecute(self, sql: sql_utils.SqlStatement, *args: Any, raw: bool = False) -> Any:
 | 
				
			||||||
        """Führt ein SQL Statement (z.B. update oder insert) aus. Das SQL wird zunächst
 | 
					        """Führt ein SQL Statement (z.B. update oder insert) aus. Das SQL wird zunächst
 | 
				
			||||||
           vom Server angepasst, so dass z.B. Mandanteninformation hinzugefügt werden."""
 | 
					           vom Server angepasst, so dass z.B. Mandanteninformation hinzugefügt werden."""
 | 
				
			||||||
        sqlC = self.completeSQL(sql, raw=raw)
 | 
					        sqlC = self.completeSQL(sql, raw=raw)
 | 
				
			||||||
        return applus_db.rawExecute(self.db_conn, sqlC, *args)
 | 
					        conn = self.getDBConnection()
 | 
				
			||||||
 | 
					        res = applus_db.rawExecute(conn, sqlC, *args)
 | 
				
			||||||
 | 
					        self.releaseDBConnection(conn)
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def isDBTableKnown(self, table: str) -> bool:
 | 
					    def isDBTableKnown(self, table: str) -> bool:
 | 
				
			||||||
        """Prüft, ob eine Tabelle im System bekannt ist"""
 | 
					        """Prüft, ob eine Tabelle im System bekannt ist"""
 | 
				
			||||||
@@ -164,6 +221,8 @@ class APplusServer:
 | 
				
			|||||||
           Als parameter wird die relative URL der ASMX-Seite erwartet. Die Base-URL automatisch ergänzt.
 | 
					           Als parameter wird die relative URL der ASMX-Seite erwartet. Die Base-URL automatisch ergänzt.
 | 
				
			||||||
           Ein Beispiel für eine solche relative URL ist "masterdata/artikel.asmx".
 | 
					           Ein Beispiel für eine solche relative URL ist "masterdata/artikel.asmx".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					           ACHTUNG: Als Umgebung wird die Umgebung des sich anmeldenden Nutzers verwendet. Sowohl Nutzer als auch Umgebung können sich von den für App-Clients verwendeten Werten unterscheiden. Wenn möglich, sollte ein App-Client verwendet werden.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           :param url: die relative URL der ASMX Seite, z.B. "masterdata/artikel.asmx"
 | 
					           :param url: die relative URL der ASMX Seite, z.B. "masterdata/artikel.asmx"
 | 
				
			||||||
           :type package: str
 | 
					           :type package: str
 | 
				
			||||||
           :return: den Client
 | 
					           :return: den Client
 | 
				
			||||||
@@ -197,7 +256,10 @@ class APplusServer:
 | 
				
			|||||||
        Jeder Eintrag enthält eine Liste von Feldern, die zusammen eindeutig sein
 | 
					        Jeder Eintrag enthält eine Liste von Feldern, die zusammen eindeutig sein
 | 
				
			||||||
        müssen.
 | 
					        müssen.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return applus_db.getUniqueFieldsOfTable(self.db_conn, table)
 | 
					        conn = self.getDBConnection()
 | 
				
			||||||
 | 
					        res = applus_db.getUniqueFieldsOfTable(conn, table)
 | 
				
			||||||
 | 
					        self.releaseDBConnection(conn)
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def useXML(self, xml: str) -> Any:
 | 
					    def useXML(self, xml: str) -> Any:
 | 
				
			||||||
        """Ruft ``p2core.xml.usexml`` auf. Wird meist durch ein ``UseXMLRow-Objekt`` aufgerufen."""
 | 
					        """Ruft ``p2core.xml.usexml`` auf. Wird meist durch ein ``UseXMLRow-Objekt`` aufgerufen."""
 | 
				
			||||||
@@ -261,6 +323,33 @@ class APplusServer:
 | 
				
			|||||||
            return res
 | 
					            return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def importUdfsAndViews(self, environment : str, views : [str] = [], udfs : [str] = []) -> str:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Importiert bestimmte  Views und UDFs
 | 
				
			||||||
 | 
					        :param environment: die Umgebung, in die Importiert werden soll
 | 
				
			||||||
 | 
					        :type environment: string
 | 
				
			||||||
 | 
					        :param views: Views, die importiert werden sollen
 | 
				
			||||||
 | 
					        :type views: [string]
 | 
				
			||||||
 | 
					        :param udfs: Views, die importiert werden sollen
 | 
				
			||||||
 | 
					        :type udfs: [string]
 | 
				
			||||||
 | 
					        :return: Infos zur Ausführung
 | 
				
			||||||
 | 
					        :rtype: str
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        lbl="";
 | 
				
			||||||
 | 
					        files=[];
 | 
				
			||||||
 | 
					        for v in views:
 | 
				
			||||||
 | 
					            files.append({"type" : 1, "name" : v})
 | 
				
			||||||
 | 
					        for u in udfs:
 | 
				
			||||||
 | 
					            files.append({"type" : 0, "name" : u})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        jobId = self.job.createSOAPJob("importing UDFs and Views");
 | 
				
			||||||
 | 
					        self.client_adaptdb.service.importUdfsAndViews(jobId, environment, False, json.dumps(files), "de");
 | 
				
			||||||
 | 
					        res = self.job.getResultURLString(jobId)
 | 
				
			||||||
 | 
					        if res is None: res = "FEHLER";
 | 
				
			||||||
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def makeWebLink(self, base: str, **kwargs: Any) -> str:
 | 
					    def makeWebLink(self, base: str, **kwargs: Any) -> str:
 | 
				
			||||||
        if not self.server_settings.webserver:
 | 
					        if not self.server_settings.webserver:
 | 
				
			||||||
            raise Exception("keine Webserver-BaseURL gesetzt")
 | 
					            raise Exception("keine Webserver-BaseURL gesetzt")
 | 
				
			||||||
@@ -286,6 +375,14 @@ class APplusServer:
 | 
				
			|||||||
    def makeWebLinkBauftrag(self, **kwargs: Any) -> str:
 | 
					    def makeWebLinkBauftrag(self, **kwargs: Any) -> str:
 | 
				
			||||||
        return self.makeWebLink("wp/bauftragRec.aspx", **kwargs)
 | 
					        return self.makeWebLink("wp/bauftragRec.aspx", **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def makeWebLinkAuftrag(self, **kwargs: Any) -> str:
 | 
				
			||||||
 | 
					        return self.makeWebLink("sales/auftragRec.aspx", **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def makeWebLinkVKRahmen(self, **kwargs: Any) -> str:
 | 
				
			||||||
 | 
					        return self.makeWebLink("sales/vkrahmenRec.aspx", **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def makeWebLinkWarenaugang(self, **kwargs: Any) -> str:
 | 
				
			||||||
 | 
					        return self.makeWebLink("sales/warenausgangRec.aspx", **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def applusFromConfigDict(yamlDict: Dict[str, Any], user: Optional[str] = None, env: Optional[str] = None) -> APplusServer:
 | 
					def applusFromConfigDict(yamlDict: Dict[str, Any], user: Optional[str] = None, env: Optional[str] = None) -> APplusServer:
 | 
				
			||||||
    """Läd Einstellungen aus einer Config und erzeugt daraus ein APplus-Objekt"""
 | 
					    """Läd Einstellungen aus einer Config und erzeugt daraus ein APplus-Objekt"""
 | 
				
			||||||
@@ -324,3 +421,39 @@ def applusFromConfig(yamlString: str, user: Optional[str] = None, env: Optional[
 | 
				
			|||||||
    """Läd Einstellungen aus einer Config-Datei und erzeugt daraus ein APplus-Objekt"""
 | 
					    """Läd Einstellungen aus einer Config-Datei und erzeugt daraus ein APplus-Objekt"""
 | 
				
			||||||
    yamlDict = yaml.safe_load(yamlString)
 | 
					    yamlDict = yaml.safe_load(yamlString)
 | 
				
			||||||
    return applusFromConfigDict(yamlDict, user=user, env=env)
 | 
					    return applusFromConfigDict(yamlDict, user=user, env=env)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class APplusServerConfigDescription:
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    Beschreibung einer Configuration bestehend aus Config-Datei, Nutzer und Umgebung.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param descr: Beschreibung als String, nur für Ausgabe gedacht
 | 
				
			||||||
 | 
					    :type descr: str
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param yamlfile: die Datei
 | 
				
			||||||
 | 
					    :type yamlfile: 'FileDescriptorOrPath'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param user: der Nutzer
 | 
				
			||||||
 | 
					    :type user: Optional[str]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :param env: die Umgebung
 | 
				
			||||||
 | 
					    :type env: Optional[str]
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    def __init__(self,
 | 
				
			||||||
 | 
					                 descr: str,
 | 
				
			||||||
 | 
					                 yamlfile: 'FileDescriptorOrPath',
 | 
				
			||||||
 | 
					                 user:Optional[str] = None,
 | 
				
			||||||
 | 
					                 env:Optional[str] = None):
 | 
				
			||||||
 | 
					        self.descr = descr
 | 
				
			||||||
 | 
					        self.yamlfile = yamlfile
 | 
				
			||||||
 | 
					        self.user = user
 | 
				
			||||||
 | 
					        self.env = env
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self) -> str:
 | 
				
			||||||
 | 
					        return self.descr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def connect(self) -> APplusServer:
 | 
				
			||||||
 | 
					        return applusFromConfigFile(self.yamlfile, user=self.user, env=self.env)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@
 | 
				
			|||||||
# https://opensource.org/licenses/MIT.
 | 
					# https://opensource.org/licenses/MIT.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from typing import TYPE_CHECKING, Optional
 | 
					from typing import TYPE_CHECKING, Optional
 | 
				
			||||||
 | 
					from zeep import Client
 | 
				
			||||||
import uuid
 | 
					import uuid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if TYPE_CHECKING:
 | 
					if TYPE_CHECKING:
 | 
				
			||||||
@@ -23,7 +24,14 @@ class APplusJob:
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, server: 'APplusServer') -> None:
 | 
					    def __init__(self, server: 'APplusServer') -> None:
 | 
				
			||||||
        self.client = server.getAppClient("p2core", "Job")
 | 
					        self.server = server
 | 
				
			||||||
 | 
					        self._client = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def client(self) -> Client:
 | 
				
			||||||
 | 
					        if not self._client: 
 | 
				
			||||||
 | 
					          self._client = self.server.getAppClient("p2core", "Job")
 | 
				
			||||||
 | 
					        return self._client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def createSOAPJob(self, bez: str) -> str:
 | 
					    def createSOAPJob(self, bez: str) -> str:
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ from .applus import APplusServer
 | 
				
			|||||||
from . import sql_utils
 | 
					from . import sql_utils
 | 
				
			||||||
import lxml.etree as ET  # type: ignore
 | 
					import lxml.etree as ET  # type: ignore
 | 
				
			||||||
from typing import Optional, Tuple, Set
 | 
					from typing import Optional, Tuple, Set
 | 
				
			||||||
 | 
					from zeep import Client
 | 
				
			||||||
import pathlib
 | 
					import pathlib
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -57,7 +58,14 @@ class APplusScriptTool:
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, server: APplusServer) -> None:
 | 
					    def __init__(self, server: APplusServer) -> None:
 | 
				
			||||||
        self.client = server.getAppClient("p2script", "ScriptTool")
 | 
					        self.server = server
 | 
				
			||||||
 | 
					        self._client = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def client(self) -> Client:
 | 
				
			||||||
 | 
					        if not self._client: 
 | 
				
			||||||
 | 
					          self._client = self.server.getAppClient("p2script", "ScriptTool")
 | 
				
			||||||
 | 
					        return self._client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def getCurrentDate(self) -> str:
 | 
					    def getCurrentDate(self) -> str:
 | 
				
			||||||
        return self.client.service.getCurrentDate()
 | 
					        return self.client.service.getCurrentDate()
 | 
				
			||||||
@@ -181,3 +189,18 @@ class APplusScriptTool:
 | 
				
			|||||||
        :rtype: ET.Element
 | 
					        :rtype: ET.Element
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        return ET.fromstring(self.getServerInfoString())
 | 
					        return ET.fromstring(self.getServerInfoString())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def getAllEnvironments(self) -> [str]:
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Liefert alle Umgebungen
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        :return: die gefundenen Umgebungen
 | 
				
			||||||
 | 
					        :rtype: [str]
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					                
 | 
				
			||||||
 | 
					        envs = []
 | 
				
			||||||
 | 
					        envString = self.client.service.getAllEnvironmentsInMasterDatabase()
 | 
				
			||||||
 | 
					        for e in envString.split(","):
 | 
				
			||||||
 | 
					            envs.append(e.split(":")[0])
 | 
				
			||||||
 | 
					        return envs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,6 +101,8 @@ class APplusServerConnection:
 | 
				
			|||||||
           Als parameter wird die relative URL der ASMX-Seite erwartet. Die Base-URL automatisch ergänzt.
 | 
					           Als parameter wird die relative URL der ASMX-Seite erwartet. Die Base-URL automatisch ergänzt.
 | 
				
			||||||
           Ein Beispiel für eine solche relative URL ist "masterdata/artikel.asmx".
 | 
					           Ein Beispiel für eine solche relative URL ist "masterdata/artikel.asmx".
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					           ACHTUNG: Als Umgebung wird die Umgebung des sich anmeldenden Nutzers verwendet. Sowohl Nutzer als auch Umgebung können sich von den für App-Clients verwendeten Werten unterscheiden. Wenn möglich, sollte ein App-Client verwendet werden.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
           :param url: die relative URL der ASMX Seite, z.B. "masterdata/artikel.asmx"
 | 
					           :param url: die relative URL der ASMX Seite, z.B. "masterdata/artikel.asmx"
 | 
				
			||||||
           :type package: str
 | 
					           :type package: str
 | 
				
			||||||
           :return: den Client
 | 
					           :return: den Client
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,6 +7,7 @@
 | 
				
			|||||||
# https://opensource.org/licenses/MIT.
 | 
					# https://opensource.org/licenses/MIT.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from typing import TYPE_CHECKING, Optional, Dict, Any, Callable, Sequence
 | 
					from typing import TYPE_CHECKING, Optional, Dict, Any, Callable, Sequence
 | 
				
			||||||
 | 
					from zeep import Client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if TYPE_CHECKING:
 | 
					if TYPE_CHECKING:
 | 
				
			||||||
    from .applus import APplusServer
 | 
					    from .applus import APplusServer
 | 
				
			||||||
@@ -22,8 +23,16 @@ class APplusSysConf:
 | 
				
			|||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __init__(self, server: 'APplusServer') -> None:
 | 
					    def __init__(self, server: 'APplusServer') -> None:
 | 
				
			||||||
        self.client = server.getAppClient("p2system", "SysConf")
 | 
					 | 
				
			||||||
        self.cache: Dict[str, type] = {}
 | 
					        self.cache: Dict[str, type] = {}
 | 
				
			||||||
 | 
					        self.server = server
 | 
				
			||||||
 | 
					        self._client = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @property
 | 
				
			||||||
 | 
					    def client(self) -> Client:
 | 
				
			||||||
 | 
					        if not self._client: 
 | 
				
			||||||
 | 
					          self._client = self.server.getAppClient("p2system", "SysConf")
 | 
				
			||||||
 | 
					        return self._client
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def clearCache(self) -> None:
 | 
					    def clearCache(self) -> None:
 | 
				
			||||||
        self.cache = {}
 | 
					        self.cache = {}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,6 +21,7 @@ APplus. Oft ist es sinnvoll, solche Parameter zu verwenden.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from __future__ import annotations
 | 
					from __future__ import annotations
 | 
				
			||||||
import datetime
 | 
					import datetime
 | 
				
			||||||
 | 
					import numpy
 | 
				
			||||||
from typing import Set, Sequence, Union, Optional, cast, List
 | 
					from typing import Set, Sequence, Union, Optional, cast, List
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -158,7 +159,7 @@ def formatSqlValue(v: SqlValue) -> str:
 | 
				
			|||||||
    if v is None:
 | 
					    if v is None:
 | 
				
			||||||
        raise Exception("formatSqlValue: null not supported")
 | 
					        raise Exception("formatSqlValue: null not supported")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if isinstance(v, (int, float, SqlField)):
 | 
					    if isinstance(v, (int, float, SqlField, numpy.int64)):
 | 
				
			||||||
        return str(v)
 | 
					        return str(v)
 | 
				
			||||||
    elif isinstance(v, str):
 | 
					    elif isinstance(v, str):
 | 
				
			||||||
        return formatSqlValueString(v)
 | 
					        return formatSqlValueString(v)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user