SYSTEMKRITISCH.org


Cronjobs und Db2 Android Apps

Da ich in meinem beruflichen Umfeld regelmäßig mit Scheduled Jobs zu tun habe, muss es doch auch für meinen VPS mit Ubuntu eine ähnliche Lösung geben. Und genau so ist es - heißt nur anders: Cronjob.

Jetzt kann ich jede Nacht um 3 Uhr diverse Tabellen auf dem Testsystem leeren.

touch /home/user/clear_tables.sh

#!/bin/bash
SQLITE_DB="/path/to/your/database.db"

sqlite3 $SQLITE_DB <<EOF
DELETE FROM tabelle_1;
DELETE FROM tabelle_2;
EOF

echo "Tabellen geleert am $(date)" >> /var/log/cleanup.log

Das Skript selbst mache ich ausführbar mittels
chmod +x /home/user/clear_tables.sh

Jetzt muss nur noch der Cronjob eingerichtet werden:

crontab -e

0 3 * * * /home/user/delete_tables.sh

Speichern, fertig. Super einfach!

Alternativ würde das ganze auch mit einem systemd-Timer funktionieren.

Außerdem bin ich gerade ziemlich begeistert über den zeitlichen Aufwand aus der Kombination FastAPI + React Native. Ein Kunde hat mir beiläufig von einem Problem mit einem Dienstleister erzählt, das mich nicht losgelassen hat. Final geht es um eine App, mittels derer das Leergut, welches bei der Be- und Entladung getauscht wird, erfasst werden soll. Im Hintergrund steht eine Power i mit Db2. 

Und der Dienstleister bekommt es, aus welchen Gründen auch immer, nicht hin zu liefern. Das alles muss nicht mal schön sein - es muss nur funktionieren. Und so habe ich zu Testzwecken ein Setup erstellt, das meiner Meinung nach genau den Sinn und Zweck erfüllt. In quasi no time. Vielleicht schiebe ich ihm die .apk mal in einem ruhigen Moment zu Testzwecken rüber.

💬 Keine Kommentare


Softwarelösungen Soziale Arbeit

Wenn mein letzter Arbeitsplatz in der Verwaltung eines Trägers der Wohnungslosenhilfe für eines gut war, dann doch für den Blick auf die, bis in die Absurdität getriebenen, analogen Prozesse. Und den - zum Teil daraus resultierenden - Papierbergen. Ob es Briefe von Behörden waren, eigene Abrechnungen oder das Sammeln von Belegen, Verträgen und Abmachungen. Räume voller Leitz-Ordner-Wänden bis unter die Decke. Und ein Archiv. Das war eine Wohnung, die eigens für noch mehr Ordner (< 10 Jahre) angemietet wurde. Am anderen Ende der Stadt.

Abgehängt war nicht nur die Idee eines papierlosen Büros, sondern auch der zweite Standort von der Dokumentationssoftware auf dem eigenen Server. Ich behaupte immer noch, dass es leichter und günstiger gewesen wäre, einen VPN-Tunnel einzurichten, als mittels einem IT-Systemhauses die Betriebssystem-Cloud-Inception zu forcieren.

Jedenfalls löse ich gerade all die Probleme, die mir in dem Umfeld begegneten, über die ich täglich stolperte und mir den letzten Nerv geraubt haben und werde die Lösung kostenlos zur Verfügung stellen. Gerade hat das Produkt das Niveau "geeignetes Dokumentationssystem für eine Beratungsstellen" erreicht. In einem nächsten Schritt will ich noch ein Verwaltungsmodul einhängen um Dienstpläne, möglichst gerecht randomisiert (aber editierbar) auszuwerfen.

Ab dann folgt die Programmierung verschiedenster Module, die mittels einem Klick aktiviert werden können. Ein Hauptaugenmerk liegt in der Sicherheit (Stichwort: Datenschutz) und Nachvollziehbarkeit. Denn auch gelöschte Einträge müssen erkennbar sein (von wem wurden Einträge wann editiert oder in den Status "inaktiv" gesetzt?).

Die Testumgebung befindet sich ständig online - bei Interesse daran bitte einfach über "Kontakt" melden für einen Testzugang.

💬 Keine Kommentare


Automatisierung mit Python

