Source code for lib_openmolar.common.qt4.postgres.openmolar_database
#! /usr/bin/env python
# -*- coding: utf-8 -*-
###############################################################################
## ##
## Copyright 2010, Neil Wallace <rowinggolfer@googlemail.com> ##
## ##
## This program is free software: you can redistribute it and/or modify ##
## it under the terms of the GNU General Public License as published by ##
## the Free Software Foundation, either version 3 of the License, or ##
## (at your option) any later version. ##
## ##
## This program is distributed in the hope that it will be useful, ##
## but WITHOUT ANY WARRANTY; without even the implied warranty of ##
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ##
## GNU General Public License for more details. ##
## ##
## You should have received a copy of the GNU General Public License ##
## along with this program. If not, see <http://www.gnu.org/licenses/>. ##
## ##
###############################################################################
'''
provides 3 classes.
ConnectionError - a custom python exception, raised if connection times out
SchemaError - a custom python exception, raised if connection times out
OpenmolarDatabase - a custom class inheriting from Pyqt4.QSql.QSqlDatabase
'''
import logging
from PyQt4 import QtSql, QtGui, QtCore
from lib_openmolar.common.datatypes import ConnectionData
[docs]class ConnectionError(Exception):
'''
a custom Exception
'''
pass
[docs]class OpenmolarDatabase(QtSql.QSqlDatabase):
'''
inherits from PyQt4.QSql.QSqlDatabase
adds a function "connect", which opens the connection whilst
using a qt wait cursor.
Will Raise a Connection error if connection has not been established
within 10 seconds
'''
_schema_version = None
[docs] class SchemaVersionError(Exception):
pass
[docs] def __init__(self, connection_data):
assert type(connection_data) == ConnectionData, (
"argument for database connection MUST be of type ConnectionData")
QtSql.QSqlDatabase.__init__(self, "QPSQL")
self.connection_data = connection_data
self.setHostName(connection_data.host)
self.setPort(connection_data.port)
if connection_data.CONNECTION_TYPE == connection_data.TCP_IP:
self.setConnectOptions("requiressl=1")
self.setUserName(connection_data.user)
self.setPassword(connection_data.password)
self.setDatabaseName(connection_data.db_name)
self.driver().notification.connect(self.notification_received)
def _wait_cursor(self, waiting=False):
'''
provides/removes a WaitCursor during connection and parsing functions
'''
try:
if waiting:
QtGui.QApplication.instance().setOverrideCursor(QtCore.Qt.WaitCursor)
else:
QtGui.QApplication.instance().restoreOverrideCursor()
except AttributeError: #no gui
pass
[docs] def connect(self, *args):
'''
open the connection, raising an error if fails or timeouts
optional arguments of (user, password)
'''
self._schema_version = None
logging.debug("OpenmolarDatabase connecting")
self._wait_cursor()
connection_in_progress = True
def time_out():
if connection_in_progress:
self._wait_cursor(False)
if not self.isOpen():
message = self.lastError().text()
self.close()
raise ConnectionError("Time out Error %s"% message)
if QtCore.QCoreApplication.instance():
QtCore.QTimer.singleShot(1000, time_out) ## 10 seconds.
logging.info("timeout set to 10 seconds")
if args:
user = args[0]
password = args[1]
logging.debug("connecting with user '%s', password '%s'"% (
user, "*"* len(password)))
connected = self.open(user, password)
else:
logging.debug("connecting with default params")
connected = self.open()
connection_in_progress = False
self._wait_cursor(False)
if not connected:
raise ConnectionError(
"<pre font='courier'>%s</pre>"% self.lastError().text())
else:
self.subscribeToNotifications()
[docs] def subscribeToNotifications(self):
'''
this should be overwritten when this connection is implemented
postgres can emit signals when the database is changed by another
client.
the query is simple
NOTIFY new_appointment_made
'''
logging.warning("classes inheriting from OpenmolarDatabase should "
"re-implement function subscribeToNotifications")
#self.driver().subscribeToNotification("new_appointment_made")
[docs] def notification_received(self, notification):
'''
the database has emitted a notify signal with text notification
that we are subscribed to.
we emit a qt signal, that should be connected by any application.
'''
logging.info("db notification received '%s'"% notification)
QtGui.QApplication.instance().emit(
QtCore.SIGNAL("db notification"), notification)
[docs] def emit_notification(self, notification):
q_query = QtSql.QSqlQuery("NOTIFY %s"% notification, self)
if q_query.lastError().isValid():
print "error", q_query.lastError().text()
@property
[docs] def description(self):
'''
databasename, host and port
'''
return u"%s %s:%s"% (
self.databaseName(), self.hostName(), self.port())
@property
[docs] def schema_version(self):
'''
poll the database to get the schema version from settings table
'''
if self._schema_version is None:
logging.debug("polling database for schema version")
query = "select max(data) from settings where key='schema_version'"
q_query = QtSql.QSqlQuery(query, self)
if not q_query.first():
self._schema_version = "???"
else:
self._schema_version = q_query.value(0).toString()
return self._schema_version
def _test():
logging.basicConfig(level=logging.DEBUG)
app = QtGui.QApplication([])
parent = QtGui.QWidget()
conn_data = ConnectionData()
conn_data.demo_connection()
db = OpenmolarDatabase(conn_data)
logging.debug(db)
message = '<body>'
try:
db.connect()
message += '<h4>connection Ok... </h4>'
message += 'Schema Version %s'% db.schema_version
db.emit_notification("hello")
db.close()
except ConnectionError as e:
message = u"connection error<hr />%s"% e
app.restoreOverrideCursor()
message += "</body>"
QtGui.QMessageBox.information(parent, "result", message)
app.closeAllWindows()
if __name__ == "__main__":
_test()