jueves, 28 de noviembre de 2024

Python utilizar librerias java (INCOMPLETO)

 Para ello se opta por utilizar Pyjnius, py4j, jpype, scyjava ..


1. Descargar los jars a una carpeta

Primeramente se instala maven en ubuntu:

sudo apt install maven -y

Creamos una carpeta que será la de nuestro proyecto, nos situamos dentro de ella

Vamos a mavencentral y buscamos nuestra dependencia por ejempo itext7 y descargamos el pom.xml en la carpteta


Veamos un ejemplo de pom.xml para poder descargar la libreria itext7

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>mi-proyecto-itext</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <itext.version>9.0.0</itext.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>com.itextpdf</groupId>
      <artifactId>itext7-core</artifactId>
      <version>${itext.version}</version>
      <type>pom</type>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
        <configuration>
          <release>17</release>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>


Ejecutamos 

mvn dependency:copy-dependencies -DoutputDirectory=target/dependencies

si no se especifica -DoutputDirectory entonces lo descarga en target/dependencies

o tambien

mvn dependency:resolve -U

pero las copia en ~/.m2/repository/com/itextpdf/itext7-core/7.2.5/itext7-core-7.2.5.jar

Y ya tenemos las dependencia en el directorio que hemos indicado. Ahora falta usar dichos jars en python


2. Usar las librerias en python

 

Python obtener el valor de un nodo de un fichero xml que tiene namespaces

 Queremos extraer el contenido del nodo <ValorBinario> de este fichero xml que namespaces: 

Nos fijamos que el namespace marcado en naranja no tiene nombre, por tanto se aplicará a todas los nodos cuyas etiquetas no tenganprefijo del namespace como "contenido" o "ValorBinario"


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<insidews:documento xmlns="http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido" 
    xmlns:ns2="http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos" 
    xmlns:ns3="http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma" 
    xmlns:ns4="http://www.w3.org/2000/09/xmldsig#" 
    xmlns:ns5="http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e" 
    xmlns:ns6="https://ssweb.seap.minhap.es/Inside/XSD/v1.0/metadatosAdicionales" 
    xmlns:ns7="https://ssweb.seap.minhap.es/Inside/XSD/v1.0/documento-e" 
    xmlns:insidews="https://ssweb.seap.minhap.es/Inside/XSD/v1.0/WebService">
    <ns5:documento Id="ES_L01462384_2022_2_ACORDS_PLE000001">
        <contenido Id="CONTENIDO_DOCUMENTO">
            <ValorBinario>JVB..</ValorBinario>
            <NombreFormato>PDF</NombreFormato>
        </contenido>
        <ns2:metadatos Id="DOC_ES_L01462384_2022_2_ACORDS_PLE000001_METADATOS">
            <ns2:VersionNTI>http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e</ns2:VersionNTI>
            <ns2:Identificador>ES_L01462384_2022_2_ACORDS_PLE000001</ns2:Identificador>
            <ns2:Organo>L01462384</ns2:Organo>
            <ns2:FechaCaptura>2024-11-05T00:00:00.000+01:00</ns2:FechaCaptura>
            <ns2:OrigenCiudadanoAdministracion>true</ns2:OrigenCiudadanoAdministracion>
            <ns2:EstadoElaboracion>
                <ns2:ValorEstadoElaboracion>EE99</ns2:ValorEstadoElaboracion>
            </ns2:EstadoElaboracion>
            <ns2:TipoDocumental>TD02</ns2:TipoDocumental>
        </ns2:metadatos>
        <ns3:firmas>
	   <ns3:firma Id="FIRMA_1">
                <ns3:TipoFirma>TF06</ns3:TipoFirma>
                <ns3:ContenidoFirma>
                    <ns3:FirmaConCertificado>
                        <ns3:ReferenciaFirma xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">#CONTENIDO_DOCUMENTO</ns3:ReferenciaFirma>
                    </ns3:FirmaConCertificado>
                </ns3:ContenidoFirma>
            </ns3:firma>
            <ns3:firma Id="FIRMA_2">
                <ns3:TipoFirma>TF06</ns3:TipoFirma>
                <ns3:ContenidoFirma>
                    <ns3:FirmaConCertificado>
                        <ns3:ReferenciaFirma xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">#CONTENIDO_DOCUMENTO</ns3:ReferenciaFirma>
                    </ns3:FirmaConCertificado>
                </ns3:ContenidoFirma>
            </ns3:firma>
        </ns3:firmas>
    </ns5:documento>
    <ns7:metadatosAdicionales>
        <ns6:MetadatoAdicional tipo="string" nombre="eEMGDE3.1.Nombre.NombreNatural">
            <ns6:valor xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">$5.NOM_DOC$</ns6:valor>
        </ns6:MetadatoAdicional>
    </ns7:metadatosAdicionales>
