PHP zu Python: Optimierung eines vorhanden Scripts

Python in C/C++ embedden, C-Module, ctypes, Cython, SWIG, SIP etc sind hier richtig.
mmueller-87
User
Beiträge: 12
Registriert: Sonntag 11. Juni 2023, 07:25

Liebe Forengemeinde,

ich habe eine Frage an die Spezialisten unter euch. Mir ist Python nicht unbekannt, aber sehr lange her als ich das letzte mal damit was zutun hatte. Meine Skills in PHP und SQL sind hervorrangend. Ich habe eine Wetterstation bei mir im Garten stehen, die täglich Satellitenbilder von den Wettersatelliten NOAA, Meteor und Elektro aufnimmt und eine Webcam, die tägliche Zeitraffer Video anfertigt. Soweit funktioniert alles wunderbar. Nun möchte ich die empfangenden Bilder vom Orbit gerne auf meine Webseite veröffentlichen. Dies funktioniert bisher auch wunderbar.

Die Original Bilder werden im Format: NOAA-15-20250112-110801-sea.jpg gespeichert. Da ich für meine Seite ein anderes Format zwecks des Dateinamen benötige, habe ich ein PHP Script im Einsatz, das von der Datenbank (SQLite) die passenen Daten wie: Evelation, Datum, zeit, Dauer des Überflugs und und und abfragt. Diese Daten fange ich von der Datenbank ab und lege die Daten in einem Dateinamen, zB. so:

d_m_Y_H_i_noaa-15_s_40_140_E_mcir_137.9125000.jpg

Das realisiere ich mit einem PHP Script. Nun möchte ich das gerne auf Python3 realisieren. Habe auch schon angefangen und etliche Funktionen bzw. Strukturen sind in Python und PHP ziemlich ähnlich. Hier ist mein Grundgerüst (Funktionsfähig):

Code: Alles auswählen

import sqlite3
import os
import glob
from datetime import datetime

# wxmeaa_passes :
# + sat_name
# + pass_start
# + pass_end
# + max_elev
# + is_active
# + pass_start_azimuth
# + direction
# + azimuth_at_max
# + at_job_id

# wxmeaa_decoded :
# + pass_start
# + file_path
# + daylight_pass
# + is_noaa
# + sat_type
# + img_count
# + has_spectrogram
# + has_pristine
# + gain
# + has_polar_az_el
# + has_polar_direction
# + has_histogram
# d_m_Y_H_i_noaa-15_s_40_140_E_mcir_137.9125000.jpg

# Database
SQLiteDB = "~/db/panel.db"

# Connect to database
SQLite = sqlite3.connect(SQLiteDB)
SQLRow = SQLite.cursor()

# Sat frequencies
sat_freq = {}
sat_freq["meteor-m25"] = "137.900000"
sat_freq["meteor-m24"] = "137.900000"
sat_freq["meteor-m23"] = "137.900000"
sat_freq["noaa-19"] = "137.620000"
sat_freq["noaa-18"] = "137.912500"
sat_freq["noaa-15"] = "137.100000"

# Show detected passes from database
SQLRow.execute("""SELECT
   decoded_passes.id,
	predict_passes.pass_start,
   file_path,
   daylight_pass,
   sat_type,
   gain,
   predict_passes.sat_name,
   predict_passes.max_elev,
   predict_passes.pass_start_azimuth,
   predict_passes.direction,
   predict_passes.azimuth_at_max
     FROM
   decoded_passes
     INNER JOIN
   predict_passes
     ON
   predict_passes.pass_start = decoded_passes.pass_start
     ORDER BY
   decoded_passes.pass_start
     DESC""")

# SQL execute
Row = SQLRow.fetchall()

# Datenbank Schema: (935, 1736696460, 'NOAA-15-20250112-154100', 0, 1, 49.6, 'NOAA 15', 17, 109, 'Northbound', 49)
for Rows in Row:
  # sat_date = int(Rows[1])
  # sat_date = datetime.utcfromtimestamp(sat_date).strftime('%Y%m%d')

  for sat_files in glob.glob(f"/srv/images/" + Rows[2] + "*.jpg"):
    sat_name = Rows[6]
    sat_name = sat_name.replace("meteor-m2-3", "meteor-m23")
    sat_name = sat_name.replace("meteor-m2-4", "meteor-m24")
    sat_name = sat_name.replace("meteor-m2-5", "meteor-m25")
    sat_name = sat_name.replace(" ", "-")
    sat_name = sat_name.lower()

    # Extract satname
    sat_filepath = sat_files
    sat_filepath = sat_filepath.replace(Rows[2] + "-", "")
    sat_filepath = sat_filepath.replace("_", "-")
    sat_filepath = os.path.basename(sat_filepath)

    # Enchenments
    sat_ench = sat_filepath.strip('.jpg')

    # Enchenments replace names
    sat_ench = sat_ench.replace("composite", "comp")
    sat_ench = sat_ench.replace("msa_corrected", "msa-corrected")
    sat_ench = sat_ench.replace("msa_projected", "msa-projected")
    sat_ench = sat_ench.replace("mcir_corrected", "mcir-corrected")
    sat_ench = sat_ench.replace("mcir_projected", "mcir-projected")
    sat_ench = sat_ench.replace("321_corrected", "corrected-321")
    sat_ench = sat_ench.replace("321_projected", "projected-321")
    sat_ench = sat_ench.replace("221_corrected", "corrected-221")
    sat_ench = sat_ench.replace("221_projected", "projected-221")
    sat_ench = sat_ench.replace("thermal_channel_corrected", "therm-ch-corrected")
    sat_ench = sat_ench.replace("equidistant_67", "eq-67")
    sat_ench = sat_ench.replace("equidistant_rain", "eq-rain")
    sat_ench = sat_ench.replace("equidistant_rain_67", "eq-rain-67")
    sat_ench = sat_ench.replace("equidistant_67_thermal_comp", "eq-67-therm-comp")
    sat_ench = sat_ench.replace("equidistant_67_rain_composite", "eq-67-rain-comp")
    sat_ench = sat_ench.replace("equidistant_67_comp", "eq-67-comp")
    sat_ench = sat_ench.replace("equidistant_67_composite", "eq-67-comp-2")
    sat_ench = sat_ench.replace("equidistant_68", "eq-68")
    sat_ench = sat_ench.replace("equidistant_221", "eq-221")
    sat_ench = sat_ench.replace("equidistant_rain_221", "eq-rain-221")
    sat_ench = sat_ench.replace("equidistant_221_com", "eq-221-com")
    sat_ench = sat_ench.replace("equidistant_221_composite", "eq-221-comp")
    sat_ench = sat_ench.replace("equidistant_224", "eq-224")
    sat_ench = sat_ench.replace("equidistant_rain_224", "eq-rain-224")
    sat_ench = sat_ench.replace("equidistant_224_comp", "eq-224-comp")
    sat_ench = sat_ench.replace("equidistant_224_composite", "eq-224-comp")
    sat_ench = sat_ench.replace("equidistant_321", "eq-321")
    sat_ench = sat_ench.replace("equidistant_654", "eq-654")
    sat_ench = sat_ench.replace("equidistant_thermal", "eq-therm")
    sat_ench = sat_ench.replace("mercator_67", "mercator-67")
    sat_ench = sat_ench.replace("mercator_rain_67", "mercator-rain-67")
    sat_ench = sat_ench.replace("mercator_68", "mercator-68")
    sat_ench = sat_ench.replace("mercator_221", "mercator-221")
    sat_ench = sat_ench.replace("mercator_rain_221", "mercator-rain-221")
    sat_ench = sat_ench.replace("mercator_224", "mercator-224")
    sat_ench = sat_ench.replace("mercator_rain_224", "mercator-rain-224")
    sat_ench = sat_ench.replace("mercator_321", "mercator-321")
    sat_ench = sat_ench.replace("mercator_654", "mercator-654")
    sat_ench = sat_ench.replace("mercator_thermal", "mercator-therm")
    sat_ench = sat_ench.replace("spread_67", "spread-67")
    sat_ench = sat_ench.replace("spread_rain_67", "spread-rain-67")
    sat_ench = sat_ench.replace("spread_68", "spread-68")
    sat_ench = sat_ench.replace("spread_123", "spread-123")
    sat_ench = sat_ench.replace("spread_221", "spread-221")
    sat_ench = sat_ench.replace("spread_rain_221", "spread-rain-221")
    sat_ench = sat_ench.replace("spread_224", "spread-224")
    sat_ench = sat_ench.replace("spread_rain_224", "spread-rain-224")
    sat_ench = sat_ench.replace("spread_321", "spread-321")
    sat_ench = sat_ench.replace("spread_654", "spread-654")
    sat_ench = sat_ench.replace("spread_rain", "spread-rain")
    sat_ench = sat_ench.replace("spread_thermal", "spread-therm")

    # Sat date to time
    # sat_date = int((Rows[1] - 7200))
    sat_date = int(Rows[1])
    sat_date = datetime.utcfromtimestamp(sat_date).strftime('%d_%m_%Y_%H_%M')

    # Calculate direction
    if (Rows[8] >= 0 and Rows[8] <= 180):
      sat_direction = "e"
    else:
      sat_direction = "w"

    # New sat and filename
    sat_ex = sat_date + "_" + sat_name + "_" + Rows[9][0:1] + "_" + str(Rows[7]) + "_" + str(Rows[8]) + "_" + sat_direction + "_" + sat_ench + "_" + sat_freq[sat_name] + ".jpg"
    sat_ex = sat_ex.lower()

    sat_filetime = os.stat(sat_files)
    sat_filetime = int(sat_filetime.st_mtime)
    sat_filebase = os.path.basename(sat_files)

    Console = sat_filebase + "\033[0;36m >>> \033[0m" + sat_ex

    if os.path.exists(sat_files + ".locked"):
      print("\033[0;36mBereits verschoben:\033[0m " + Console)
    elif ((sat_filetime + 300) > time.time()):
      print("\033[0;36mWarte noch:\033[0m " + Console)
    else:
      print("\033[0;91mVerschiebe:\033[0m " + Console)