Es ist gar nicht so lange her, als ich relativ viel Zeit damit verbracht habe, Änderungen an eigenen Projekten mittels Filezilla oder dem cpanel File Manager zeitintensiv produktiv zu bringen. Und ich hielt es die letzten Tage schon für einen großen Wurf mich einfach auf dem VPS einzuwählen, gunicorn und nginx zu stoppen, das Repo zu pullen und alle Services wieder zustarten. Die Downtime lag dabei schon nur noch bei etwa 30 Sekunden.

Heute kam mir jedoch die Idee mittels Python ein Skript zu schreiben, welches den kompletten Prozess nach dem Speichern abbildet, um die Änderungen instant live zu bringen mit Git und GitHub. Folgende Schritte habe unternommen, um das Projekt einzurichten:

mkdir Deployment
cd Deployment
python3 -m venv venv
source venv/bin/activate
pip3 install paramiko

touch deploy.py

Die deploy.py sieht wie folgt aus:

import os
import subprocess
import datetime
import paramiko

def log_message(message):
    log_file = "deploylog.txt"
    timestamp = datetime.datetime.now().strftime("%y-%m-%d %H:%M:%S")
    with open(log_file, "a") as f:
        f.write(f"{timestamp} - {message}\n")

def run_local_command(cmd, cwd):
    result = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True)
    if result.returncode != 0:
        raise Exception(f"Lokaler Befehl '{' '.join(cmd)}' schlug fehl:\n{result.stderr}")
    return result

def main():
    # Lokaler Ordner des Blogs
    blog_dir = "/pfad/zum/Blog/"
    try:

        # Schritt 1: Git-Befehle lokal ausführen
        print("Führe 'git add .' aus ...")
        run_local_command(["git", "add", "."], blog_dir)

        # Erzeuge den Commit-Text mit aktuellem Datum und Uhrzeit im Format "JJ-MM-TT HH:MM"
        commit_message = datetime.datetime.now().strftime("%y-%m-%d %H:%M")
        print(f"Führe 'git commit -m \"{commit_message}\"' aus ...")
        commit_result = subprocess.run(
            ["git", "commit", "-m", commit_message],
            cwd=blog_dir,
            capture_output=True,
            text=True
        )

        # Wenn nichts zu committen ist, liefert git einen entsprechenden Hinweis.
        if commit_result.returncode != 0:
            if "nothing to commit" in commit_result.stderr.lower() or "nothing to commit" in commit_result.stdout.lower():
                print("Nichts zu committen.")
            else:
                raise Exception(f"Git commit schlug fehl:\n{commit_result.stderr}")

        print("Führe 'git push' aus ...")
        run_local_command(["git", "push"], blog_dir)

        # Schritt 2: Auf den VPS verbinden
        vps_ip = "ip"           # Ersetze "ip" durch die tatsächliche IP-Adresse
        vps_password = "