</insidews:documento>


para ello se ha creado este programa python

import base64
from lxml import etree

def extraer_pdf(xml_path, output_pdf_path):
    try:
        # Cargar el archivo XML
        with open(xml_path, "rb") as xml_file:
            xml_data = xml_file.read()
        
        # Parsear el XML
        tree = etree.fromstring(xml_data)
        
        # Definir los namespaces utilizados en el XML
        namespaces = {
            "insidews": "https://ssweb.seap.minhap.es/Inside/XSD/v1.0/WebService",
            "ns5": "http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e",
            "contenido": "http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido"
        }
        
        # Listar nodos para depuración
        print("Nodos disponibles:")
        for elem in tree.iter():
            print(f"{elem.tag}")

        # Buscar el contenido del tag <ValorBinario>
        valor_binario_node = tree.xpath(
            "//insidews:documento/ns5:documento/contenido:contenido/contenido:ValorBinario",
            namespaces=namespaces
        )
        
        if not valor_binario_node:
            raise ValueError("No se encontró el tag <ValorBinario> en el documento XML.")
        
        # Extraer el contenido en Base64
        valor_binario = valor_binario_node[0].text
        
        # Decodificar el contenido Base64
        pdf_data = base64.b64decode(valor_binario)
        
        # Guardar el contenido en el archivo PDF
        with open(output_pdf_path, "wb") as pdf_file:
            pdf_file.write(pdf_data)
        
        print(f"Archivo PDF extraído correctamente y guardado en: {output_pdf_path}")
    
    except Exception as e:
        print(f"Error al procesar el archivo XML: {e}")


# Ruta al archivo XML y al PDF de salida
xml_path = "/home/eduard/MyPython/02.llibreriesOLD/ximo-python-library/ES_L01462384_2022_2_ACORDS_PLE000001.xml"
output_pdf_path = "mifichero.pdf"

# Ejecutar la función
extraer_pdf(xml_path, output_pdf_path)


Y cuando nos lista los nodos aparece