# Close database connection
SQLite.close()
Nun zu meiner Frage: Gibt es Verbesserungsvorschläge bzw. Optimierungen im Codesnippet selber, um die Performance etwas zu steigern? Ich werde das Gefühl nicht los, das der Code Snippet viel zu umständlich von mir geschrieben wurde.

In diesem Snippet soll noch eine Upload Funktion implementiert werden, das kommt allerdings etwas später. Was ich noch bräuchte, wäre eine Möglichkeit 2 Verzeichnisse abzuarbeiten in der "for/glob" Anweisung. In PHP ginge das mit "{Verzeichnis1, Verzeichnis2}" was hier in Python nicht funktioniert. Wenn es da eine Lösung für gibt, wäre ich für jeden Hinweis dankbar.

Bitte nicht zu hart mit mir sein, ich lerne noch und Frage euch deshalb, ob jemand einige Anpassungen vornehmen kann (Bitte auch mit Begründung und Erklärung, damit ich das auch verstehe).

Vielen dank an alle!
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht 2.
Variablennamen werden generell klein geschrieben.
Ein Cursor ist keine Row.
Namen mit s am Ende bedeuten oft plural. Bei `for Rows in Row` ist das gerade falsch herum. Auch "sat_files" ist eigentlich EIN Dateiname.
Strings stückelt man nicht mit + zusammen sondern benutzt f-Strings.
Dateinamen sind keine einfachen Strings, weshalb man für die Bearbeitung dererlei pathlib.Path benutzt.
Beim Ersetzen kommt es auf die Reihenfolge an: wenn man "equidistant_221" durch etwas anderes ersetzt, wird "equidistant_221_com" nicht mehr gefunden.
Statt einer langen Aneinanderreihung von Befehlen gliedert man ein Programm in Funktionen.
Kommentare sollten beschreiben, warum etwas gemacht wird, nicht was. Kommentare sollten nicht dem Code widersprechen.
`time` wird benutzt, aber nicht importiert.
Ungetestet:

Code: Alles auswählen

from datetime import datetime
from pathlib import Path
from contextlib import closing
import sqlite3
import time

# wxmeaa_passes :
# + sat_name
# + pass_start
# + pass_end
# + max_elev
# + is_active
# + pass_start_azimuth
# + direction
# + azimuth_at_max
# + at_job_id

# wxmeaa_decoded :
# + pass_start
# + file_path
# + daylight_pass
# + is_noaa
# + sat_type
# + img_count
# + has_spectrogram
# + has_pristine
# + gain
# + has_polar_az_el
# + has_polar_direction
# + has_histogram
# d_m_Y_H_i_noaa-15_s_40_140_E_mcir_137.9125000.jpg

SQLITE_DB_FILENAME = Path("~/db/panel.db").expanduser()
IMAGE_PATH = Path("/srv/images")

SAT_FREQUENCIES = {
    "meteor-m25": "137.900000",
    "meteor-m24": "137.900000",
    "meteor-m23": "137.900000",
    "noaa-19": "137.620000",
    "noaa-18": "137.912500",
    "noaa-15": "137.100000",
}

SAT_NAME_REPLACEMENTS = [
    ("meteor-m2-3", "meteor-m23"),
    ("meteor-m2-4", "meteor-m24"),
    ("meteor-m2-5", "meteor-m25"),
    (" ", "-"),
]