passwort"  # Das Passwort für den VPS
        print(f"Stelle SSH-Verbindung zu {vps_ip} her ...")
        ssh = paramiko.SSHClient()
        ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        ssh.connect(vps_ip, username="
benutzer", password=vps_password)

        # Schritt 3: Remote-Befehle ausführen
        remote_commands = [
            "sudo service gunicorn stop",
            "sudo service nginx stop",
            "cd
/pfad/zum/Blog/ && git pull",
            "sudo service gunicorn start",
            "sudo service nginx start"
        ]

        for command in remote_commands:
            print(f"Führe remote Befehl '{command}' aus ...")
            stdin, stdout, stderr = ssh.exec_command(command)

            # Warten, bis der Befehl beendet ist und den Exit-Status ermitteln
            exit_status = stdout.channel.recv_exit_status()
            if exit_status != 0:
                error = stderr.read().decode()
                raise Exception(f"Remote Befehl '{command}' schlug fehl (Exit-Status {exit_status}):\n{error}")

        ssh.close()
        print("Deployment erfolgreich durchgeführt.")
        log_message("Deployment erfolgreich durchgeführt.")

    except Exception as e:
        log_message(f"Deployment fehlgeschlagen: {e}")
        print("Fehler beim Deployment:", e)

if __name__ == "__main__":
    main()

Das ganze funktioniert super und erzeugt mir auch einen Log. Die Einträge von heute:

25-02-07 21:15:48 - Deployment erfolgreich durchgeführt.
25-02-07 21:16:33 - Deployment erfolgreich durchgeführt.

Nachteile:

Ich habe git add . und git commit -m "JJ-MM-TT HH:MM" in den Ablauf eingebaut und kann so nicht mehr anhand des Commits erkennen, was geändert wurde. Sinnvollerweise setzt man den Prozess nach git push an. Und es ist nicht wirklich intelligent den Benutzernamen und das Passwort im Klartext irgendwo abzulegen.

Update: Ich habe das Script um die Eingabe eines benutzerdefinierten Commit-Textes und um makemigrations & migrate ergänzt. Einsehen kann man das hier.

💬 Keine Kommentare


Db2 und JSON

Ein kleiner Reminder, wie angenehm es ist, mit JSON umzugehen. Für mich ist ja alles neu. Und so stand ich vor dem Problem, dass ich einen JSON-String in Form eines QR-Codes bekomme und damit umgehen muss. Klassisch Kopf und Positionen.

Den JSON legen wir nach dem Scannen in einer Tabelle ab mit Identifier und ich habe folgende Lernkurven:

SELECT JSON_VALUE('JSON-String', '$.POS[0].Feld' ) AS Feldname
FROM sysibm.sysdummy1;

Anstelle von 'JSON-String' steht bei mir sowas wie (select JSONFELD from Tabelle where id = :identifier). So komme ich, in diesem Falle für die erste Position im JSON, an den Wert aus dem Positionsfeld.

Ich kann allerdings auch mit dem kompletten JSON als Tabelle umgehen und zwar so, wie man es kennt. Und Zeile für Zeile bearbeiten:

SELECT * --oder Feld/er
-- INTO :Variable/n
FROM JSON_TABLE( 'JSON-String', '$.POS[*]'                -- * = alle Positionen für das Resultset
     COLUMNS ( FELD1 VARCHAR(100) PATH '$.FELD1',
                          FELD2 VARCHAR(100) PATH '$.FELD2',
                          FELD3 VARCHAR(100) PATH '$.FELD3',
                          FELD4 VARCHAR(100) PATH '$.FELD4',
                          FELD5 VARCHAR(100) PATH '$.FELD5',
                          FELD6 VARCHAR(100) PATH '$.FELD6',
                          FELD7 VARCHAR(100) PATH '$.FELD7'
                          )
)
AS t
-- WHERE FELD1 = "x" -- AND FELD2 = "y" -- ORDER BY FELD3 etc.

Und da wird das ganze dann schon sehr bequem. Wie gesagt: nur ein kleiner Reminder für mich. Ich stand nämlich vor dem Thema "Übergabe und Verarbeitung von JSON" und wusste erstmal gar nichts. Hatte also auch keine Idee für den Umgang damit. Jetzt kann ich das mit SQL und COBOL einmal durchverarbeiten.

💬 Keine Kommentare


Systemkritisch 5.0

Hui. Runde zwei mit Python. Diesmal ohne User Auth und wilden Web Applikationen. Aber grundsätzlich bin ich sehr zufrieden, wenngleich es ein paar Startschwierigkeiten gab, alles zum Laufen zu bringen. Statt, wie beim letzten Mal, Shared Host + cpanel, läuft es diesmal über einen selbstkonfigurierten VPS (Virtual Private Server) in der sehr angenehmen Kombination aus Django + gunicorn + Nginx. Da ich gerade an eigenen Lösungen arbeite, hat mich der - skalierbare - Weg des Bereitstellens doch nochmal interessiert.

Den Blog selbst habe ich in 30 Minuten hingeklatscht, weshalb es noch ein paar offene To-Do-Punkte gibt. Die Umstellung der Datenbank zum Beispiel. Favicon. Sowas eben. smiley Das Bereitstellen ist jetzt aber schon deutlich angenehmer, da ich einfach aus dem Repository pulle. Wenn ich alles stabil habe, sehe ich mir Docker und Kubernets für eine kontinuierliche Entwicklung und Bereitstellung an.

Historie:
Version 1: HTML, CSS
Version 2: WordPress
Version 3: Django (Shared Host + cpanel)
Version 4: WordPress
Version 5: Django (VPS + gunicorn + Nginx)

💬 Keine Kommentare