{https://ssweb.seap.minhap.es/Inside/XSD/v1.0/WebService}documento
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e}documento
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido}contenido
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido}ValorBinario
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido}NombreFormato
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}metadatos
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}VersionNTI
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}Identificador
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}Organo
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}FechaCaptura
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}OrigenCiudadanoAdministracion
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}EstadoElaboracion
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}ValorEstadoElaboracion
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/metadatos}TipoDocumental
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}firmas
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}firma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}TipoFirma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}ContenidoFirma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}FirmaConCertificado
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}ReferenciaFirma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}firma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}TipoFirma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}ContenidoFirma
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}FirmaConCertificado
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/firma}ReferenciaFirma
{https://ssweb.seap.minhap.es/Inside/XSD/v1.0/documento-e}metadatosAdicionales
{https://ssweb.seap.minhap.es/Inside/XSD/v1.0/metadatosAdicionales}MetadatoAdicional
{https://ssweb.seap.minhap.es/Inside/XSD/v1.0/metadatosAdicionales}valor

A raiz de todo esto vemos que en el fichero xml, la ruta a <ValorBinario> es:

<insidews:documento><ns5:documento><contenido><ValorBinario>

Por otra parte si reducimos el listado anterior:

{https://ssweb.seap.minhap.es/Inside/XSD/v1.0/WebService}documento
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e}documento
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido}contenido
{http://administracionelectronica.gob.es/ENI/XSD/v1.0/documento-e/contenido}ValorBinario

Vemos que contenido si tiene un namespace que es "contenido" y tambien ValorBinario tiene el namespace "contenido". Por tanto la ruta para llegar a valor binario es:


# Buscar el contenido del tag <ValorBinario>
valor_binario_node = tree.xpath(
   "//insidews:documento/ns5:documento/contenido:contenido/contenido:ValorBinario",
   namespaces=namespaces
)








miércoles, 20 de noviembre de 2024

Python geolocalizacion html y java script

 Veamos como se resuelve a geolocalización con este programa de ejemplo:


<!DOCTYPE html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Geolocalización del Usuario</title>
</head>
<body>
    <h1>Obtener Geolocalización</h1>
    <button onclick="obtenerUbicacion()">Obtener mi ubicación</button>
    <p id="resultado"></p>

    <script>
        function obtenerUbicacion() {
            const resultado = document.getElementById("resultado");

            if ("geolocation" in navigator) {
                resultado.textContent = "Obteniendo ubicación...";
                
                navigator.geolocation.getCurrentPosition(
                    function(posicion) {
                        const latitud = posicion.coords.latitude;
                        const longitud = posicion.coords.longitude;
                        resultado.innerHTML = `Latitud: ${latitud}<br>Longitud: ${longitud}`;
                    },
                    function(error) {
                        switch(error.code) {
                            case error.PERMISSION_DENIED:
                                resultado.textContent = "Usuario denegó la solicitud de geolocalización.";
                                break;
                            case error.POSITION_UNAVAILABLE:
                                resultado.textContent = "La información de ubicación no está disponible.";
                                break;
                            case error.TIMEOUT:
                                resultado.textContent = "Se agotó el tiempo de espera para obtener la ubicación.";
                                break;
                            case error.UNKNOWN_ERROR:
                                resultado.textContent = "Ocurrió un error desconocido.";
                                break;
                        }
                    }
                );
            } else {
                resultado.textContent = "La geolocalización no está soportada por este navegador.";
            }
        }
    </script>
</body>
</html>



martes, 19 de noviembre de 2024

Python Ejecutar un fichero sh (bash de linux) que utilice un entorno virtual y ejecute un programa python. Ojo con los parámetros tipo "regex pattern"!!!

1. Introducción

Pasos:

  1. Desde el programa python creamos un array que contenga la ruta del script (bash de linux) como primer componente. Cada componente restante estará formada por la cadena "clave=valor", donde las comillas dobles forman parte del parámetro. Ojo los espacios en blanco pueden dar problemas, para ello se pueden sustituir por algúna cadena , en mi caso he utilizado "¬spc¬"(sin colillas dobles)
  2. Desde el script de linux verificamos que recibimos obligatoriamente los parámetros "folder", "module","funcion" y "venv". Siendo "folder" la carpeta donde tenemos que situarnos, "venv" la carpeta dentro de folder donde está el entrono virtual, "module" el módulo a llamar y "function" la función del módulo a ejecutar. En el script cambiamos a la carpeta definida en la variable "folder" y activamos el entorno virtual de la variable "venv" y ejecutamos  pyton xmexec.py "module=miModulo" "function=miFuncion" ..... 
  3. El módulo xmexec.py que se encuentra en la carpeta "folder" se encarga de recibir los parámetros, arreglar los mismos sustituyendo "¬spc¬" por espacios en blanco, cargando el módulo indicado en el parámetro "module" y ejecutando la función indicada  "function".

2. Llamada al script desde python

Para ello supongamos que tenemos un diccionario con el nombre del script, del módulo, función a ejecutar, entorno virtual y otros parámetros.
Sustituimos los espacios en blanco por "¬spc¬" y construimos el array para llamar a "subproces.run".
Observar que el modulo se puede definir como:
  • "carpeta.modulo" ó
  • "carpeta/modulo.py"
siendo "carpeta" la carpeta que contien a "modulo.py" que vamos a llamar

El script a llamar está en la carpeta "static/scripts" y se llama "execShell.sh"


import subprocess

# Diccionario que contiene los parámetros PARA EL SHELL SCRIPT
execDict={
	'shell':'static/scripts/execShell.sh',
	'folder':'../02.llibreries',
	'venv':'/static/scripts/llibreries.sh',
	'module':'eni/xmexpeni.py',
	#'module':'eni.xmexpeni',
	'function':'expedientENI',
	'param1':'1814787N',
	'param2':12,
	'param3':'TD02',
	'param4':'INDEX_ACTES_PLE',
	'param5':'ENIExpTemplate.xml',
}

if __name__ == '__main__':
	# Recogemos la ruta del shell y lo quitamos del diccionario
	myShell=execDict['shell']
	del execDict['shell']
	execProgs=[myShell]
	
	#Creamos la cadena con los parámetros paraejecuar
	for key,value in execDict.items():
		value1=value.replace(" ","¬spc¬")
		st=f'"{key}={value1}"'
		execProgs.append(st) #Exec progs.

	print(str(subprocess.run(execProgs, capture_output=True)))


3. Llamada al programa xmexec.

Desde el script vamos a llamar al programa pyhon xmexec, paa ello:
  1. Verificamos que recibimos los parámetros "folder", "venv", module" y "function"
  2. Nos situamos a la carpeta del parámetro "folder"
  3. Activamos el entorno virtual de la carpeta indicada en el parámetro "venv"
  4. Ejecutamos "python" al que pasamos el propgrama xmexec.py y el resto de parámetros (excepto "venv" y "folder") 
#!/bin/bash

#--------------------------
# 0. Exemple de crida
# ./static/scripts/llibreries.sh \
#  "folder=../02.llibreries" "venv=venv02" \
#  "module=eni.xmexpeni" "function=expedientENI" \
#  "docsFolderPath=docs/actes2022/" \
#  "templatesPath=templates/ENI/" \
#  "filter=*ord*aprov*sio*_signed.pdf" \
#  "anyPle=2022" \
#  "expCodi=1814787N" \
#  "organo=L01462384" \
#  "tDocu=TD02" \
#  "prefPle=INDEX_ACTES_PLE" \
#  "plantillaXML=ENIExpTemplate.xml" \
#  "docxIndex=indexTemplate.docx"
#
#
# O també podriem executar en background
# Run the Python script in the background
#  nohup python myscript.py > /dev/null 2>&1 &
#
#---------------------------

remove_quotes() {
    local arg="$1"
	# Remove leading double quote if present
    arg="${arg#\"}"
	# Remove leading double quote if present
    arg="${arg%\"}"
    echo "$arg"
}

#------------------------------------
# 1. Recogemos los parametros
#------------------------------------

# Inicialize variables
folder_value=""
venv_value=""
module_value=""
other_params=""

# Función para mostrar uso del script
usage() {
  echo "Uso: $0 'folder=RUTA'  'venv=carpeta entorno virtual' 'module=carpeta.modulo.py'  ... otros_parametros=VALOR ..."
  echo "Ejemplo:"
  echo "  $0 folder=/mi/carpeta env=producción venv=venv02 module=eni.xmexpeni config=ejemplo version=1.0"
  exit 1
}

# Verificar si se proporcionaron argumentos
if [ $# -lt 3 ]; then
  usage
fi

# Procesar cada argumento
for argIni in "$@"; do
  # Remover comillas
  arg=$(remove_quotes "$argIni")
  
  # Verificar si el argumento contiene un '='
  if [[ "$arg" == *=* ]]; then
    # Dividir el argumento en clave y valor
    key="${arg%%=*}"
    value="${arg#*=}"
	#echo "$key    AAAA   $value  AAAAA  $arg"
	
    case "$key" in
      folder)
        folder_value="$value"
        ;;
      venv)
        venv_value="$value"
        ;;
	  module) 
        module_value="$arg"
		;;
      *)
        # Concatenar otros parámetros
        other_params+=" \"$arg\""
        ;;
    esac
	#echo "$key    BBBBB   $value"
  else
    echo "Argumento inválido: $arg"
    usage
  fi