# TODO: correct order
SAT_ENCHENMENT_REPLACEMENTS = [
    ("composite", "comp"),
    ("msa_corrected", "msa-corrected"),
    ("msa_projected", "msa-projected"),
    ("mcir_corrected", "mcir-corrected"),
    ("mcir_projected", "mcir-projected"),
    ("321_corrected", "corrected-321"),
    ("321_projected", "projected-321"),
    ("221_corrected", "corrected-221"),
    ("221_projected", "projected-221"),
    ("thermal_channel_corrected", "therm-ch-corrected"),
    ("equidistant_67", "eq-67"),
    ("equidistant_rain", "eq-rain"),
    ("equidistant_rain_67", "eq-rain-67"),
    ("equidistant_67_thermal_comp", "eq-67-therm-comp"),
    ("equidistant_67_rain_composite", "eq-67-rain-comp"),
    ("equidistant_67_comp", "eq-67-comp"),
    ("equidistant_67_composite", "eq-67-comp-2"),
    ("equidistant_68", "eq-68"),
    ("equidistant_221", "eq-221"),
    ("equidistant_rain_221", "eq-rain-221"),
    ("equidistant_221_com", "eq-221-com"),
    ("equidistant_221_composite", "eq-221-comp"),
    ("equidistant_224", "eq-224"),
    ("equidistant_rain_224", "eq-rain-224"),
    ("equidistant_224_comp", "eq-224-comp"),
    ("equidistant_224_composite", "eq-224-comp"),
    ("equidistant_321", "eq-321"),
    ("equidistant_654", "eq-654"),
    ("equidistant_thermal", "eq-therm"),
    ("mercator_67", "mercator-67"),
    ("mercator_rain_67", "mercator-rain-67"),
    ("mercator_68", "mercator-68"),
    ("mercator_221", "mercator-221"),
    ("mercator_rain_221", "mercator-rain-221"),
    ("mercator_224", "mercator-224"),
    ("mercator_rain_224", "mercator-rain-224"),
    ("mercator_321", "mercator-321"),
    ("mercator_654", "mercator-654"),
    ("mercator_thermal", "mercator-therm"),
    ("spread_67", "spread-67"),
    ("spread_rain_67", "spread-rain-67"),
    ("spread_68", "spread-68"),
    ("spread_123", "spread-123"),
    ("spread_221", "spread-221"),
    ("spread_rain_221", "spread-rain-221"),
    ("spread_224", "spread-224"),
    ("spread_rain_224", "spread-rain-224"),
    ("spread_321", "spread-321"),
    ("spread_654", "spread-654"),
    ("spread_rain", "spread-rain"),
    ("spread_thermal", "spread-therm"),
]


def replace_all(replacements, string):
    for from_string, to_string in replacements:
        string = string.replace(from_string, to_string)
    return string


def read_database():
    with closing(sqlite3.connect(SQLITE_DB_FILENAME)) as connection:
        with closing(connection.cursor()) as cursor:
            # Show detected passes from database
            cursor.execute(
                """SELECT
                    decoded_passes.id,
                    predict_passes.pass_start,
                    file_path,
                    daylight_pass,
                    sat_type,
                    gain,
                    predict_passes.sat_name,
                    predict_passes.max_elev,
                    predict_passes.pass_start_azimuth,
                    predict_passes.direction,
                    predict_passes.azimuth_at_max
                FROM decoded_passes
                INNER JOIN predict_passes
                ON predict_passes.pass_start = decoded_passes.pass_start
                ORDER BY decoded_passes.pass_start DESC"""
            )
            return list(cursor)


def process_file(row, sat_filepath):
    sat_name = replace_all(SAT_NAME_REPLACEMENTS, row[6]).lower()

    sat_enchenment = sat_filepath.stem.replace(f"{row[2]}-", "").replace("_", "-")
    sat_enchenment = replace_all(SAT_ENCHENMENT_REPLACEMENTS, sat_enchenment)

    # TODO: which one is correct, comment or code?
    # sat_date = int((Rows[1] - 7200))
    sat_date = datetime.utcfromtimestamp(int(row[1])).strftime("%d_%m_%Y_%H_%M")

    sat_direction = "e" if 0 <= row[8] <= 180 else "w"

    new_sat_filename = f"{sat_date}_{sat_name}_{row[9][0]}_{row[7]}_{row[8]}_{sat_direction}_{sat_enchenment}_{SAT_FREQUENCIES[sat_name]}.jpg"
    new_sat_filename = new_sat_filename.lower()

    sat_filetime = sat_filepath.stat().st_mtime

    if sat_filepath.with_stem(sat_filepath.stem + ".locked").exists():
        text = "\033[0;36mBereits verschoben:"
    elif sat_filetime + 300 > time.time():
        text = "\033[0;36mWarte noch:"
    else:
        text = "\033[0;91mVerschiebe:"
    print(f"{text}\033[0m {sat_filepath.name}\033[0;36m >>> \033[0m{new_sat_filename}")


def main():
    # Datenbank Schema: (935, 1736696460, 'NOAA-15-20250112-154100', 0, 1, 49.6, 'NOAA 15', 17, 109, 'Northbound', 49)
    for row in read_database():
        for sat_filepath in IMAGE_PATH.glob(f"{row[2]}*.jpg"):
            process_file(row, sat_filepath)


if __name__ == "__main__":
    main()
Benutzeravatar
DeaD_EyE
User
Beiträge: 1205
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Schauen wir mal, wo die Unterschiede sind. Hier mein ungetesteter Code:

datetime.datetime.utcfromtimestamp soll man nicht mehr nutzen. Diese Klassenmethode hat noch nie richtig funktioniert.
Stattdessen einfach datetime.datetime.fromtimestamp nutzen und die Zeitzone kann man der Methode als zweites Argument übergeben.
https://docs.python.org/3/library/datet ... mtimestamp
https://docs.python.org/3/library/datet ... mtimestamp

Code: Alles auswählen

import atexit
import sqlite3
import time
from datetime import datetime
from pathlib import Path

SAT_FREQUENCIES = {
    "meteor-m25": "137.900000",
    "meteor-m24": "137.900000",
    "meteor-m23": "137.900000",
    "noaa-19": "137.620000",
    "noaa-18": "137.912500",
    "noaa-15": "137.100000",
}

IMAGE_DIRECTORY = Path("/srv/images/")
SQL_QUERY = """
SELECT decoded_passes.id,
       predict_passes.pass_start,
       file_path,
       daylight_pass,
       sat_type,
       gain,
       predict_passes.sat_name,
       predict_passes.max_elev,
       predict_passes.pass_start_azimuth,
       predict_passes.direction,
       predict_passes.azimuth_at_max
FROM   decoded_passes
       INNER JOIN predict_passes
               ON predict_passes.pass_start = decoded_passes.pass_start
ORDER  BY decoded_passes.pass_start DESC;"""

REPLACEMENTS_NAME_1 = (
    ("meteor-m2-3", "meteor-m23"),
    ("meteor-m2-4", "meteor-m24"),
    ("meteor-m2-5", "meteor-m25"),
    (" ", "-"),
)

