Source code for lib_openmolar.admin.qt4.admin_gui

#! /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/>.    ##
##                                                                           ##
###############################################################################

import re
import sys
from xmlrpclib import Fault as ServerFault
from PyQt4 import QtGui, QtCore

from lib_openmolar.common.connect import ProxyClient, ProxyUser

from lib_openmolar.common.datatypes import ConnectionData
from lib_openmolar.common.qt4.widgets import RestorableApplication

from lib_openmolar.admin import qrc_resources

from lib_openmolar.admin.connect import AdminConnection

from lib_openmolar.admin.db_tools.proxy_manager import ProxyManager

from lib_openmolar.common.qt4.dialogs import *
from lib_openmolar.admin.qt4.dialogs import *

from lib_openmolar.admin.qt4.classes import (
    AdminTabWidget,
    LogWidget,
    AdminSessionWidget)

from lib_openmolar.common.qt4.postgres.postgres_mainwindow import \
    PostgresMainWindow

##TODO for windows version... this will need to be tweaked.
CONF_DIR = "/etc/openmolar/admin/connections"

def require_session(func):
    '''
    a decorator function around methods that require a database session
    '''
    def sessionf(self):
        if not self.has_pg_connection:
            self.advise("no session started",1)
            return None
        return func(self)

    return sessionf