done

#echo "FOLDER=  $folder_value"
#echo "VENV= $venv_value"
#echo "MODULE= $module_value"

# Verificar que 'folder' y 'env' hayan sido proporcionados
if [[ -z "$folder_value" || -z "$venv_value" || -z "$module_value" ]]; then
  echo "Error: Se requieren al menos un argumento 'folder' 'venv'y 'module'."
  usage
fi


#--------------------------------------
# 2. Nos cambiamos a la carpeta del modulo
#-------------------------------------
pwd
cd "$folder_value"
pwd

#--------------------------------------
# 3. Activamos el entorno virtual
#-------------------------------------
source "$venv_value"/bin/activate


# Crear la cadena concatenada
final_string1="python xmexec.py \"${module_value}\" ${other_params}"
echo "--------------------------------------"
echo "FINAL STRING=$final_string1"
echo "--------------------------------------"
python xmexec.py \"${module_value}\" ${other_params}


4. Verificar que la carpeta del entrono virtual "venv" está creado dentro de la carpteta "folder"


5. Crear el programa python "xmexec.py" en la crpeta folder.

Este programa:
  1. Recoge los argumentos y cambia "¬spc¬ por espacio " "
  2. Carga el módulo "module" y
  3. Ejecuta la función con el diccionario que recoge todos los parámetrso menos "module" y "function" 