REPLACEMENTS_NAME_2 = (
    ("composite", "comp"),
    ("msa_corrected", "msa-corrected"),
    ("msa_projected", "msa-projected"),
    ("mcir_corrected", "mcir-corrected"),
    ("mcir_projected", "mcir-projected"),
    ("321_corrected", "corrected-321"),
    ("321_projected", "projected-321"),
    ("221_corrected", "corrected-221"),
    ("221_projected", "projected-221"),
    ("thermal_channel_corrected", "therm-ch-corrected"),
    ("equidistant_67", "eq-67"),
    ("equidistant_rain", "eq-rain"),
    ("equidistant_rain_67", "eq-rain-67"),
    ("equidistant_67_thermal_comp", "eq-67-therm-comp"),
    ("equidistant_67_rain_composite", "eq-67-rain-comp"),
    ("equidistant_67_comp", "eq-67-comp"),
    ("equidistant_67_composite", "eq-67-comp-2"),
    ("equidistant_68", "eq-68"),
    ("equidistant_221", "eq-221"),
    ("equidistant_rain_221", "eq-rain-221"),
    ("equidistant_221_com", "eq-221-com"),
    ("equidistant_221_composite", "eq-221-comp"),
    ("equidistant_224", "eq-224"),
    ("equidistant_rain_224", "eq-rain-224"),
    ("equidistant_224_comp", "eq-224-comp"),
    ("equidistant_224_composite", "eq-224-comp"),
    ("equidistant_321", "eq-321"),
    ("equidistant_654", "eq-654"),
    ("equidistant_thermal", "eq-therm"),
    ("mercator_67", "mercator-67"),
    ("mercator_rain_67", "mercator-rain-67"),
    ("mercator_68", "mercator-68"),
    ("mercator_221", "mercator-221"),
    ("mercator_rain_221", "mercator-rain-221"),
    ("mercator_224", "mercator-224"),
    ("mercator_rain_224", "mercator-rain-224"),
    ("mercator_321", "mercator-321"),
    ("mercator_654", "mercator-654"),
    ("mercator_thermal", "mercator-therm"),
    ("spread_67", "spread-67"),
    ("spread_rain_67", "spread-rain-67"),
    ("spread_68", "spread-68"),
    ("spread_123", "spread-123"),
    ("spread_221", "spread-221"),
    ("spread_rain_221", "spread-rain-221"),
    ("spread_224", "spread-224"),
    ("spread_rain_224", "spread-rain-224"),
    ("spread_321", "spread-321"),
    ("spread_654", "spread-654"),
    ("spread_rain", "spread-rain"),
    ("spread_thermal", "spread-therm"),
)

# Database
database_file = Path.home().joinpath("db/panel.db")

# Connect to database
database = sqlite3.connect(database_file)
cursor = database.cursor()

# DB vor beenden des Interpreters schließen
atexit.register(database.close)


with database:
    for row in cursor.execute(SQL_QUERY):
        # Datenbank Schema: (935, 1736696460, 'NOAA-15-20250112-154100', 0, 1, 49.6, 'NOAA 15', 17, 109, 'Northbound', 49)
        for sat_file in IMAGE_DIRECTORY.joinpath(row[2]).glob("*.jpg"):
            sat_name = row[6]
            for old, new in REPLACEMENTS_NAME_1:
                sat_name = sat_name.replace(old, new)
            sat_name = sat_name.lower()

            # Enchenments
            sat_ench = sat_file.stem
            for old, new in REPLACEMENTS_NAME_2:
                sat_ench = sat_ench.replace(old, new)

            sat_date = datetime.fromtimestamp(int(row[1])).strftime("%d_%m_%Y_%H_%M")
            sat_direction = "e" if 0 <= row[8] <= 180 else "w"

            # New sat and filename
            sat_ex = (
                "_".join(
                    (
                        sat_date,
                        sat_name,
                        row[9][0:1],
                        str(row[7]),
                        row[8],
                        sat_direction,
                        sat_ench,
                        SAT_FREQUENCIES[sat_name],
                    )
                ).lower()
                + ".jpg"
            )

            sat_filetime = sat_file.stat().st_mtime
            message = f"{sat_file.parent}\033[0;36m >>> \033[0m{sat_ex}"
            lock_file = Path(sat_file.parent, sat_file.name + ".locked")

            if lock_file.exists:
                print(f"\033[0;36mBereits verschoben:\033[0m {message}")
            elif (sat_filetime + 300) > time.time():
                print(f"\033[0;36mWarte noch:\033[0m {message}")
            else:
                print(f"\033[0;91mVerschiebe:\033[0m {message}")

sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
mmueller-87
User
Beiträge: 12
Registriert: Sonntag 11. Juni 2023, 07:25

Sirius3 hat geschrieben: Montag 13. Januar 2025, 14:53 Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht 2.
Variablennamen werden generell klein geschrieben.
Ein Cursor ist keine Row.
Namen mit s am Ende bedeuten oft plural. Bei `for Rows in Row` ist das gerade falsch herum. Auch "sat_files" ist eigentlich EIN Dateiname.
Strings stückelt man nicht mit + zusammen sondern benutzt f-Strings.
Dateinamen sind keine einfachen Strings, weshalb man für die Bearbeitung dererlei pathlib.Path benutzt.
Beim Ersetzen kommt es auf die Reihenfolge an: wenn man "equidistant_221" durch etwas anderes ersetzt, wird "equidistant_221_com" nicht mehr gefunden.
Statt einer langen Aneinanderreihung von Befehlen gliedert man ein Programm in Funktionen.
Kommentare sollten beschreiben, warum etwas gemacht wird, nicht was. Kommentare sollten nicht dem Code widersprechen.
`time` wird benutzt, aber nicht importiert.
Vielen dank für die Ausführliche Aufklärung. Sind vieles was ich noch nicht wußte. Das mit den "stückeln" ist bei PHP etwas einfacher und konnte mir auch nicht geanu zusammenreimen wie das in Python genau gemacht wird. Ich fuchse mich da noch rein.

@DeaD_EyE
Leider bekomme ich bei deiner Variante keinerlei Ausgabe weder einen Fehler oder Info. Beim ausführen erhalte ich in der Konsole nichts. Hab versucht mal eine simple print-Ausgabe in die entsprechende Eindrückung zu schicken und erhalte nur Ergebnisse der ersten "Schleife":

Code: Alles auswählen

    for row in cursor.execute(SQL_QUERY):
Benutzeravatar
DeaD_EyE
User
Beiträge: 1205
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Wenn die erste Schleife funktioniert, die zweite innere aber nicht iteriert, dann ist der Pfad falsch,

Entweder dieser Basis-Pfad ist falsch:

Code: Alles auswählen

IMAGE_DIRECTORY = Path("/srv/images/")
Oder die Erweiterung des Pfades ist falsch:

Code: Alles auswählen

IMAGE_DIRECTORY.joinpath(row[2]).glob("*.jpg")
Wenn das Verzeichnis nicht existiert, liefert glob keine Ergebnisse und es kommt auch keine Fehlermeldung.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
mmueller-87
User
Beiträge: 12
Registriert: Sonntag 11. Juni 2023, 07:25