[docs]class AdminMainWindow(PostgresMainWindow, ProxyManager): ''' This class is the core application. ''' log = LOGGER CONN_CLASS = AdminConnection
[docs] def __init__(self, parent=None): PostgresMainWindow.__init__(self, parent) self.setMinimumSize(600, 400) self.setWindowTitle("Openmolar Admin") self.setWindowIcon(QtGui.QIcon(":icons/openmolar-server.png")) ## Main Menu ## "file" icon = QtGui.QIcon.fromTheme("network-wired") self.action_omconnect = QtGui.QAction(icon, "OM %s"% _("Connect"), self) self.action_omconnect.setToolTip( _("Connect (to an openmolar server)")) icon = QtGui.QIcon.fromTheme("network-error") self.action_omdisconnect = QtGui.QAction(icon, "OM %s"% _("Disconnect"), self) self.action_omdisconnect.setToolTip( _("Disconnect (from an openmolar server)")) insertpoint = self.action_connect self.menu_file.insertAction(insertpoint, self.action_omconnect) self.menu_file.insertAction(insertpoint,self.action_omdisconnect) self.menu_file.insertSeparator(insertpoint) insertpoint = self.action_connect self.main_toolbar.insertAction(insertpoint, self.action_omconnect) self.main_toolbar.insertAction(insertpoint, self.action_omdisconnect) ## "Database Tools" self.menu_database = QtGui.QMenu(_("&Database Tools"), self) self.insertMenu_(self.menu_database) icon = QtGui.QIcon.fromTheme("contact-new") self.action_new_database = QtGui.QAction(icon, _("New Openmolar Database"), self) icon = QtGui.QIcon(":icons/database.png") self.action_populate_demo = QtGui.QAction(icon, _("Populate database with demo data"), self) icon = QtGui.QIcon(":icons/database.png") self.action_import_data = QtGui.QAction(icon, _("Import data from other sources"), self) self.menu_database.addAction(self.action_new_database) self.menu_database.addAction(self.action_populate_demo) self.menu_database.addAction(self.action_import_data) self.database_toolbar = QtGui.QToolBar(self) self.database_toolbar.setObjectName("Database Toolbar") self.database_toolbar.toggleViewAction().setText(_("Database Toolbar")) self.database_toolbar.addAction(self.action_new_database) self.database_toolbar.addAction(self.action_populate_demo) self.database_toolbar.addAction(self.action_import_data) self.insertToolBar(self.help_toolbar, self.database_toolbar) self.log_widget = LogWidget(LOGGER, self.parent()) self.log_widget.welcome() self.log_dock_widget = QtGui.QDockWidget(_("Log"), self) self.log_dock_widget.setObjectName("LogWidget") #for save state! self.log_dock_widget.setWidget(self.log_widget) self.addDockWidget(QtCore.Qt.BottomDockWidgetArea, self.log_dock_widget) self.action_show_log = self.log_dock_widget.toggleViewAction() insertpoint = self.action_show_statusbar self.menu_view.insertAction(insertpoint, self.action_show_log) #take a note of this before restoring settings self.system_font = self.font() #### now load stored settings #### self.loadSettings() self.pg_sessions = [] self.end_pg_sessions() self.connect_signals() self.show() QtCore.QTimer.singleShot(100, self.setBriefMessageLocation) QtCore.QTimer.singleShot(100, self._init_proxies)
[docs] def connect_signals(self): ''' set up signals/slots ''' ##some old style connects are used to ensure argument (bool=0) ##is not passed to the slot self.action_omconnect.triggered.connect(self.om_connect) self.action_omdisconnect.triggered.connect(self.om_disconnect) self.action_show_log.triggered.connect(self.show_log) self.action_new_database.triggered.connect(self.create_new_database) self.action_populate_demo.triggered.connect(self.populate_demo) self.action_import_data.triggered.connect(self.import_data) self.connect(self.central_widget, QtCore.SIGNAL("end_pg_sessions"), self.end_pg_sessions) self.known_server_widget.shortcut_clicked.connect(self.manage_shortcut) self.known_server_widget.server_changed.connect(self.set_proxy_index)
@property
[docs] def central_widget(self): ''' overwrite the property of the Base Class ''' if self._central_widget is None: LOGGER.debug("AdminMainWindow.. creating central widget") self._central_widget = AdminTabWidget(self) self.known_server_widget = self._central_widget.known_server_widget self._central_widget.add = self._central_widget.addTab self._central_widget.remove = self._central_widget.removeTab return self._central_widget
@property
[docs] def new_session_widget(self): ''' overwrite the property of the Base Class ''' admin_session_widget = AdminSessionWidget(self) admin_session_widget.query_error.connect(self.advise_dl) admin_session_widget.query_sucess.connect(self.advise) return admin_session_widget
def _init_proxies(self): ''' called at startup, and by the om_connect action ''' self.wait() ProxyManager._init_proxies(self) self.known_server_widget.clear() for client in self.proxy_clients: self.known_server_widget.add_proxy_client(client) self.known_server_widget.setEnabled(True) self.wait(False)
[docs] def om_disconnect(self): ProxyManager.om_disconnect(self) self.known_server_widget.clear() self.known_server_widget.setEnabled(False)
def switch_server_user(self): self.advise("we need to up your permissions for this",1) return False
[docs] def show_log(self): ''' toggle the state of the log dock window ''' if self.action_show_log.isChecked(): self.log_dock_widget.show() else: self.log_dock_widget.hide()
[docs] def end_pg_sessions(self, shutting_down=False): ''' overwrite baseclass function ''' if shutting_down or ( self.has_pg_connection and self.central_widget.closeAll()): PostgresMainWindow.end_pg_sessions(self) else: if self.central_widget.closeAll(): PostgresMainWindow.end_pg_sessions(self) self.update_session_status()
[docs] def use_proxy_database(self, db_name): ''' user has clicked on a link provided by a :doc:`ProxyClient` requesting a session on dbname ''' ## TODO this should use more information pulled from the proxy server result, user, passwd = self.get_user_pass(db_name) if not result: return client = self.known_server_widget.current_client host = client.host port = 5432 connection_data = ConnectionData( connection_name = "%s_%s:%s"% (db_name, host, 5432), host = host, user = user, password = passwd, port = 5432, db_name = db_name) pg_session = AdminConnection(connection_data) if self._attempt_connection(pg_session): self.add_session(pg_session) self.update_session_status()
[docs] def create_new_database(self): ''' raise a dialog, then create a database with the chosen name ''' dl = NewDatabaseDialog(self) if not dl.exec_() or dl.database_name == "": self.display_proxy_message() return dbname = dl.database_name try: ProxyManager.create_database(self, dbname) except ProxyClient.PermissionError as exc: self.advise(exc.message, 2)
[docs] def create_demo_database(self): ''' initiates the demo database ''' LOGGER.info("creating demo database") result = ProxyManager.create_demo_database(self) LOGGER.info(result) if (result and QtGui.QMessageBox.question(self, _("Confirm"), u"%s"% _("Populate with demo data now?"), QtGui.QMessageBox.Ok | QtGui.QMessageBox.Cancel, QtGui.QMessageBox.Ok) == QtGui.QMessageBox.Ok): self.populate_demo() self.display_proxy_message()
[docs] def set_permissions(self, database): ''' alters permissions on a known database ''' dl = NewUserPasswordDialog(self) result, user, password = dl.getValues() message = "TODO - enable set_permissions for %s, %s"% (user, "****") self.advise(message, 1) if result: LOGGER.info(message)
@property
[docs] def chosen_pg_session(self): if len(self.session_widgets) == 1: i = 0 else: i = self.central_widget.currentIndex()-1 pg_session = self.session_widgets[i].pg_session return pg_session
@require_session
[docs] def populate_demo(self): ''' catches signal when user hits the demo action ''' pg_session = self.chosen_pg_session LOGGER.info("calling populate demo on session %s"% pg_session) dl = PopulateDemoDialog(pg_session, self) if not dl.exec_(): self.advise(_("Demo data population was abandoned"), 1)
@require_session
[docs] def import_data(self): ''' raise a dialog to import into the current database ''' pg_session = self.chosen_pg_session LOGGER.info("calling import data for session %s"% pg_session) from lib_openmolar.admin.data_import import ImportDialog dl = ImportDialog(pg_session, self) if not dl.exec_(): self.advise(_("Import was abandoned"), 1)
[docs] def manage_db(self, dbname): ''' raise a dialog, and provide database management tools ''' dl = ManageDatabaseDialog(dbname, self) if dl.exec_(): if dl.manage_users: self.advise("manage users") elif dl.drop_db: self.advise(u"%s %s"%(_("dropping database"), dbname)) self.drop_db(dbname) elif dl.truncate_db: self.advise(u"%s %s"%(_("deleting all data from database"), dbname)) self.truncate_db(dbname)
[docs] def closeEvent(self, event=None): ''' re-implement the close event of QtGui.QMainWindow, and check the user really meant to do this. ''' if (self.log_widget.dirty and QtGui.QMessageBox.question(self, _("Confirm"), _("You have unsaved log changes - Quit Application?"), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.Yes) == QtGui.QMessageBox.No): event.ignore() else: self.saveSettings() self.end_pg_sessions(shutting_down=True)
@property
[docs] def confirmDataOverwrite(self): ''' check that the user is prepared to lose any changes ''' return self.get_confirm(u"<p>%s<br />%s</p>"% ( _("this action will overwrite any current data stored"), _("proceed?")))
[docs] def save_template(self): ''' save the template, so it can be re-used in future ''' try: filepath = QtGui.QFileDialog.getSaveFileName(self, _("save template file"),"", _("openmolar template files ")+"(*.om_xml)") if filepath != '': if not re.match(".*\.om_xml$", filepath): filepath += ".om_xml" f = open(filepath, "w") f.write(self.template.toxml()) f.close() self.advise(_("Template Saved"), 1) else: self.advise(_("operation cancelled"), 1) except Exception, e: self.advise(_("Template not saved")+" - %s"% e, 2)
[docs] def load_template(self): ''' change the default template for a new database ''' if not self.confirmDataOverwrite: return filename = QtGui.QFileDialog.getOpenFileName(self, _("load an existing template file"),"", _("openmolar template files")+" (*.om_xml)") if filename != '': try: self.template = minidom.parse(str(filename)) self.advise(_("template loaded sucessfully"),1) except Exception, e: self.advise(_("error parsing template file")+" - %s"% e, 2) else: self.advise(_("operation cancelled"), 1)
[docs] def loadSettings(self): PostgresMainWindow.loadSettings(self) QtCore.QSettings().setValue("connection_conf_dir", CONF_DIR)
[docs] def show_about(self): ''' raise a dialog showing version info etc. ''' ABOUT_TEXT = "<p>%s</p><pre>%s\n%s</pre><p>%s<br />%s</p>"% ( _(''' This application provides tools to manage and configure your database server and can set up either a demo openmolar database, or a customised database for a specific dental practice situation.'''), _("Version"), SETTINGS.VERSION, "<a href='http://www.openmolar.com'>www.openmolar.com</a>", 'Neil Wallace - rowinggolfer@googlemail.com') self.advise(ABOUT_TEXT, 1)
[docs] def show_help(self): ''' todo - this is the same as show_about ''' self.show_about()
[docs] def switch_server_user(self): ''' to change the user of the proxy up to admin overwrites :doc:`ProxyManager` function ''' LOGGER.debug("switch_server_user called") self.advise("we need to up your permissions for this", 1) dl = UserPasswordDialog(self) dl.set_name("admin") if dl.exec_(): name = dl.name psword = dl.password user = ProxyUser(name, psword) client = self.selected_client LOGGER.debug("switch user of %s to %s"% (client, user)) client.set_user(user) return True return False
[docs] def display_proxy_message(self): ''' display the proxy message. overwrites :doc:`ProxyManager` function ''' self.known_server_widget.set_html(self.selected_client.html)
[docs] def manage_shortcut(self, url): ''' the admin browser (which commonly contains messages from the openmolar_server) is connected to this slot. when a url is clicked it finds it's way here for management. unrecognised signals are send to the user via the notification. ''' LOGGER.debug("manage_shortcut %s"% url) try: if url == "install_demo": LOGGER.debug("Install demo called via shortcut") self.create_demo_database() elif re.match("connect_.*", url): dbname = re.match("connect_(.*)", url).groups()[0] self.advise("start session on database %s"% dbname) self.use_proxy_database(dbname) elif re.match("manage_.*", url): dbname = re.match("manage_(.*)", url).groups()[0] self.manage_db(dbname) elif url == 'Retry_230_connection': self.advise(_("retrying connection")) try: self.selected_client.connect() except ProxyClient.ConnectionError as ex: self.advise(ex.message, 2) else: self.advise("%s<hr />%s"% (_("Shortcut not found"), url), 2) except ProxyManager.PermissionError as exc: self.advise("%s<hr />%s" %(_("Permission denied"), exc), 2)
def main(): app = RestorableApplication("openmolar-admin") ui = AdminMainWindow() ui.show() app.exec_() app = None if __name__ == "__main__": import gettext gettext.install("openmolar") sys.exit(main())