'''
Execute a function from a module
paramenters:
	"module= module name"
	"function= function name"		
	"param1 = value1"
	"param2 = value2"
	"param_n = value_n"

'''
import importlib
import sys

requiredProps=['module','function']

def arreglaArg(arg:str)->str:
	if arg.startswith('"') or arg.startswith("'"):
		arg=arg[1:] 
	if arg.endswith('"') or arg.endswith("'"): 
		arg=arg[:-1]
	arg=arg.replace('¬spc¬',' ')	
	return arg

if __name__ == '__main__':
	argDict={}
	for i, arg1 in enumerate (sys.argv):
		arg=arreglaArg(arg1)
		#print(i, "--", arg1, '===', arg)
		if i>0:
			aArg=arg.split('=')
			#print(aArg)
			argDict[aArg[0]]=aArg[1]
			

	keys=argDict.keys()
	for prop in requiredProps:
		if prop not in keys:
			raise Exception (prop + ' not in arguments')
		
	moduleName=argDict['module'].replace('.py','').replace("/",".")
	#print (moduleName)
	#module = __import__(argDict['module']) # No acaba d'anar be
	module = importlib.import_module(moduleName)
	#print (module)
	aFunc=argDict['function']
	func = getattr(module, aFunc)
	
	# Remove module and function props
	for prop in requiredProps: del argDict[prop]


	# Execute the function with arguments
	#print ("executing the function "+ aFunc)
	#print (argDict)
	#print ("=======================================================")
	func (**argDict)
	

6. Verificaciones

Verificar que :
  1. Exista el módulo dentro de la carpeta correcta
  2. Que el módulo tenga la funcion que vamos a llamar
  3. Que los parámetros de la fucnión tengan los mismos nombres y sean del mismo tipo que los parámetros que le pasamos.

7. Parámetros regex pattern


Si los parámetros "regex pattern" pueden estar definidos en un fichero u¡yaml o dentro del programa python

Por ejemplo en este fichero yaml hay 2 elementos tipo pattern.

pattern1: '^Ple \d{2} ord \d{2}-\d{2}-\d{4} .* \d{2} \d{2} \d{4}(?:_|\.)signed(?:_|\.)signed\.pdf$'

params: [1, '^Acta d{2} .* \d{2} \d{2} \d{4}.pdf$', 'otro valor']

Para el caso de der definidos en un progama python vale la pena  utilizar un string "rau" que se hace  anteponiendo r al string