Guten Abend und vielen dank für deine Geduld mit mir. Ich habe den Snippet etwas anpassen müssen, das "joinpath" und "sat_file.exists" nicht funktionierte und keinerlei Plan hatte warum. Hier mal die modifizierte Version:

Code: Alles auswählen

import atexit
import sqlite3
import time, os
from datetime import datetime
from pathlib import Path

# wxmeaa_passes :
# + sat_name
# + pass_start
# + pass_end
# + max_elev
# + is_active
# + pass_start_azimuth
# + direction
# + azimuth_at_max
# + at_job_id

# wxmeaa_decoded :
# + pass_start
# + file_path
# + daylight_pass
# + is_noaa
# + sat_type
# + img_count
# + has_spectrogram
# + has_pristine
# + gain
# + has_polar_az_el
# + has_polar_direction
# + has_histogram
# d_m_Y_H_i_noaa-15_s_40_140_E_mcir_137.9125000.jpg

SAT_FREQUENCIES = {
    "meteor-m25": "137.900000",
    "meteor-m24": "137.900000",
    "meteor-m23": "137.900000",
    "noaa-19": "137.620000",
    "noaa-18": "137.912500",
    "noaa-15": "137.100000",
}

IMAGE_DIRECTORY = Path("/srv/images/")
SQL_QUERY = """
SELECT decoded_passes.id,
       predict_passes.pass_start,
       file_path,
       daylight_pass,
       sat_type,
       gain,
       predict_passes.sat_name,
       predict_passes.max_elev,
       predict_passes.pass_start_azimuth,
       predict_passes.direction,
       predict_passes.azimuth_at_max
FROM   decoded_passes
       INNER JOIN predict_passes
               ON predict_passes.pass_start = decoded_passes.pass_start
ORDER  BY decoded_passes.pass_start DESC;"""

REPLACEMENTS_NAME_1 = (
    ("meteor-m2-3", "meteor-m23"),
    ("meteor-m2-4", "meteor-m24"),
    ("meteor-m2-5", "meteor-m25"),
    (" ", "-"),
)

REPLACEMENTS_NAME_2 = (
    ("composite", "comp"),
    ("msa_corrected", "msa-corrected"),
    ("msa_projected", "msa-projected"),
    ("mcir_corrected", "mcir-corrected"),
    ("mcir_projected", "mcir-projected"),
    ("321_corrected", "corrected-321"),
    ("321_projected", "projected-321"),
    ("221_corrected", "corrected-221"),
    ("221_projected", "projected-221"),
    ("thermal_channel_corrected", "therm-ch-corrected"),
    ("equidistant_67", "eq-67"),
    ("equidistant_rain", "eq-rain"),
    ("equidistant_rain_67", "eq-rain-67"),
    ("equidistant_67_thermal_comp", "eq-67-therm-comp"),
    ("equidistant_67_rain_composite", "eq-67-rain-comp"),
    ("equidistant_67_comp", "eq-67-comp"),
    ("equidistant_67_composite", "eq-67-comp-2"),
    ("equidistant_68", "eq-68"),
    ("equidistant_221", "eq-221"),
    ("equidistant_rain_221", "eq-rain-221"),
    ("equidistant_221_com", "eq-221-com"),
    ("equidistant_221_composite", "eq-221-comp"),
    ("equidistant_224", "eq-224"),
    ("equidistant_rain_224", "eq-rain-224"),
    ("equidistant_224_comp", "eq-224-comp"),
    ("equidistant_224_composite", "eq-224-comp"),
    ("equidistant_321", "eq-321"),
    ("equidistant_654", "eq-654"),
    ("equidistant_thermal", "eq-therm"),
    ("mercator_67", "mercator-67"),
    ("mercator_rain_67", "mercator-rain-67"),
    ("mercator_68", "mercator-68"),
    ("mercator_221", "mercator-221"),
    ("mercator_rain_221", "mercator-rain-221"),
    ("mercator_224", "mercator-224"),
    ("mercator_rain_224", "mercator-rain-224"),
    ("mercator_321", "mercator-321"),
    ("mercator_654", "mercator-654"),
    ("mercator_thermal", "mercator-therm"),
    ("spread_67", "spread-67"),
    ("spread_rain_67", "spread-rain-67"),
    ("spread_68", "spread-68"),
    ("spread_123", "spread-123"),
    ("spread_221", "spread-221"),
    ("spread_rain_221", "spread-rain-221"),
    ("spread_224", "spread-224"),
    ("spread_rain_224", "spread-rain-224"),
    ("spread_321", "spread-321"),
    ("spread_654", "spread-654"),
    ("spread_rain", "spread-rain"),
    ("spread_thermal", "spread-therm"),
)

# Database
database_file = Path.home().joinpath("/db/panel.db")

# Connect to database
database = sqlite3.connect(database_file)
cursor = database.cursor()

# DB vor beenden des Interpreters schließen
atexit.register(database.close)

with database:
    for row in cursor.execute(SQL_QUERY):
        # Datenbank Schema: (935, 1736696460, 'NOAA-15-20250112-154100', 0, 1, 49.6, 'NOAA 15', 17, 109, 'Northbound', 49)
        # for sat_file in IMAGE_DIRECTORY.joinpath(row[2]).glob("*.jpg"):
        for sat_file in IMAGE_DIRECTORY.glob(f"{row[2]}*.jpg"):
            sat_name = row[6]
            sat_name = sat_name.replace(" ", "-").lower()

            for old, new in REPLACEMENTS_NAME_1:
                sat_name = sat_name.replace(old, new)
            sat_name = sat_name.lower()

            # Enchenments
            sat_ench = sat_file.stem
            sat_ench = sat_ench.replace(row[2] + "-", "")

            for old, new in REPLACEMENTS_NAME_2:
                sat_ench = sat_ench.replace(old, new)

            sat_date = datetime.fromtimestamp(int(row[1])).strftime("%d_%m_%Y_%H_%M")
            sat_direction = "e" if 0 <= row[8] <= 180 else "w"

            # New sat and filename
            sat_ex = f"{sat_date}_{sat_name}_{row[9][0]}_{row[7]}_{row[8]}_{sat_direction}_{sat_ench}_{SAT_FREQUENCIES[sat_name]}.jpg".lower()

            sat_filetime = sat_file.stat().st_mtime
            message = f"{sat_file}\033[0;36m >>> \033[0m{sat_ex}"
            lock_file = Path(sat_file.parent, sat_file.name + ".locked")

            if os.path.exists(lock_file):
                print(f"\033[0;36mBereits verschoben:\033[0m {message}")
            elif (sat_filetime + 300) > time.time():
                print(f"\033[0;36mWarte noch:\033[0m {message}")
            else:
                print(f"\033[0;91mVerschiebe:\033[0m {message}")
mmueller-87
User
Beiträge: 12
Registriert: Sonntag 11. Juni 2023, 07:25

Noch eine kleine Frage zu dem Snippet: Was ich noch bräuchte, wäre eine Möglichkeit 2 Verzeichnisse abzuarbeiten in der "for/glob" Anweisung. In PHP ginge das mit:

