ich bin gerade an einem kleinen Hochlade-Programm dran. Obwohl es noch relativ im Anfangsstadium ist, wollte ich fragen, ob jemand kurz darüber schauen und sagen könnte, ob ich irgendwo eklatante Fehler gemacht habe (Jetzt ist der Code ja noch halbwegs übersichtlich, so dass das noch möglich ist)?
Im Moment sieht es so aus:
Das Programm soll Dateien per Kommandozeilenparameter übergeben bekommen, diese dann auf einen FTP-Server hochladen und in eine Datenbank mit „Ablaufdatum“ eintragen. Beim nächsten Start soll dann geschaut werden, welche Dateien abgelaufen sind (im Bild rot markiert) und diese dann gelöscht werden. So soll verhindert werden, dass der FTP-Server eine vermüllte Datenhalde wird.
Also ich habe das Programm in mehrere Dateien aufgeteilt:
uploader.py (Das Hauptprogramm):
Code: Alles auswählen
import sys
import gui
from PyQt4 import QtGui, QtCore
def start(argv):
app = QtGui.QApplication(argv)
QtGui.QApplication.setApplicationName("Uploader")
#Translation
myappTranslator = QtCore.QTranslator()
language = QtCore.QLocale.system().name()
myappTranslator.load("uploader_{0}".format(language), "i18n")
app.installTranslator(myappTranslator)
#Initialise window
dialog = gui.MainWindow()
dialog.show()
sys.exit(app.exec_())
if __name__ == "__main__":
start(sys.argv)
dann gui.py:
Code: Alles auswählen
import sys
import time, datetime
import os
from background import *
from PyQt4 import QtCore, QtGui
from src.Ui_mainwindow import Ui_mainwindow
def extractfilename(file):
"""Extrahiert den Dateinamen aus einem kompletten Pfad"""
os.path.normpath(file)
return(os.path.split(file)[1])
class MainWindow(QtGui.QDialog, Ui_mainwindow):
def __init__(self):
"""Creates the main window and connects to the database and calls
functions to populate the servers and files list"""
QtGui.QDialog.__init__(self)
self.setupUi(self)
#Connect to database
databasepath = os.path.split(sys.argv[0])[0]
databasefile = os.path.join(databasepath, "files.sqlite3")
self.databaseconnection = Database(databasefile)
#Load servers and files from database
self.listservers()
self.readarguments()
self.listfiles()
#Slots
self.connect(self.buttonClose, QtCore.SIGNAL("clicked()"), self.onClose)
self.connect
def listservers(self):
"""Create server objects from every entry in the database"""
self.servers=[]
for server in self.databaseconnection.readserver():
#Appends a new server object with the settings from the DB
self.servers.append(Server(server))
self.cmbServer.insertItem(self.servers[-1].serverid, self.servers[-1].name)
self.cmbServer.insertItem(50, self.tr("Create a new server..."))
#TODO: Hier eine bessere Lösung anstatt der 50 finden.
def listfiles(self):
"""Inserts the current files listed in the database"""
red = QtGui.QColor("red")
self.files = []
for file in self.databaseconnection.readfiles():
self.files.append(DatabaseFile(file))
#Inserts files into QTableWidget
self.tblFiles.setRowCount(len(self.files))
row = 0
for file in self.files:
column = 0
#Converts expiry date from timestamp to readable
expiry_readable = datetime.datetime.fromtimestamp(file.expirydate).strftime(self.tr("%Y-%m-%d %H:%M:%S"))
#Converts server number to server name
for server in self.servers:
if server.serverid == file.serverid:
server_readable = server.name
break
for item in (file.name, expiry_readable, server_readable):
widgetitem = QtGui.QTableWidgetItem(item)
if file.expired == True:
widgetitem.setTextColor(red)
self.tblFiles.setItem(row, column, widgetitem)
column += 1
row += 1
def readarguments(self):
"""Reads the files from the arguments and saves them into the database"""
if len(sys.argv) > 1:
for file in sys.argv[1:]:
filename = extractfilename(file)
self.databaseconnection.fileentry(filename, int(time.time()) + 500, 1)
def onClose(self):
self.databaseconnection.close()
self.close()
Dann wird die Funktion listservers() aufgerufen,welche die Datenbank ausliest und dort je Server-Objekt mit den verschiedenen Einstellungen erstellt. Hier direkt meine erste Frage: Lohnt es sich hier überhaupt, das mit einer Klasse zu machen? Ich finde es ganz nett, dass je ein Server eine Klasse hat und so alle Einstellungen gebündelt sind, aber ist das irgendwie problematisch (oder ressourcenverschwenderisch)? Für jeden Server wird dann auch ein Eintrag in der QComboBox cmbServer gemacht.
Anschließend liest er in readarguments() die Kommandozeilenargumente aus, die dem Programm übergeben wurden. Der Dateiname selber wird getrennt (also z.b. d:\uploader.py -> uploader.py) und dann dem Datenbankobjekt in der Funktion „fileentry“ übergeben. Die Funktion speichert dann die Datei in der Datenbank zusammen mit dem Ablaufdatum (im Moment noch fix jetzige Zeit + 500 Sekunden) und der Server-ID (im Moment noch fest). Im Moment noch nicht implementiert, allerdings soll readarguments() nachher auch noch die Dateien hochladen.
Anschließend wird listfiles() aufgerufen. Hierbei liest er alle Dateien, die in der Datenbank gespeichert sind, ein, erstellt für jede Datei ein Objekt und listet dies anschließend in dem QTableWidget tblFiles. Hierbei hätte ich mehrere Fragen:
1. Lohnt es sich, für jeden Dateieintrag eine Klasse zu machen? Ich habe mir überlegt, dass ich nachher z.B. der Datenbank oder der Server-Klasse o.ä. dann sehr einfach einfach jeweils ein Dateiobjekt übergeben könnte, wodurch dann intern z.B. der Dateiname extrahiert wird, die Datei von der Festplatte gelesen wird usw. blabla. Das könnte man dann soweit einfach dort intern verstecken und bräuchte immer nur das Dateiobjekt übergeben.
2. Meine Schleife um das QTableWidget zu füllen sieht mir etwas blöd aus. Im Moment geht er halt praktisch erst jede Spalte durch, erhöht dann column um 1, und nachdem eine Zeile fertig ist, geht er in die nächste Spalte. Ist das soweit in Ordnung?
Ok, und hier dann noch die Hintergrundklassen in der Datei background.py:
Code: Alles auswählen
import sqlite3
import ftplib
import time
class Server(object):
"""Class of servers
A class of a server, which connects to FTP and uploads
files. """
def __init__(self, settings):
"""Reads multiple settings (in the setting of a SQLite entry
and creates a new server object"""
self.serverid = settings[0]
self.name = settings[1]
self.server = settings[2]
self.port = settings[3]
self.encrypted = settings[4]
self.user = settings[5]
self.password= settings[6]
self.remotefolder = settings[7]
#self.setting = settings
def connectftp(self):
self.ftp = ftplib.FTP(self.server, self.user, self.password)
self.ftp.cwd(self.remotefolder)
#TODO: Verschlüsselte Verbindungen unterstützen
def closeftp(self):
self.ftp.quit()
def upload(self, file, filename):
"""Uploads a file
Accepts a file (a complete path, e.g. d:\file.pdf) and the filename
(file.pdf) and uploads it.
"""
with open(file, "rb") as f:
self.ftp.storbinary("STOR {}".format(filename), f)
#TODO: Unicode unterstützen.
def delete(self, filename):
try:
self.ftp.delete(filename)
except:
print(self.tr("{0} was not on the server".format(filename)))
def listfiles(self):
pass
class DatabaseFile(object):
def __init__(self, databaseentry):
self.fileid = databaseentry[0]
self.name = databaseentry[1]
self.expirydate = databaseentry[2]
self.serverid = databaseentry[3]
if self.expirydate < int(time.time()):
self.expired = True
else:
self.expired = False
class Database(object):
def __init__(self, databasefile):
"""Connects to SQLite database"""
self.database = sqlite3.connect(databasefile)
self.cursor = self.database.cursor()
self.createtable()
def close(self):
"""Closes the SQLite database"""
self.database.close()
def createtable(self):
tblcmd = """CREATE TABLE IF NOT EXISTS files
(fileid INTEGER PRIMARY KEY, url TEXT, expire INT, serverid INT)"""
self.cursor.execute(tblcmd)
tblcmd = """CREATE TABLE IF NOT EXISTS servers
(serverid INTEGER PRIMARY KEY,
name TEXT,
server TEXT,
port INT,
encrypted INT,
user TEXT,
password TEXT,
remotefolder TEXT)"""
self.cursor.execute(tblcmd)
self.database.commit()
def fileentry(self, file, expire, serverid):
"""Inserts a new file with expiry date into the webserver"""
tblcmd = "INSERT INTO files(url, expire, serverid) VALUES(?, ?, ?)"
self.cursor.execute(tblcmd, (file, expire, serverid))
self.database.commit()
#TODO: Keine bereits vorhanden Einträge zulassen.
def saveserver(self, settings):
tblcmd = """INSERT INTO servers
(name, server, port, encrypted, user, password, remotefolder)
VALUES(?, ?, ?, ?, ?, ?, ?)"""
self.cursor.execute(tblcmd, settings)
self.database.commit()
def readserver(self):
"""Reads all saved server entries from the database"""
self.cursor.execute("SELECT * FROM servers")
servers = self.cursor.fetchall()
return servers
def readfiles(self):
"""Displays a list of all files stored in the database """
self.cursor.execute("SELECT * FROM files")
files = self.cursor.fetchall()
return files
def checkifold(self, time):
"""Returns old files. The current time must be supplied."""
self.cursor.execute("SELECT fileid, url FROM files WHERE expire < ?", (int(time),))
oldfiles = self.cursor.fetchall()
return oldfiles
def deletefiles(self, fileid):
"""Deletes a file from the database"""
self.cursor.execute("DELETE FROM files WHERE fileid = ?", (fileid,))
self.database.commit()
Dann die Klasse DatabaseFile, die die Einstellungen einer Datei aus der Datenbank aufnehmen soll, z.B. wie der Dateiname ist und wann sie abläuft. Ich will da noch einige andere Sachen implementieren.
Außerdem die Klasse Database, die eben eine Datenbank mit den entsprechenden Tabellen erstellt und Einträge dort einträgt.
Danke an jeden, der sich den ganzen Text durchliest