pattern = r'^Acta d{2} .* \d{2} \d{2} \d{4}.pdf$'


Los posibles problemas que pueden aparecer son:
  1. Las interrogaciones "?"
  2. Los espacios en blanco
Las interrogaciones pueden dar problemas al convertirlas en una cadena dela URL, mientras que los espacios en blanco pueden dar problemas al pasarlos a un script como parámetro, pues los trocea. 

Para el caso de las interrogaciones, se puede pasar el parámetro codificado en base64 y a veces aún así puede fallar. A un caso se pueden cambiar por "¬qm¬" y luego deshacer el cambio. Para el caso de los espacios hemos optado por sustituirlos por "¬spc¬".
 



viernes, 15 de noviembre de 2024

tkinter python: se ejecutan los los eventos cuando se crea el botón por error. Parametro command

Este código ejecuta la función cuando se crea el botón:

tk.Button(root, text="Execute", command=execute_program(action))

Para solucionarlo según Spataner NO hay que asignar el command a una función con paréntesis. Por tanto quedaria así

  1. Para eliminar los paréntesis hay que crear una función que no tome argumentos y por tanto debe poder llamar a la función deseada con los argumentos pedidos
  2. Cambiar la función desesada por la nueva función

def myFunction():  execute_program(action)

tk.Button(root, text="Execute", command=myFunction)



jueves, 14 de noviembre de 2024

Python Crear un fichero bat (windows) que utilice un entorno virtual y ejecute un programa python

0. Introducción

Se pretende que la máquina cliente pueda ejecutar un programa en el servidor sin tener instalado python localmente. Para ello se realizan los siguientes pasos:
  1. Instalar WinPython que es un ejecutable que no requiere instalación de librerías.
  2. Crear un entorno virtual de WinPython
  3. Crea un fichero batch que active el entorno virtual y ejecute el programa python en cuestión 

1. Instalar WinPython

Se descarga en la carpeta compartida del servidor. En mi caso el fichero es Winpython64-3.12.4.1.exe

Ejecutamos dicho fichero, el cual crea a carpeta WPy64-31241

Dentro de dicha carpeta hay varias carpetas (n, notebooks, python-3.12.4.amd64, scripts, settings y t) y varios ficheros.

2. Crear entorno virtual

Supongamos que se ha compartido para ello una carpeta en el servidor y la hemos mapeado a la unidad Y, y queremos que el entorno virtaul esté en Y:\weenv

El ejecutable de python está en la carpeta python-3.12.4.amd64, Nos situamos en dicha carpeta y ejecutamos

Y:\WPy64-31241\python-3.12.4.amd64>python.exe -m venv Y:\wvenv

y contesta

Actual environment location may have moved due to redirects, links or junctions.
  Requested location: "Y:\wvenv\Scripts\python.exe"
  Actual location:    "\\srv-softprop2\Prog_PY\wvenv\Scripts\python.exe"

Activamos el entorno virtual e instalamos las dependencias:

Y:\venv\Scripts\activate.bat
pip install -r requirements.txt

Si no tenemos el requirements, podemos ejecutr una a una utilizando "pip install dependencia"

3. Crear el script que activa el entorno virtual y ejecuta dicho programa

Según ChatGPT tenemos este script (menu_soft.bat) que activando el entorno virtual venv ejecuta el programa python menu_soft.py

El "pause" que hay al final hay que quitarlo una vez comprabado que funcione el script.

Hay algunos ordenadores que no va bien dicho script y no he averiguado porqué.

@echo off

REM No podem fer un cd //server01/Prog_PY. Conseguir la carpeta del programa
set SCRIPT_FOLDER=%~dp0
pushd %SCRIPT_FOLDER%

REM Path to the virtual environment
set WINPYTHON_ACTIVATE=wvenv\Scripts\activate.bat

REM Path to Python.exe
set PYTHON_EXE=wvenv\Scripts\python.exe

REM Path to the Python program
set PYTHON_PROGRAM=menu_tk.py