Code: Alles auswählen

foreach(glob($folder."{/images/thumb/,/images/}*".$row['file_path']."*.{jpg,png}", GLOB_BRACE) as $k => $v) {
was hier in Python nicht funktioniert. Wenn es da eine Lösung für gibt, wäre ich für jeden Hinweis dankbar.
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

@DeaD_EyE: tuple sind für verschiedenartige Elemente da, Listen für gleichartige. Semantisch machen die Tuple REPLACEMENTS_NAME_1 und REPLACEMENTS_NAME_2 also keinen Sinn.
Variablennamen sollten sprechend benannt werden, angehängte Nummern sind das selten.
Weil es with-Blöcke gibt, braucht man atexit hier nicht.
Bei lock_file.exists fehlen die Klammern.
"{row[2]}/*.jpg" ist etwas anderes als "{row[2]}*.jpg". Im ersten Fall hast Du ein Verzeichnis zu viel.

@mmueller-87: solche komplizierten Konstrukte gibt es bei pathlib nicht. Man würde einfach die vier Patterns getrennt durchsuchen: "images/thumb/*{row[2]}*.jpg", "images/thumb/*{row[2]}*.png", "images/*{row[2]}*.jpg" und "images/thumb/*{row[2]}*.png"
mmueller-87
User
Beiträge: 12
Registriert: Sonntag 11. Juni 2023, 07:25

Sirius3 hat geschrieben: Montag 13. Januar 2025, 21:55 @mmueller-87: solche komplizierten Konstrukte gibt es bei pathlib nicht. Man würde einfach die vier Patterns getrennt durchsuchen: "images/thumb/*{row[2]}*.jpg", "images/thumb/*{row[2]}*.png", "images/*{row[2]}*.jpg" und "images/thumb/*{row[2]}*.png"
Guten Abend, ich möchte nur nicht 2x das Script aufrufen. Dachte es gäbe bei Python auch solch eine Variante. Die Variante mit *.png kann weg bleiben. Dieser solle nur 2 Verzeichnisse durchsuchen, sprich also das Verzeichnis: "/srv/images/*.jpg" und "/srv/images/thumb/*.jpg"
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum sollte man das Skript zweimal aufrufen?
Die Endung könnte man auch nachträglich prüfen.

Code: Alles auswählen

IMAGE_PATHS = [
    Path("/srv/images/"),
    Path("/srv/images/thumbs"),
]

def main():
    # Datenbank Schema: (935, 1736696460, 'NOAA-15-20250112-154100', 0, 1, 49.6, 'NOAA 15', 17, 109, 'Northbound', 49)
    for row in read_database():
        for image_path in IMAGE_PATHS:
            for sat_filepath in image_path.glob(f"{row[2]}*"):
                if sat_filepath.suffix in [".png", ".jpg"]:
                    process_file(row, sat_filepath)
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mmueller-87: Ich gehe mal von der überarbeiteten Version von Sirius3 aus. Da sind noch die ganzen magischen Indexzugriffe auf das ehemalige `Rows` drin, die nicht schön sind. Beim Original hätte ich das in der ``for``-Schleife auf sprechende Namen entpackt, aber dann hätten wir in der überarbeiteten Variante sehr viele Argumente zu übergeben. Also würde ich das in ein Objekt verpacken.

Da könnte man auch schauen was man an Code dort hin verschiebt. Beispielsweise das umwandeln des Zeitpunktes in Sekunden in ein `datetime`-Objekt.

Fünf abgefragte Werte pro Datensatz werden gar nicht verwendet‽

Ein `replace()` das etwas was man eigentlich nur am Anfang einer Zeichenkette erwartet, durch eine leere Zeichenkette ersetzt, ist ein `removeprefix()`.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import sqlite3
import time
from contextlib import closing
from datetime import datetime, timezone
from pathlib import Path

# wxmeaa_passes :
# + sat_name
# + pass_start
# + pass_end
# + max_elev
# + is_active
# + pass_start_azimuth
# + direction
# + azimuth_at_max
# + at_job_id

# wxmeaa_decoded :
# + pass_start
# + file_path
# + daylight_pass
# + is_noaa
# + sat_type
# + img_count
# + has_spectrogram
# + has_pristine
# + gain
# + has_polar_az_el
# + has_polar_direction
# + has_histogram
# d_m_Y_H_i_noaa-15_s_40_140_E_mcir_137.9125000.jpg

SQLITE_DB_FILENAME = Path("~/db/panel.db").expanduser()
IMAGE_PATH = Path("/srv/images")

SAT_FREQUENCIES = {
    "meteor-m25": "137.900000",
    "meteor-m24": "137.900000",
    "meteor-m23": "137.900000",
    "noaa-19": "137.620000",
    "noaa-18": "137.912500",
    "noaa-15": "137.100000",
}

SAT_NAME_REPLACEMENTS = [
    ("meteor-m2-3", "meteor-m23"),
    ("meteor-m2-4", "meteor-m24"),
    ("meteor-m2-5", "meteor-m25"),
    (" ", "-"),
]
#
# TODO: correct order
#
SAT_ENCHENMENT_REPLACEMENTS = [
    ("composite", "comp"),
    ("msa_corrected", "msa-corrected"),
    ("msa_projected", "msa-projected"),
    ("mcir_corrected", "mcir-corrected"),
    ("mcir_projected", "mcir-projected"),
    ("321_corrected", "corrected-321"),
    ("321_projected", "projected-321"),
    ("221_corrected", "corrected-221"),
    ("221_projected", "projected-221"),
    ("thermal_channel_corrected", "therm-ch-corrected"),
    ("equidistant_67", "eq-67"),
    ("equidistant_rain", "eq-rain"),
    ("equidistant_rain_67", "eq-rain-67"),
    ("equidistant_67_thermal_comp", "eq-67-therm-comp"),
    ("equidistant_67_rain_composite", "eq-67-rain-comp"),
    ("equidistant_67_comp", "eq-67-comp"),
    ("equidistant_67_composite", "eq-67-comp-2"),
    ("equidistant_68", "eq-68"),
    ("equidistant_221", "eq-221"),
    ("equidistant_rain_221", "eq-rain-221"),
    ("equidistant_221_com", "eq-221-com"),
    ("equidistant_221_composite", "eq-221-comp"),
    ("equidistant_224", "eq-224"),
    ("equidistant_rain_224", "eq-rain-224"),
    ("equidistant_224_comp", "eq-224-comp"),
    ("equidistant_224_composite", "eq-224-comp"),
    ("equidistant_321", "eq-321"),
    ("equidistant_654", "eq-654"),
    ("equidistant_thermal", "eq-therm"),
    ("mercator_67", "mercator-67"),
    ("mercator_rain_67", "mercator-rain-67"),
    ("mercator_68", "mercator-68"),
    ("mercator_221", "mercator-221"),
    ("mercator_rain_221", "mercator-rain-221"),
    ("mercator_224", "mercator-224"),
    ("mercator_rain_224", "mercator-rain-224"),
    ("mercator_321", "mercator-321"),
    ("mercator_654", "mercator-654"),
    ("mercator_thermal", "mercator-therm"),
    ("spread_67", "spread-67"),
    ("spread_rain_67", "spread-rain-67"),
    ("spread_68", "spread-68"),
    ("spread_123", "spread-123"),
    ("spread_221", "spread-221"),
    ("spread_rain_221", "spread-rain-221"),
    ("spread_224", "spread-224"),
    ("spread_rain_224", "spread-rain-224"),
    ("spread_321", "spread-321"),
    ("spread_654", "spread-654"),
    ("spread_rain", "spread-rain"),
    ("spread_thermal", "spread-therm"),
]


def replace_all(replacements, string):
    for from_string, to_string in replacements:
        string = string.replace(from_string, to_string)
    return string


class Pass:
    def __init__(
        self,
        timestamp,
        filename_prefix,
        sat_name,
        max_elevation,
        start_azimuth,
        direction,
    ):
        self.timestamp = timestamp
        self.filename_prefix = filename_prefix
        self.sat_name = sat_name
        self.max_elevation = max_elevation
        self.start_azimuth = start_azimuth
        self.direction = direction

    @property
    def sat_direction(self):
        return "e" if 0 <= self.start_azimuth <= 180 else "w"

    @classmethod
    def from_row(cls, row):
        (
            timestamp,
            filename_prefix,
            sat_name,
            max_elevation,
            start_azimuth,
            direction,
        ) = row
        return cls(
            datetime.fromtimestamp(timestamp, timezone.utc),
            filename_prefix,
            sat_name,
            max_elevation,
            start_azimuth,
            direction,
        )


def read_database():
    with closing(sqlite3.connect(SQLITE_DB_FILENAME)) as connection:
        with closing(connection.cursor()) as cursor:
            #
            # Select detected passes from database.
            #
            cursor.execute(
                """SELECT
                    predict_passes.pass_start,
                    file_path,
                    predict_passes.sat_name,
                    predict_passes.max_elev,
                    predict_passes.pass_start_azimuth,
                    predict_passes.direction
                FROM decoded_passes
                INNER JOIN predict_passes
                ON predict_passes.pass_start = decoded_passes.pass_start
                ORDER BY decoded_passes.pass_start DESC"""
            )
            return list(map(Pass.from_row, cursor.fetchall()))


def process_file(pass_, sat_file_path):
    sat_name = replace_all(SAT_NAME_REPLACEMENTS, pass_.sat_name).lower()
    sat_enchenment = replace_all(
        SAT_ENCHENMENT_REPLACEMENTS,
        sat_file_path.stem.removeprefix(f"{pass_.filename_prefix}-").replace(
            "_", "-"
        ),
    )
    new_sat_filename = (
        f"{pass_.timestamp:%d_%m_%Y_%H_%M}_{sat_name}_{pass_.direction[0]}"
        f"_{pass_.max_elevation}_{pass_.start_azimuth}_{pass_.sat_direction}"
        f"_{sat_enchenment}_{SAT_FREQUENCIES[sat_name]}.jpg"
    ).lower()

    sat_filetime = sat_file_path.stat().st_mtime

    if sat_file_path.with_stem(sat_file_path.stem + ".locked").exists():
        text = "\033[0;36mBereits verschoben:"
    elif sat_filetime + 300 > time.time():
        text = "\033[0;36mWarte noch:"
    else:
        text = "\033[0;91mVerschiebe:"

    print(
        f"{text}\033[0m {sat_file_path.name}\033[0;36m"
        f" >>> \033[0m{new_sat_filename}"
    )


def main():
    #
    # Datenbank Schema:
    # (1736696460, 'NOAA-15-20250112-154100', 'NOAA 15', 17, 109, 'Northbound')
    #
    for pass_ in read_database():
        for sat_file_path in IMAGE_PATH.glob(f"{pass_.filename_prefix}*.jpg"):
            process_file(pass_, sat_file_path)


if __name__ == "__main__":
    main()
Ansonsten bin ich bei Datenbanken grosser Fan von SQLAlchemy als Modul.
“I am Dyslexic of Borg, Your Ass will be Laminated” — unknown
mmueller-87
User
Beiträge: 12
Registriert: Sonntag 11. Juni 2023, 07:25

__blackjack__ hat geschrieben: Montag 13. Januar 2025, 23:45 @mmueller-87: Ich gehe mal von der überarbeiteten Version von Sirius3 aus. Da sind noch die ganzen magischen Indexzugriffe auf das ehemalige `Rows` drin, die nicht schön sind. Beim Original hätte ich das in der ``for``-Schleife auf sprechende Namen entpackt, aber dann hätten wir in der überarbeiteten Variante sehr viele Argumente zu übergeben. Also würde ich das in ein Objekt verpacken.

Da könnte man auch schauen was man an Code dort hin verschiebt. Beispielsweise das umwandeln des Zeitpunktes in Sekunden in ein `datetime`-Objekt.

Fünf abgefragte Werte pro Datensatz werden gar nicht verwendet‽

Ein `replace()` das etwas was man eigentlich nur am Anfang einer Zeichenkette erwartet, durch eine leere Zeichenkette ersetzt, ist ein `removeprefix()`.
Guten morgen und erstmal vielen dank das ihr mir hilft. Ich habe nun folgenden Fehler:

Code: Alles auswählen

Traceback (most recent call last):
  File "/home/pi/wx_run.py", line 219, in <module>
    main()
  File "/home/pi/wx_run.py", line 215, in main
    process_file(pass_, sat_file_path)
  File "/home/pi/wx_run.py", line 190, in process_file
    f"_{sat_enchenment}_{SAT_FREQUENCIES[sat_name]}.jpg"
                         ~~~~~~~~~~~~~~~^^^^^^^^^^
KeyError: 'meteor-m2-4'
Es sieht so aus, als würde er den Dateinamen: METEOR-M2-4-20250113-132005-spread_thermal.jpg nicht mit dem oben erstzen: METEOR-M2-4 soll werden: meteor-m24

Code: Alles auswählen

meteor-m2-3
Verschiebe: METEOR-M2-3-20250113-104150-equidistant_rain_221.jpg >>> 13_01_2025_10_41_meteor-m2-3_s_19_8_e_equidistant-rain-221_.jpg
Habe gerade mal versucht den Snippet zu verstehen und werde nicht ganz daraus schlau. Ich werde mich heute mit Python intensiver beschäftigen, denn es reizt mich irgendwie mehr als PHP. Mir ist es etwas peinlich um Hilfe hier zu fragen, aber in PHP und SQL stelle ich mich nicht so blöd an :cry:
Benutzeravatar
Dennis89
User
Beiträge: 1503
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

ändere mal

Code: Alles auswählen

sat_name = replace_all(SAT_NAME_REPLACEMENTS, pass_.sat_name).lower()
in

Code: Alles auswählen

sat_name = replace_all(SAT_NAME_REPLACEMENTS, pass_.sat_name.lower())
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
mmueller-87
User
Beiträge: 12
Registriert: Sonntag 11. Juni 2023, 07:25

Guten Morgen Denis,

das mache ich nachher, teste es und berichte. Jetzt ruft die Arbeit, damit ihr schön eure Post bekommt (Depot) 😉
Benutzeravatar
DeaD_EyE
User
Beiträge: 1205
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Sirius3 hat geschrieben: Montag 13. Januar 2025, 21:55 @DeaD_EyE: tuple sind für verschiedenartige Elemente da, Listen für gleichartige. Semantisch machen die Tuple REPLACEMENTS_NAME_1 und REPLACEMENTS_NAME_2 also keinen Sinn.
Ich habe die Tuple verwendet, um einen unveränderbaren Datentyp zu haben. Da zur Laufzeit des Programms die Ersetzungen nicht verändert werden, ist die Tuple die richtige Wahl. Anders sieht es z.B. aus, wenn man die Regeln zur Ersetzung auf einer Datei lädt, dann ist es sinnlos, die resultierende Liste wieder in eine Tuple "umzuwandeln".

REPLACEMENTS_NAME_2 ist ein Relikt, weil ich das Programm nicht komplett verstanden habe.
Sirius3 hat geschrieben: Montag 13. Januar 2025, 21:55 Variablennamen sollten sprechend benannt werden, angehängte Nummern sind das selten.
Gut, das ist nicht meine Aufgabe. Ich kenne die DB nicht und ja man sollte passende Namen verwenden. Ich würde die Namen so benennen, wie die Spalten heißen.
Sirius3 hat geschrieben: Montag 13. Januar 2025, 21:55 Weil es with-Blöcke gibt, braucht man atexit hier nicht.
War nur präventiv. Hat wahrscheinlich keinen Nutzen. Ja doch, close() wird dann zweimal aufgerufen...
Sirius3 hat geschrieben: Montag 13. Januar 2025, 21:55 Bei lock_file.exists fehlen die Klammern.
"{row[2]}/*.jpg" ist etwas anderes als "{row[2]}*.jpg". Im ersten Fall hast Du ein Verzeichnis zu viel.
Das erklärt, wieso nichts funktioniert.
Eine Funktion liefert immer ein True zurück.

bool(print) -> True
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
mmueller-87
User
Beiträge: 12
Registriert: Sonntag 11. Juni 2023, 07:25

Guten Morgen an alle,

dank eurer Hilfe läuft alles so wie erwartet. Alle möglichen Vorgänge habe ich durchgetestet und das Snippet macht alles, so wie ich es mir vorstelle. Vielen dank an alle!
DeaD_EyE hat geschrieben: Dienstag 14. Januar 2025, 09:29REPLACEMENTS_NAME_2 ist ein Relikt, weil ich das Programm nicht komplett verstanden habe.
Kurze Erläuterung:

Auf einem Raspberry Pi 4 läuft im Hintergrund eine Wetterstation mit einem SDR Receiver der mit "Predict" an bestimmten Tagen und Uhrzeiten Satellitenbilder empfängt. Dieses Sat programm speichert diese empfangenen Bilder in einem bestimmten vom Programm festgelegten Ordner mit dem folgenden Dateinamen (Beispiel):

Code: Alles auswählen

NOAA-15-20250112-173300-TA.jpg
und gleichzeitig speichert das Programm in einer SQLite Datenbank alle nötigen Informationen wie: Datum, Uhrzeit, Frequenz, Höhe, Neigung, Richtung und Modus. Diese Info frage ich anhand des Dateinamens von einer Datenbank ab und möchte diese Informationen in einem neuen Dateinamen speichern, zB so:

Code: Alles auswählen

12_01_2025_17_33_noaa-15_w_17_198_ta_137.91250.jpg
und anschließen möchte ich diese "neuen" Dateien mittels "ncftp" auf einem Remote Server hochladen und auf einer Webseite veröffentlichen.

Kurze Frage noch: Ich würde wenn das Script erfolgreich durchlaufen wurde, die Dateien mittels "ncftp" über "Subprocess" anhand des hier veröffentlichten Python Scripts hochladen. Da die Wetterstation im Garten steht, habe ich mittels 5G Internet nur eine begrenzte Bandbreite zur Verfügung (Unbegrenztes Datenvolumen, aber weniger Geschwindigkeit). Nun würde ich gerne, wenn das Python Script die Dateien hochlädt, das es eine Bandbreitenbegrenzung gibt, und "ncftp" die Dateien statt mit 3 MBits nur mit 100kBits höchlädt. Wäre das umsetzbar?
Sirius3
User
Beiträge: 18216
Registriert: Sonntag 21. Oktober 2012, 17:20

Du benutzt wirklich noch ftp?
Ich würde die Dateien per scp direkt in Python über paramiko hochladen.
Für Limitierung der Bandbreite gibt es https://linux.die.net/man/1/trickle.
Benutzeravatar
grubenfox
User
Beiträge: 593
Registriert: Freitag 2. Dezember 2022, 15:49

mmueller-87 hat geschrieben: Mittwoch 15. Januar 2025, 10:14 Nun würde ich gerne, wenn das Python Script die Dateien hochlädt, das es eine Bandbreitenbegrenzung gibt, und "ncftp" die Dateien statt mit 3 MBits nur mit 100kBits höchlädt. Wäre das umsetzbar?
[ungetestet]
Das "Subprocess" rauswerfen (inkl. ncftp) und

Code: Alles auswählen

import pyftplib
einfügen und dort dann mit throttle-bandwidth arbeiten.
Benutzeravatar
__blackjack__
User
Beiträge: 13919
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@mmueller-87: Wobei man auch bei FTP bei Python bleiben könnte — gibt ja `ftplib` in der Standardbibliothek.

@grubenfox: Auf den ersten Blick sieht `pyftplib` nach einem *Server* aus. Kann das auch Client? Denn das wird ja gebraucht.
“I am Dyslexic of Borg, Your Ass will be Laminated” — unknown
mmueller-87
User
Beiträge: 12
Registriert: Sonntag 11. Juni 2023, 07:25

Sirius3 hat geschrieben: Mittwoch 15. Januar 2025, 10:27 Du benutzt wirklich noch ftp?
Ich würde die Dateien per scp direkt in Python über paramiko hochladen.
Für Limitierung der Bandbreite gibt es https://linux.die.net/man/1/trickle.
Ja, für die simplen Bildchen würde das doch reichen. Der FTP Ordner ist so oder so nur auf einen bestimmten Ordner in einem temporären Verzeichnis festgesetzt.

Kurze Aufklärung noch, vor lauter Freude habe ich vergessen euch das Projekt Mal zu zeigen:

Bilder: https://www.astrowetter.de/
Webcam vom Standort: http://87.106.98.52:7080/wx_cam.php

Oder hat das mit der Sicherheit zutun ? Kann mich einer Aufklären ? :roll:
Antworten