REM Activate the virtual environment
call %WINPYTHON_ACTIVATE% 

REM Check if activation succeeded
if errorlevel 1 (
    echo Failed to activate virtual environment.
    exit /b 1
)

call %PYTHON_EXE% %PYTHON_PROGRAM%

REM Deactivate the virtual environment
deactivate

pause


4. Crear un acceso directo en el ordenador del cliente

Supongamos que el servidor tien de nombre "server01", y la carpeta compartida tiene el nombre "Prog_PY"

Ahora en el ordenador cliente mapeamos una unidad de red a la carpeta compartida de este servidor:

  1. Conectar a una unidad de red
  2. Le damos el valor de la unidad "P" que será la que deben tener cualquier ordenador que quiera ejecutar el programa. Pued3e ser otra letra, pero será la misma para todos los ordenadores que se conecten.
  3. La ruta remota a mapear esta letra "P" es //server01/Prog_PY tal como hemos dicho
Ahora tenenos que crear un acceso directo a ese fichero bat y lo metemos en el escritorio. La ruta será

P:/menu_soft.bat





Y si ejecutamos aparece la pantallita:








martes, 12 de noviembre de 2024

VS Code: Recuperar ficheros borrados. Enganchar con Gitlab

 1. Recuperar ficheros borrados

Ir al explorador de archivos como muestra el dibujo y desplazarse hasta el último fichero que muestra y justo debajo, en la pequeña zona en blanco, hacer click izquierdo y "crtl z" y nos aparecerá por arte de magia el fichero borrado.

En la imagen el último fichero es "xmexec.py" y justo debajo hacemos  click izquierdo y "crtl z


2. Enganchar una carpeta de un proyecto VS a GitLab



domingo, 3 de noviembre de 2024

SqlAlchemy (I) : Conexión, crer DB, Tablas y columnas. Ejecutar SQL a pelo

Veamos este código python (db.py)

import sqlalchemy as db
import yaml

#1. Read data from the static/tables folder and Create tables
#1.a Read the tables information from file
def getTables(tablesFilePath: str='static/tables/prov.yml')->list:
    with open(tablesFilePath) as f: 
        tableDict=yaml.safe_load(f)	
    return tableDict

#1.b Create Columns of the Table
def getColumn(col: list)->db.Column:
    lst=[]
    aDict={}
    for i, elem in enumerate(col):
        if i==0: lst.append(elem)
        elif i==1: 
            sublst=elem.split('(')
            type =sublst[0]
            lenOrFkey='255'
            if len(sublst)>1:
                lenOrFkey=sublst[1].replace(')','')
            match type.lower():
                case 'int' | 'integer': lst.append(db.Integer())
                case 'float': lst.append(db.Float)
                case 'bool': lst.append(db.Boolean)
                case 'str' | 'string' | 'char' | 'varchar' : lst.append(db.String(int(lenOrFkey))) 
                case 'date': lst.append(db.Date())
                case 'timestamp': lst.append(db.TIMESTAMP())
                case 'json': lst.append(db.JSON())
                case 'foreignkey' : lst.append(db.ForeignKey(lenOrFkey))
    else: aDict |= elem
    column=db.Column(*lst, **aDict)    
    print (str(column))
    return column          
                
#1.c Create the tables from the information                
def getTable(table: dict, metadataObj: db.MetaData)->db.Table:
    return db.Table(table['name'], metadataObj, *[getColumn(col) for col in table['columns']])   


#2. Insert a list of elements in a table
#2.1 Using sqlAlchemist
def insert(table: db.Table | str, listDataDict: list[dict],conn: db.engine.base.Connection=None, commit:bool=False):
    """
    Insert a list of dictionaries in the given table.
    
    Parameters
    ----------
    table: db.Table | str
        Either a db.Table object or the name of the table
    listDataDict: list[dict]
        A list of dictionaries where each dictionary represents a row to be inserted
    conn: db.engine.base.Connection, optional
        The connection to use for the insert. If None, it will use the default engine.
    commit: bool, optional
        If True, will commit the transaction. Defaults to False.
    
    
    Returns
    -------
    A list of tuples of ids of the inserted rows.
    """
    mytable=db.Table('Student', metadataObj, autoload_with=conn.engine) if isinstance(table, str) else table
    query = db.insert(mytable).values(listDataDict).returning(table.c.id)
    Result = conn.execute(query)
    result=Result.fetchall()
    if commit: conn.commit()
    return result

#2.1 Using raw SQL
def insertSQL(table: str, listDataDict: list[dict],conn: db.engine.base.Connection=None, commit:bool=False):
    for aDict in listDataDict:
        fields='(' + ','.join(str(key) for key in aDict.keys()) + ')'
        values=fields.replace("(", "( :"). replace(",",", :") 
        command="INSERT INTO "+ table + fields + "VALUES "+ values 
        conn.execute(db.text(command), **aDict)
        if commit: conn.commit()
        
    
    
#3. List all elements in a table
def listAll(table: db.Table | str, conn: db.engine.base.Connection=None):
    mytable=db.Table('Student', metadataObj, autoload_with=conn.engine) if isinstance(table, str) else table
    query = db.select([mytable])
    result = conn.execute(query)
    return result

#3.1 Execute a command from SQL 
def execSQL(sql: str, conn: db.engine.base.Connection=None, commit: bool=False) ->list:   
    result=conn.execute(db.text(sql))  
    if commit: conn.commit()
    else: return result.fetchall()
    
            
engine=db.create_engine('sqlite:///test.db', echo=True)
conn=engine.connect()

metadataObj = db.MetaData() #extracting the metadata


#4. Construct the DB
'''
tableLst=getTables(tablesFilePath='static/tables/prov.yml')
dbTableLst=[]
for tableInfo in tableLst:
    table=getTable(tableInfo, metadataObj)
    print(repr(table))
    dbTableLst.append(table)
'''
sql="INSERT INTO Student (name, age, tutor) VALUES('Peret',14,1)"
execSQL(sql,conn,True)

sql="CREATE VIEW VWKK AS SELECT * FROM Student a join Teacher b on a.tutor=b.id"
execSQL(sql,conn,True)

result=execSQL('SELECT * FROM VWKK',conn)
#tuples=result.fetchall()
for t in result:
    print(str(t))
    
    
'''
query = db.insert(dbTableLst[0]).values(name='Matthew', subject="Quantum Mechanics").returning(dbTableLst[0].c.id)
Result = conn.execute(query)
print(Result.fetchone().id)   
query = db.insert(dbTableLst[0]).values(name='Motos', subject="Ampliació de matemàtiques").returning(dbTableLst[0].c.id)
Result = conn.execute(query)
print(Result.fetchone().id)   

query = db.insert(dbTableLst[1]).values(name='Matthew', age=18, tutor=1).returning(dbTableLst[1].c.id)
Result = conn.execute(query)
print(Result.fetchone().id)   
query = db.insert(dbTableLst[1]).values(name='Pepet', age=18, tutor=2).returning(dbTableLst[1].c.id)
Result = conn.execute(query)
print(Result.fetchone().id)   
conn.commit()

output = conn.execute(dbTableLst[0].select()).fetchall()
print(output)
output = conn.execute(dbTableLst[1].select()).fetchall()
print(output)

Student= db.Table('Student', metadataObj, autoload_with=engine) #Table object

#Teacher= db.Table('Teacher', metadata, autoload_with=engine) #Table object
#print(repr(metadata.tables['Student','Teacher']))

    
'''    


Esta es la configuración de las tablas en formato yml (static/tables/prov.yml)

 - name: Teacher
   columns:
      - [id, int, primary_key: true, autoincrement: true]
      - [name, str(50), nullable : false]
      - ['subject', str(50), default: Maths]


 - name: Student
   columns:
      - [id, int, primary_key: true, autoincrement: true]
      - [name, str(50), nullable : false]
      - [age, int, default: 15]
      - [tutor, ForeignKey(Teacher.id), nullable: false]