def __init__(self, parent=None): """Constructor of ExportModule View """ QWidget.__init__(self, parent) self.parent = parent #----------------------------------------------------------------------- #----------------------- Logger ---------------------------------------- self.logger = logging.getLogger(__name__) logging.config.fileConfig('logging.ini', disable_existing_loggers=False) #----------------------------------------------------------------------- #---------------------- App config -------------------------------------- self.appConfig = AppConfigurationService() #----------------------------------------------------------------------- #--------------------- Create Module UI -------------------------------- self.setupUi(self) #----------------------------------------------------------------------- #----------------------- Services -------------------------------------- # There are two http services, one for communicating with my own # site server/database section = "RadPlanBioServer" hostname = self.appConfig.get(section)["host"] port = self.appConfig.get(section)["port"] self.svcHttp = HttpConnectionService(hostname, port, UserDetails()) #TODO: SiteName according to user account self.mySite = self.svcHttp.getPartnerSiteByName("DKTK-DRESDEN") baseUrl = self.mySite.edc.soapbaseurl # Create connection artefact to OC self.ocConnectInfo = OCConnectInfo(baseUrl, OCUserDetails().username) self.ocConnectInfo.setPassword(OCUserDetails().password) self.ocWebServices = OCWebServices(self.ocConnectInfo) # Create REST mainzellieste userName = self.mySite.pidg.adminusername password = self.mySite.pidg.adminpassword baseUrl = self.mySite.pidg.generatorbaseurl apiKey = self.mySite.pidg.apikey connectInfo = MainzellisteConnectInfo(baseUrl, userName, password, apiKey) self.svcPseudonymisation = PseudonymisationService(connectInfo) #----------------------------------------------------------------------- #----------------------- On Create ------------------------------------- self.reloadSites() #----------------------------------------------------------------------- #---------- Load modules buttons - events definitions ------------------ self.btnNew.clicked.connect(self.btnNewClicked) self.btnReload.clicked.connect(self.btnReloadClicked) self.tvStudies.selectionModel().currentChanged.connect( self.tvStudyChanged)
def prepareServices(self): """Prepare services for this module """ # HTTP connection to RadPlanBio server (Database) self._svcHttp = HttpConnectionService(ConfigDetails().rpbHost, ConfigDetails().rpbHostPort, UserDetails()) self._svcHttp.application = ConfigDetails().rpbApplication if ConfigDetails().proxyEnabled: self._svcHttp.setupProxy(ConfigDetails().proxyHost, ConfigDetails().proxyPort, ConfigDetails().noProxy) if ConfigDetails().proxyAuthEnabled: self._svcHttp.setupProxyAuth(ConfigDetails().proxyAuthLogin, ConfigDetails().proxyAuthPassword) # Read partner site of logged user self._mySite = self._svcHttp.getMyDefaultAccount().partnersite # Create connection artefact to users main OpenClinica SOAP baseUrl = self._mySite.edc.soapbaseurl self.ocConnectInfo = OCConnectInfo(baseUrl, OCUserDetails().username) self.ocConnectInfo.setPasswordHash(OCUserDetails().passwordHash) if ConfigDetails().proxyEnabled: self.ocWebServices = OCWebServices( self.ocConnectInfo, ConfigDetails().proxyHost, ConfigDetails().proxyPort, ConfigDetails().noProxy, ConfigDetails().proxyAuthLogin, ConfigDetails().proxyAuthPassword) else: self.ocWebServices = OCWebServices(self.ocConnectInfo) # ODM XML metadata processing self.fileMetaDataService = OdmFileDataService() # DICOM PROCESSING SERVICE self._svcDicom = DicomService()
class CreateSubjectModule(QWidget, CreateSubjectModuleUI): """PullDataRequest Module view class """ #---------------------------------------------------------------------- #--------------------------- Constructors ----------------------------- def __init__(self, parent=None): """Constructor of ExportModule View """ QWidget.__init__(self, parent) self.parent = parent #----------------------------------------------------------------------- #----------------------- Logger ---------------------------------------- self.logger = logging.getLogger(__name__) logging.config.fileConfig('logging.ini', disable_existing_loggers=False) #----------------------------------------------------------------------- #---------------------- App config -------------------------------------- self.appConfig = AppConfigurationService() #----------------------------------------------------------------------- #--------------------- Create Module UI -------------------------------- self.setupUi(self) #----------------------------------------------------------------------- #----------------------- Services -------------------------------------- # There are two http services, one for communicating with my own # site server/database section = "RadPlanBioServer" hostname = self.appConfig.get(section)["host"] port = self.appConfig.get(section)["port"] self.svcHttp = HttpConnectionService(hostname, port, UserDetails()) #TODO: SiteName according to user account self.mySite = self.svcHttp.getPartnerSiteByName("DKTK-DRESDEN") baseUrl = self.mySite.edc.soapbaseurl # Create connection artefact to OC self.ocConnectInfo = OCConnectInfo(baseUrl, OCUserDetails().username) self.ocConnectInfo.setPassword(OCUserDetails().password) self.ocWebServices = OCWebServices(self.ocConnectInfo) # Create REST mainzellieste userName = self.mySite.pidg.adminusername password = self.mySite.pidg.adminpassword baseUrl = self.mySite.pidg.generatorbaseurl apiKey = self.mySite.pidg.apikey connectInfo = MainzellisteConnectInfo(baseUrl, userName, password, apiKey) self.svcPseudonymisation = PseudonymisationService(connectInfo) #----------------------------------------------------------------------- #----------------------- On Create ------------------------------------- self.reloadSites() #----------------------------------------------------------------------- #---------- Load modules buttons - events definitions ------------------ self.btnNew.clicked.connect(self.btnNewClicked) self.btnReload.clicked.connect(self.btnReloadClicked) self.tvStudies.selectionModel().currentChanged.connect( self.tvStudyChanged) #---------------------------------------------------------------------- #------------------- Module buttons Handlers -------------------------- def btnNewClicked(self): """ """ index = self.tabCreateSubjectModule.currentIndex() if index == 1: # Initialize dialog and bind data to UI dialog = NewSubjectDialog(self) dialog.svcPseudonymisation = self.svcPseudonymisation dialog.setData(self.selectedStudy, self.selectedStudySite) # Show dialog dialogResult = dialog.exec_() # When ok commit session transaction if dialogResult == QtGui.QDialog.Accepted: try: newStudySubject = dialog.newStudySubject self.ocWebServices.createStudySubject( newStudySubject, self.selectedStudy, self.selectedStudySite) except: QtGui.QMessageBox.warning(self, 'Error', 'OC study subject not created.') # Reload index = self.tabCreateSubjectModule.currentIndex() self.loadModuleData(index) elif index == 2: studyEvents = self.ocWebServices.listAllStydyEventDefinitionsByStudy( self.selectedStudy) dialog = NewEventDialog(self) dialog.setData(self.selectedStudy, self.selectedStudySite, self.selectedSubject, studyEvents) # Show dialog dialogResult = dialog.exec_() # When ok commit session transaction if dialogResult == QtGui.QDialog.Accepted: #try: unsheduledEvent = dialog.selectedEvent self.ocWebServices.scheduleStudyEvent(self.selectedStudy, self.selectedStudySite, self.selectedSubject, unsheduledEvent) #except: # QtGui.QMessageBox.warning(self, 'Error', 'OC study event was not sheduled.') # Reload index = self.tabCreateSubjectModule.currentIndex() self.loadModuleData(index) def btnReloadClicked(self): """ """ index = self.tabCreateSubjectModule.currentIndex() self.loadModuleData(index) #---------------------------------------------------------------------- #------------------- TableView Handlers ------------------------------- def tvStudyChanged(self, current, previous): self.tvSubjects.setModel(None) index = current.row() self.selectedStudy = None self.selectedStudySite = None counter = 0 found = False for study in self.studies: for site in study.sites: if counter == index: self.selectedStudy = study self.selectedStudySite = site found = True break else: counter = counter + 1 if found: break if found: self.reloadSubjects() def tvSubjectChanged(self, current, previous): index = current.row() self.selectedSubject = self.subjects[index] self.reloadEvents() def tvEventsChanged(self, current, previous): index = current.row() self.selectedEvent = self.events[index] #---------------------------------------------------------------------- #------------------------ Module commands ----------------------------- def loadModuleData(self, selectedIndex): if selectedIndex == 0: self.reloadSites() self.tvStudies.selectionModel().currentChanged.connect( self.tvStudyChanged) elif selectedIndex == 1: self.reloadSubjects() self.tvSubjects.selectionModel().currentChanged.connect( self.tvSubjectChanged) elif selectedIndex == 2: self.reloadSubjects() self.tvSubjects.selectionModel().currentChanged.connect( self.tvSubjectChanged) self.selectedSubject = first.first( subject for subject in self.subjects if subject.subject.uniqueIdentifier == self.selectedSubject.subject.uniqueIdentifier) self.reloadEvents() self.tvEvents.selectionModel().currentChanged.connect( self.tvEventsChanged) def reloadSites(self): successfull, self.studies = self.ocWebServices.listAllStudies() # Quick way of crating simple viewModel self.studySitesModel = QtGui.QStandardItemModel() self.studySitesModel.setHorizontalHeaderLabels( ["OID", "Partner Site", "Study"]) row = 0 for study in self.studies: if len(study.sites) > 0: for site in study.sites: siteOidValue = QtGui.QStandardItem(site.oid) siteNameValue = QtGui.QStandardItem(site.name) studyNameValue = QtGui.QStandardItem(study.name()) self.studySitesModel.setItem(row, 0, siteOidValue) self.studySitesModel.setItem(row, 1, siteNameValue) self.studySitesModel.setItem(row, 2, studyNameValue) row = row + 1 self.tvStudies.setModel(self.studySitesModel) self.tvStudies.resizeColumnsToContents() def reloadSubjects(self): self.subjects = self.ocWebServices.listAllStudySubjectsByStudySite( self.selectedStudy, self.selectedStudySite) # Quick way of crating simple viewModel self.subjectsModel = QtGui.QStandardItemModel() self.subjectsModel.setHorizontalHeaderLabels( ["PID", "Gender", "Enrollment date"]) row = 0 for studySubject in self.subjects: pidItem = QtGui.QStandardItem( studySubject.subject.uniqueIdentifier) genderItem = QtGui.QStandardItem(studySubject.subject.gender) enrollmentDateItem = QtGui.QStandardItem( studySubject.enrollmentDate) self.subjectsModel.setItem(row, 0, pidItem) self.subjectsModel.setItem(row, 1, genderItem) self.subjectsModel.setItem(row, 2, enrollmentDateItem) row = row + 1 self.tvSubjects.setModel(self.subjectsModel) self.tvSubjects.resizeColumnsToContents() def reloadEvents(self): self.events = self.selectedSubject.events # Quick way of crating simple viewModel self.eventsModel = QtGui.QStandardItemModel() self.eventsModel.setHorizontalHeaderLabels(["Event OID", "Start date"]) row = 0 for event in self.events: oidItem = QtGui.QStandardItem(event.eventDefinitionOID) startDateItem = QtGui.QStandardItem(event.startDate.isoformat()) self.eventsModel.setItem(row, 0, oidItem) self.eventsModel.setItem(row, 1, startDateItem) row = row + 1 self.tvEvents.setModel(self.eventsModel) self.tvEvents.resizeColumnsToContents()
class DicomDownloadModule(QWidget, DicomDownloadModuleUI): """Bulk download of DICOM data withing specific clinical trial """ def __init__(self, parent=None): """Constructor of DicomUploadModule """ # Setup GUI QWidget.__init__(self, parent) self.parent = parent self.setupUi(self) # Setup logger - use config file self.logger = logging.getLogger(__name__) logging.config.fileConfig('logging.ini', disable_existing_loggers=False) # List of worker threads self._threadPool = [] # Initialise main data members self._mySite = None # Prepares services and main data for this ViewModel self.prepareServices() # Initialize data structures for UI self._downloadDir = None self._studies = [] self._studySubjects = [] # Initial data load self.reloadData() # Finish UI setup self.lblOcConnection.setText("[" + OCUserDetails().username + "] " + self._mySite.edc.soapbaseurl) # Register handlers self.btnDownload.clicked.connect(self.btnDownloadClicked) self.cmbStudy.currentIndexChanged['QString'].connect( self.cmbStudyChanged) self.destroyed.connect(self.handleDestroyed) def __del__(self): """Default Destructor """ self.handleDestroyed() ## ## ### ## ## ######## ## ######## ######## ###### ## ## ## ## ### ## ## ## ## ## ## ## ## ## ## ## ## ## #### ## ## ## ## ## ## ## ## ######### ## ## ## ## ## ## ## ## ###### ######## ###### ## ## ######### ## #### ## ## ## ## ## ## ## ## ## ## ## ## ### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ######## ######## ######## ## ## ###### def cmbStudyChanged(self, text): """Executed when the concrete study is selected from studies combobox """ # Reset table views self.tvStudies.setModel(None) self.textBrowserProgress.clear() # Set selected study and reload sites self._selectedStudy = first.first( study for study in self._studies if study.name().encode("utf-8") == text.toUtf8()) # Disable download button self._selectedStudySite = None self.btnDownload.setDisabled(True) # Reload study sites self.reloadStudySites() def tblStudyItemChanged(self, current, previous): """Event handler which is triggered when selectedStudy change When the study is selected I have to clean all the other data elements Which depends on study. It means: StudySubjects, StudyEventDefinitions, StudyEventCRFs """ self.textBrowserProgress.clear() # Take the first column of selected row from table view index = self.studyProxyModel.index(current.row(), 0) # Multicentric if len(self._selectedStudy.sites) > 0: self._selectedStudySite = first.first( studySite for studySite in self._selectedStudy.sites if studySite.identifier.encode( "utf-8") == index.data().toPyObject().toUtf8()) # Enabled download button if (self._selectedStudySite is not None): self.btnDownload.setDisabled(False) else: if (self._selectedStudy is not None): self.btnDownload.setDisabled(False) # Get the study metadata self.reloadStudyMetadata() # Show the selected upload target msg = "Selected study for download: " msg += self._selectedStudy.name() + "/" if len(self._selectedStudy.sites) > 0: msg += self._selectedStudySite.name + "/" self.textBrowserProgress.append(msg) def tblStudySubjectItemChanged(self, current, previous): """Event handler which is triggered when selectedStudySubject change """ self.tvStudyEvents.setModel(None) self.tvDicomStudies.setModel(None) self.textBrowserProgress.clear() # Take the first column of selected row from table view index = self.studySubjectProxyModel.index(current.row(), 1) if index.data().toPyObject(): self._selectedStudySubject = (first.first( subject for subject in self._studySubjects if subject.label().encode( "utf-8") == index.data().toPyObject().toUtf8())) # TODO: enable this when you migrate to a new version of OC # I need to load a SubjectKey # ssREST = self._svcHttp.getStudyCasebookSubject( # self._mySite.edc.edcbaseurl, # self.getStudyOid(), # self._selectedStudySubject.label() # ) # if ssREST != None: # self._selectedStudySubject.oid = ssREST.oid # self.logger.debug("Loaded subject key: " + self._selectedStudySubject.oid) # TODO: I dont like it, get rid of it () # Propagate PatientID into service self._svcDicom.PatientID = self._selectedStudySubject.subject.uniqueIdentifier def btnDownloadClicked(self): """Upload button pressed (DICOM upload workflow started) """ QtGui.qApp.processEvents(QtCore.QEventLoop.AllEvents, 1000) #self.progressBar.setRange(0, 100) #self.progressBar.setValue(0) self._downloadDir = self.selectFolderDialog() if self._downloadDir is not None: # Disable upload button for now but better is to disable whole UI self.btnDownload.setEnabled(False) # Start download DICOM data # Create thread self._threadPool.append( WorkerThread(self._svcDicom.downloadDicomData, [ self._downloadDir, self.getStudyIdentifier(), self._svcHttp ])) # Connect finish event self._threadPool[len(self._threadPool) - 1].finished.connect( self.DicomDownloadFinishedMessage) # Connect message eventscd self.connect(self._threadPool[len(self._threadPool) - 1], QtCore.SIGNAL("message(QString)"), self.Message) self.connect(self._threadPool[len(self._threadPool) - 1], QtCore.SIGNAL("log(QString)"), self.LogMessage) # Progress self.connect(self._threadPool[len(self._threadPool) - 1], QtCore.SIGNAL("taskUpdated"), self.handleTaskUpdated) # Start thread self._threadPool[len(self._threadPool) - 1].start() def handleTaskUpdated(self, data): """Move progress bar precento by precento """ if type(data) is list: if data[0]: processed = data[0] if data[1]: size = data[1] progress = processed * 100 / size self.window().statusBar.showMessage("Processed files: [" + str(processed) + "/" + str(size) + "]") self.progressBar.setValue(progress) def handleDestroyed(self): """Kill running threads """ self.logger.debug("Destroying DICOM download module") for thread in self._threadPool: thread.terminate() thread.wait() self.logger.debug("Thread killed.") def getStudyOid(self): """Return study or site OID depending on mono/multi centre configuration """ # Multicentre if len(self._selectedStudy.sites) > 0: return self._selectedStudySite.oid # Monocentre else: return self._selectedStudy.oid() def getStudyIdentifier(self): """Return study or site OID depending on mono/multi centre configuration """ # Multicentre if len(self._selectedStudy.sites) > 0: return self._selectedStudySite.identifier # Monocentre else: return self._selectedStudy.identifier() ###### ####### ## ## ## ## ### ## ## ######## ###### ## ## ## ## ### ### ### ### ## ## ### ## ## ## ## ## ## ## ## #### #### #### #### ## ## #### ## ## ## ## ## ## ## ## ### ## ## ### ## ## ## ## ## ## ## ## ###### ## ## ## ## ## ## ## ######### ## #### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ### ## ## ## ## ###### ####### ## ## ## ## ## ## ## ## ######## ###### def prepareServices(self): """Prepare services for this module """ # HTTP connection to RadPlanBio server (Database) self._svcHttp = HttpConnectionService(ConfigDetails().rpbHost, ConfigDetails().rpbHostPort, UserDetails()) self._svcHttp.application = ConfigDetails().rpbApplication if ConfigDetails().proxyEnabled: self._svcHttp.setupProxy(ConfigDetails().proxyHost, ConfigDetails().proxyPort, ConfigDetails().noProxy) if ConfigDetails().proxyAuthEnabled: self._svcHttp.setupProxyAuth(ConfigDetails().proxyAuthLogin, ConfigDetails().proxyAuthPassword) # Read partner site of logged user self._mySite = self._svcHttp.getMyDefaultAccount().partnersite # Create connection artefact to users main OpenClinica SOAP baseUrl = self._mySite.edc.soapbaseurl self.ocConnectInfo = OCConnectInfo(baseUrl, OCUserDetails().username) self.ocConnectInfo.setPasswordHash(OCUserDetails().passwordHash) if ConfigDetails().proxyEnabled: self.ocWebServices = OCWebServices( self.ocConnectInfo, ConfigDetails().proxyHost, ConfigDetails().proxyPort, ConfigDetails().noProxy, ConfigDetails().proxyAuthLogin, ConfigDetails().proxyAuthPassword) else: self.ocWebServices = OCWebServices(self.ocConnectInfo) # ODM XML metadata processing self.fileMetaDataService = OdmFileDataService() # DICOM PROCESSING SERVICE self._svcDicom = DicomService() def reloadData(self): """Initialization of data for UI """ # OpenClinica study del self._studies[:] self._studies = [] self._selectedStudy = None self._studyMetadata = None # OpenClinica study site self._selectedStudySite = None # OpenClinica study subjects del self._studySubjects[:] self._studySubjects = [] self._selectedStudySubject = None # Load studies self.reloadStudies() def reloadStudies(self): """Reload OpenClinica studies (in working thread) """ # Setup loading UI self.window().statusBar.showMessage( "Loading list of clinical studies...") self.window().enableIndefiniteProgess() self.tabWidget.setEnabled(False) # Create data loading thread self._threadPool.append(WorkerThread( self.ocWebServices.listAllStudies)) # Connect slots self.connect(self._threadPool[len(self._threadPool) - 1], QtCore.SIGNAL("finished(QVariant)"), self.loadStudiesFinished) # Start thread self._threadPool[len(self._threadPool) - 1].start() def reloadStudyMetadata(self): """Reload study metadata for selected study """ # Setup loading UI self.window().statusBar.showMessage("Loading study metadata...") self.window().enableIndefiniteProgess() self.tabWidget.setEnabled(False) # Create data loading thread self._threadPool.append( WorkerThread(self.ocWebServices.getStudyMetadata, self._selectedStudy)) # Connect slots self.connect(self._threadPool[len(self._threadPool) - 1], QtCore.SIGNAL("finished(QVariant)"), self.loadStudyMetadataFinished) # Start thread self._threadPool[len(self._threadPool) - 1].start() def reloadStudySites(self): """Reload sites for selected OpenClinica sutdy (in memory processing) (If it is monocentric study show one default site) """ # Quick way of crating simple viewModel self.studySitesModel = QtGui.QStandardItemModel() self.studySitesModel.setHorizontalHeaderLabels( ["Identifier", "Partner Site", "Study"]) row = 0 if len(self._selectedStudy.sites) > 0: for site in self._selectedStudy.sites: siteIdentifierValue = QtGui.QStandardItem(site.identifier) siteNameValue = QtGui.QStandardItem(site.name) studyNameValue = QtGui.QStandardItem( self._selectedStudy.name()) self.studySitesModel.setItem(row, 0, siteIdentifierValue) self.studySitesModel.setItem(row, 1, siteNameValue) self.studySitesModel.setItem(row, 2, studyNameValue) row = row + 1 else: studyIdentifierValue = QtGui.QStandardItem( self._selectedStudy.identifier()) studyNameValue = QtGui.QStandardItem(self._selectedStudy.name()) self.studySitesModel.setItem(row, 0, studyIdentifierValue) self.studySitesModel.setItem(row, 2, studyNameValue) row = row + 1 # Create a proxy model to enable Sorting and filtering self.studyProxyModel = QtGui.QSortFilterProxyModel() self.studyProxyModel.setSourceModel(self.studySitesModel) self.studyProxyModel.setDynamicSortFilter(True) self.studyProxyModel.setFilterCaseSensitivity( QtCore.Qt.CaseInsensitive) # Connect to filtering UI element QtCore.QObject.connect(self.txtStudyFilter, QtCore.SIGNAL("textChanged(QString)"), self.studyProxyModel.setFilterRegExp) self.tvStudies.setModel(self.studyProxyModel) self.tvStudies.resizeColumnsToContents() # After the view has model, set currentChanged behaviour self.tvStudies.selectionModel().currentChanged.connect( self.tblStudyItemChanged) def reloadSubjects(self): """Reload OpenClinica study subects enrolled into selected study/site in working thread """ # Setup loading UI self.window().statusBar.showMessage( "Loading list of study subjects...") self.window().enableIndefiniteProgess() self.tabWidget.setEnabled(False) self._subjectsLoaded = False # Load subject for whole study or only site if it is multicentre study if self._selectedStudy and self._selectedStudy.isMulticentre: self._threadPool.append( WorkerThread( self.ocWebServices.listAllStudySubjectsByStudySite, [ self._selectedStudy, self._selectedStudySite, self._studyMetadata ])) else: self._threadPool.append( WorkerThread(self.ocWebServices.listAllStudySubjectsByStudy, [self._selectedStudy, self._studyMetadata])) # Connect slots self.connect(self._threadPool[len(self._threadPool) - 1], QtCore.SIGNAL("finished(QVariant)"), self.loadSubjectsFinished) # Start thread self._threadPool[len(self._threadPool) - 1].start() # TODO: it would be much faster if I request REST subject only for the selected one # TODO: however we have to migrate to a new version of OC first # Need to get OIDs of subjects self._threadPool.append( WorkerThread(self._svcHttp.getStudyCasebookSubjects, [self._mySite.edc.edcbaseurl, self.getStudyOid()])) # Connect slots self.connect(self._threadPool[len(self._threadPool) - 1], QtCore.SIGNAL("finished(QVariant)"), self.loadSubjectsRESTFinished) # Start thread self._threadPool[len(self._threadPool) - 1].start() def selectFolderDialog(self): """User selects a directory for DICOM study files """ try: dirPath = QtGui.QFileDialog.getExistingDirectory( None, "Please select the DICOM download destination folder") if dirPath == "": return None isReadable = os.access(str(dirPath), os.R_OK) QtGui.qApp.processEvents(QtCore.QEventLoop.AllEvents, 1000) if isReadable == False: self.Error( "The client is not allowed to read data from the selected folder!" ) self.logger.error("No read access to selected folder.") return None except UnicodeEncodeError: self.Error( "The path to the selected folder contains unsupported characters (unicode), please use only ascii characters in names of folders!" ) self.logger.error("Unsupported unicode folder path selected.") return None return str(dirPath) def loadStudiesFinished(self, studies): """Finished loading studies from server """ self._studies = studies.toPyObject() self._studies.sort(cmp=lambda x, y: cmp(x.name(), y.name())) # And prepare ViewModel for the GUI studiesModel = QtGui.QStandardItemModel() for study in self._studies: text = QtGui.QStandardItem(study.name()) studiesModel.appendRow([text]) self.cmbStudy.setModel(studiesModel) # Select the first study self._selectedStudy = self._studies[0] # Update status bar self.tabWidget.setEnabled(True) self.window().statusBar.showMessage("Ready") self.window().disableIndefiniteProgess() def loadStudyMetadataFinished(self, metadata): """Finished loading metadata from server """ self._studyMetadata = metadata.toPyObject() # Update status bar self.tabWidget.setEnabled(False) self.window().statusBar.showMessage("Ready") self.window().disableIndefiniteProgess() # Set the OC active study according to the selection (in order to make sure that RESTfull calls are executed) if self._selectedStudy and self._selectedStudy.isMulticentre: ocStudy = self._svcHttp.getOCStudyByIdentifier( self._selectedStudySite.identifier) else: ocStudy = self._svcHttp.getOCStudyByIdentifier( self._selectedStudy.identifier()) updateResult = self._svcHttp.changeUserActiveStudy( UserDetails().username, ocStudy.id) self.tabWidget.setEnabled(True) self.window().statusBar.showMessage("Ready") self.window().disableIndefiniteProgess() def loadSubjectsFinished(self, subjects): """Finished loading of subject data """ self._studySubjects = subjects.toPyObject() self._subjectsLoaded = True def loadSubjectsRESTFinished(self, subjects): """ """ # In case the REST worker finished sooner than the SOAP worker while self._subjectsLoaded != True: time.sleep(1) subjectsREST = subjects.toPyObject() # Create the ViewModels for Views # Quick way of crating simple viewModel self.subjectsModel = QtGui.QStandardItemModel() self.subjectsModel.setHorizontalHeaderLabels([ "PersonID", "StudySubjectID", "SecondaryID", "Gender", "Enrollment date", "OID" ]) row = 0 for studySubject in self._studySubjects: # Always mandatory labelItem = QtGui.QStandardItem(studySubject.label()) enrollmentDateItem = QtGui.QStandardItem( studySubject.enrollmentDate) # Optional secondaryLabelItem = QtGui.QStandardItem("") if studySubject.secondaryLabel() != None: secondaryLabelItem = QtGui.QStandardItem( studySubject.secondaryLabel()) # Not everything has to be collected (depend on study setup) pidItem = QtGui.QStandardItem("") genderItem = QtGui.QStandardItem("") if studySubject.subject != None: if studySubject.subject.uniqueIdentifier != None: pidItem = QtGui.QStandardItem( studySubject.subject.uniqueIdentifier) if studySubject.subject.gender != None: genderItem = QtGui.QStandardItem( studySubject.subject.gender) oidItem = QtGui.QStandardItem("") for sREST in subjectsREST: if sREST.studySubjectId == studySubject.label(): oidItem = QtGui.QStandardItem(sREST.oid) studySubject.oid = sREST.oid self.subjectsModel.setItem(row, 0, pidItem) self.subjectsModel.setItem(row, 1, labelItem) self.subjectsModel.setItem(row, 2, secondaryLabelItem) self.subjectsModel.setItem(row, 3, genderItem) self.subjectsModel.setItem(row, 4, enrollmentDateItem) self.subjectsModel.setItem(row, 5, oidItem) row = row + 1 # Create a proxy model to enable Sorting and filtering self.studySubjectProxyModel = QtGui.QSortFilterProxyModel() self.studySubjectProxyModel.setSourceModel(self.subjectsModel) self.studySubjectProxyModel.setDynamicSortFilter(True) self.studySubjectProxyModel.setFilterCaseSensitivity( QtCore.Qt.CaseInsensitive) # Connect to filtering UI element QtCore.QObject.connect(self.txtStudySubjectFilter, QtCore.SIGNAL("textChanged(QString)"), self.studySubjectProxyModel.setFilterRegExp) # Set the models Views self.tvStudySubjects.setModel(self.studySubjectProxyModel) # Resize the width of columns to fit the content self.tvStudySubjects.resizeColumnsToContents() # After the view has model, set currentChanged behaviour self.tvStudySubjects.selectionModel().currentChanged.connect( self.tblStudySubjectItemChanged) self.tabWidget.setEnabled(True) self.window().statusBar.showMessage("Ready") self.window().disableIndefiniteProgess() ## ## ######## ###### ###### ### ###### ######## ###### ### ### ## ## ## ## ## ## ## ## ## ## ## ## #### #### ## ## ## ## ## ## ## ## ## ### ## ###### ###### ###### ## ## ## #### ###### ###### ## ## ## ## ## ######### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ######## ###### ###### ## ## ###### ######## ###### def Question(self, string): """Question message box """ reply = QtGui.QMessageBox.question(self, "Question", string, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: self.btnDownload.setEnabled(False) else: self.btnDownload.setEnabled(True) def Error(self, string): """Error message box """ QtGui.QMessageBox.critical(self, "Error", string) self.btnDownload.setEnabled(True) def Warning(self, string): """Warning message box """ QtGui.QMessageBox.warning(self, "Warning", string) self.btnDownload.setEnabled(True) def Message(self, string): """Called from message event, opens a small window with the message """ QtGui.QMessageBox.about(self, "Info", string) self.btnDownload.setEnabled(True) def LogMessage(self, string): """Called from log event in thread and adds log into textbrowser UI """ self.textBrowserProgress.append(string) def DicomDownloadFinishedMessage(self): """ Called after uploadDataThread finished, after the data were uploaded to the RadPlanBio server """ # Enable upload button self.btnDownload.setEnabled(True) self.window().statusBar.showMessage("Ready")
def __init__(self, parent=None): """Constructor of ExportModule View """ QWidget.__init__(self, parent) self.parent = parent #----------------------------------------------------------------------- #----------------------- Logger ---------------------------------------- self.logger = logging.getLogger(__name__) logging.config.fileConfig('logging.ini', disable_existing_loggers=False) #----------------------------------------------------------------------- #---------------------- App config -------------------------------------- self.appConfig = AppConfigurationService() #----------------------------------------------------------------------- #--------------------- Create Module UI -------------------------------- self.setupUi(self) #----------------------------------------------------------------------- #---------- Load modules buttons - events definitions ------------------ self.btnReload.clicked.connect(self.btnReloadClicked) self.btnMapExportField.clicked.connect(self.btnMapExportFieldClicked) self.btnLocateDataFilename.clicked.connect( self.btnLocateDataFilenameClicked) self.btnLocateMetaDataFilename.clicked.connect( self.btnLocateMetaDataFilenameClicked) self.btnValidateMapping.clicked.connect( self.btnValidateDataTransformationClicked) self.btnLoadMapping.clicked.connect(self.btnLoadMappingClicked) self.cmbStudyEvent.currentIndexChanged['QString'].connect( self.cmbStudyEventChanged) self.cmbEventForm.currentIndexChanged['QString'].connect( self.cmbEventFormChanged) #----------------------------------------------------------------------- # Domain model self.__study = None self.__studyEvents = [] self.__selectedStudyEvent = None self.__studyEventCrfs = [] self.__selectedEventCrf = None #----------------------------------------------------------------------- #----------------------- Services -------------------------------------- # There are two http services, one for communicating with my own # site server/database section = "MySiteServer" hostname = self.appConfig.get(section)["host"] port = self.appConfig.get(section)["port"] self.svcHttp = HttpConnectionService(hostname, port, UserDetails()) self.reload() #----------------------------------------------------------------------- # TODO: make it a Stragety desing pattern later self.fileDataService = CsvFileDataService() self.fileMetaDataService = OdmFileDataService() self.exportMapping = [] # Just for testing to have guick access to files self.dataFilename = "/mnt/edv/skripcakt/ulice/branches/radplanbio/data/radiochemotherapy.csv" self.metaDataFilename = "/mnt/edv/skripcakt/ulice/branches/radplanbio/data/HNSCCStudyMetadata.xml" #----------------------------------------------------------------------- # Create REST mainzellieste userName = "******" password = "******" baseUrl = "http://g89rtpsrv.med.tu-dresden.de:8080/mainzelliste-1.1-SNAPSHOT/" apiKey = "mdatdd" connectInfo = MainzellisteConnectInfo(baseUrl, userName, password, apiKey) self.svcPseudonymisation = PseudonymisationService(connectInfo)
class ExportModule(QWidget, ExportModuleUI): """Export Module view class Export Module is class inerited from ExportModuleUI. There the GUI layout and elements are defined. In this class the handlers aka View application logic is defined. Handler are invoking functionality which is provided in bussiness layer of the application (services, domain). """ #---------------------------------------------------------------------- #--------------------------- Constructors ----------------------------- def __init__(self, parent=None): """Constructor of ExportModule View """ QWidget.__init__(self, parent) self.parent = parent #----------------------------------------------------------------------- #----------------------- Logger ---------------------------------------- self.logger = logging.getLogger(__name__) logging.config.fileConfig('logging.ini', disable_existing_loggers=False) #----------------------------------------------------------------------- #---------------------- App config -------------------------------------- self.appConfig = AppConfigurationService() #----------------------------------------------------------------------- #--------------------- Create Module UI -------------------------------- self.setupUi(self) #----------------------------------------------------------------------- #---------- Load modules buttons - events definitions ------------------ self.btnReload.clicked.connect(self.btnReloadClicked) self.btnMapExportField.clicked.connect(self.btnMapExportFieldClicked) self.btnLocateDataFilename.clicked.connect( self.btnLocateDataFilenameClicked) self.btnLocateMetaDataFilename.clicked.connect( self.btnLocateMetaDataFilenameClicked) self.btnValidateMapping.clicked.connect( self.btnValidateDataTransformationClicked) self.btnLoadMapping.clicked.connect(self.btnLoadMappingClicked) self.cmbStudyEvent.currentIndexChanged['QString'].connect( self.cmbStudyEventChanged) self.cmbEventForm.currentIndexChanged['QString'].connect( self.cmbEventFormChanged) #----------------------------------------------------------------------- # Domain model self.__study = None self.__studyEvents = [] self.__selectedStudyEvent = None self.__studyEventCrfs = [] self.__selectedEventCrf = None #----------------------------------------------------------------------- #----------------------- Services -------------------------------------- # There are two http services, one for communicating with my own # site server/database section = "MySiteServer" hostname = self.appConfig.get(section)["host"] port = self.appConfig.get(section)["port"] self.svcHttp = HttpConnectionService(hostname, port, UserDetails()) self.reload() #----------------------------------------------------------------------- # TODO: make it a Stragety desing pattern later self.fileDataService = CsvFileDataService() self.fileMetaDataService = OdmFileDataService() self.exportMapping = [] # Just for testing to have guick access to files self.dataFilename = "/mnt/edv/skripcakt/ulice/branches/radplanbio/data/radiochemotherapy.csv" self.metaDataFilename = "/mnt/edv/skripcakt/ulice/branches/radplanbio/data/HNSCCStudyMetadata.xml" #----------------------------------------------------------------------- # Create REST mainzellieste userName = "******" password = "******" baseUrl = "http://g89rtpsrv.med.tu-dresden.de:8080/mainzelliste-1.1-SNAPSHOT/" apiKey = "mdatdd" connectInfo = MainzellisteConnectInfo(baseUrl, userName, password, apiKey) self.svcPseudonymisation = PseudonymisationService(connectInfo) # # Retrive all pids # allPids = self.svcPseudonymisation.getAllPatients() # # Start reading session (I know the PID and I want to gat patient data) # sessionId, uri1 = self.svcPseudonymisation.newSession() # tokenId, url2 = self.svcPseudonymisation.readPatientToken(sessionId, allPids[0].idString) # self.svcPseudonymisation.getPatient(tokenId) #tokenId, uri2 = self.svcPseudonymisation.newPatientToken(sessionId) #self.svcPseudonymisation.createPatientJson(tokenId) #self.svcPseudonymisation.resolveTempIds(tokenId) def loadModuleData(self, selectedIndex): if selectedIndex == 0: pass def reload(self): requests = [] # TODO: read from users data # It is not Heidelberg I have it here just for testing to simulate on one database mySite = "DKTK-HEIDELBERG" site = self.svcHttp.getPartnerSiteByName(mySite) if site is not None: requests = self.svcHttp.getAllPullDataRequestsToSite(site.sitename) pullDataRequestModel = QtGui.QStandardItemModel() pullDataRequestModel.setHorizontalHeaderLabels( ["Request sent from", "Subject", "Created"]) row = 0 for itm in requests: fromValue = QtGui.QStandardItem(itm.sentFromSite.sitename) subjectValue = QtGui.QStandardItem(itm.subject) createdValue = QtGui.QStandardItem(str(itm.created)) pullDataRequestModel.setItem(row, 0, fromValue) pullDataRequestModel.setItem(row, 1, subjectValue) pullDataRequestModel.setItem(row, 2, createdValue) row = row + 1 self.tvPullDataRequests.setModel(pullDataRequestModel) self.tvPullDataRequests.resizeColumnsToContents() #---------------------------------------------------------------------- #------------------- Browse Buttons Handlers ------------------------- def btnLocateDataFilenameClicked(self): """User selects a file to export """ # Get a data file name fileName = QtGui.QFileDialog.getOpenFileName(self, "Locate data file", QtCore.QDir.currentPath(), "csv files(*.csv)") # Check read access to the file ok = os.access(fileName, os.R_OK) # If access is OK if ok: self.dataFilename = str(fileName) self.txtDataFilename.setText(self.dataFilename) def btnLocateMetaDataFilenameClicked(self): """Open a file open dialog where user selects a study metadata XML file """ # Get a metadata file name fileName = QtGui.QFileDialog.getOpenFileName(self, "Locate metadata file", QtCore.QDir.currentPath(), "xml files(*.xml)") # Check read access to the file ok = os.access(fileName, os.R_OK) # If access is OK if ok: # Get file path as string self.metaDataFilename = str(fileName) # Set the path to GUI self.txtMetaDataFilename.setText(self.metaDataFilename) # Prepare service for reading XML ODM files self.fileMetaDataService.setFilename(self.metaDataFilename) # Load the Study domain object self.__study = self.fileMetaDataService.loadStudy() # And inform the GUI self.lblStudy.setText(self.__study.name()) # Load list of Study Event Definition domain objects self.__studyEvents = self.fileMetaDataService.loadStudyEvents() # And prepare ViewModel for the GUI seModel = QtGui.QStandardItemModel() for studyEventDef in self.__studyEvents: text = QtGui.QStandardItem(studyEventDef.name()) seModel.appendRow([text]) self.cmbStudyEvent.setModel(seModel) #---------------------------------------------------------------------- #------------------- Command Buttons Handlers ------------------------- def btnReloadClicked(self): self.reload() def btnLoadMappingClicked(self): """Load mapping into table view """ if self.dataFilename == "": if self.txtDataFileName.text() == "": # error pass else: ok = os.access(self.txtDataFilename.text(), os.R_OK) if ok: self.dataFilename = self.txtDataFilename.text() else: #error pass if self.metaDataFilename == "": if self.txtMetaDataFilename.text() == "": # error pass else: ok = os.access(self.txtMetaDataFilename.text(), os.R_OK) if ok: self.metaDataFilename = self.txtMetaDataFilename.text() else: #error pass # Accumulate data headers self.fileDataService.setFilename(self.dataFilename) self.fileDataService.loadHeaders() # Accumulate metadata headers self.fileMetaDataService.setFilename(self.metaDataFilename) self.fileMetaDataService.loadHeaders() # Create a exportMaping list for table ViewModel self.exportMapping = self.fileMetaDataService.loadExportMapping( self.__selectedEventCrf) # Create a tableView model self.exportMappingModel = ExportMappingTableModel(self.exportMapping) self.tvExportDataMapping.setModel(self.exportMappingModel) # Make tableView editable self.tvExportDataMapping.setEditTriggers( QtGui.QAbstractItemView.CurrentChanged) # Create combo box delegate to display in teableView #dataComboBoxDelegate = ComboBoxDelegate(self, self.fileDataService.headers) #actionButtonDelegate = PushButtonDelegate(self) # Assign delegates to columns #self.tvExportDataMapping.setItemDelegateForColumn(2, dataComboBoxDelegate) #self.tvExportDataMapping.setItemDelegateForColumn(4, actionButtonDelegate) # Make cobobox always displayed #for i in range(0, len(self.fileMetaDataService.headers)): #self.tvExportDataMapping.openPersistentEditor(self.exportMappingModel.index(i, 2)); #self.tvExportDataMapping.openPersistentEditor(self.exportMappingModel.index(i, 4)); # Resize table columns to content self.tvExportDataMapping.resizeColumnsToContents() self.tvExportDataMapping.selectionModel().currentChanged.connect( self.tblExportDataMappingItemChanged) def btnMapExportFieldClicked(self): """Map metadata to datafiled from provided CSV data """ # Initialize dialog dialog = ExportFieldMappingDialog(self) dialog.dataService = self.fileDataService dialog.setData(self.selectedDataMapping, self.fileDataService.headers) # Show dialog dialogResult = dialog.exec_() if dialogResult == QtGui.QDialog.Accepted: # Take data from dialog self.exportMappingModel.dataItems[ self.selectedRow].data = dialog.dataMapping.data self.exportMappingModel.dataItems[ self.selectedRow].setConverter = dialog.dataMapping.converter # Resize table columns to content self.tvExportDataMapping.resizeColumnsToContents() def btnMapExportFieldConstantClicked(self): pass def btnValidateMappingClicked(self): """ """ mappingErrors = [] for i in range(0, len(self.fileMetaDataService.headers)): print self.exportMappingModel.dataItems[ i].metadata + " -> " + self.exportMappingModel.dataItems[i].data # Error: All mandatory columns are mapped # Warning: one column is mapped to several metadata def btnValidateDataTransformationClicked(self): """ """ dataSize = self.fileDataService.size() validationErrors = [] # Try to convert all data (from 1 ignore the header) for i in range(1, dataSize): # For every row of input data set dataRow = self.fileDataService.getRow(i) # Go through specified export mapping for j in range(0, len(self.fileMetaDataService.headers)): # And locate data in csv which corresponds to mapping metadataHeader = self.exportMappingModel.dataItems[j].metadata dataHeader = self.exportMappingModel.dataItems[j].data dataIndex = self.fileDataService.headers.index(dataHeader) dataValue = dataRow[dataIndex] print "metadata: " + metadataHeader + " -> " + dataHeader + " = " + dataValue # Check if the data conform #self.exportMappingModel.dataItems[j].checkDataType(dataValue) if (self.exportMappingModel.dataItems[j].validate(dataValue) == False): errorMessage = "Input file row: " + i + ". " + dataHeader + " = " + dataValue + "does not conform metadata codelist for: " + metadataHeader validationErrors.append(errorMessage) # Show errors if appiers # Show confirmation of valid in no errors #---------------------------------------------------------------------- #------------------- ComboBox Handlers -------------------------------- def cmbStudyEventChanged(self, text): """ """ # I need to find StudyEvent object according to name (text) self.__selectedStudyEvent = first.first(event for event in self.__studyEvents if event.name() == text) # When selected event found search CRFs for the event if self.__selectedStudyEvent is not None: # Load list of Study Event Form Definition domain objects self.__studyEventCrfs = self.fileMetaDataService.loadEventCrfs( self.__selectedStudyEvent) # And prepare ViewModel for the GUI sefModel = QtGui.QStandardItemModel() for studyEventCrf in self.__studyEventCrfs: text = QtGui.QStandardItem(studyEventCrf.name()) sefModel.appendRow([text]) self.cmbEventForm.setModel(sefModel) def cmbEventFormChanged(self, text): """ """ self.__selectedEventCrf = first.first(crf for crf in self.__studyEventCrfs if crf.name() == text) #---------------------------------------------------------------------- #------------------- TableView Handlers ------------------------------- def tblExportDataMappingItemChanged(self, current, previous): self.selectedDataMapping = self.exportMappingModel.dataItems[ current.row()] self.selectedRow = current.row()
def __init__(self, parent=None): """Constructor of main application widnow """ QMainWindow.__init__(self, parent) self._logger = logging.getLogger(__name__) logging.config.fileConfig("logging.ini", disable_existing_loggers=False) self.setupUi(self) self.statusBar.showMessage("Ready") self._logger.info("RadPlanBio host: " + ConfigDetails().rpbHost + ":" + str(ConfigDetails().rpbHostPort)) self._logger.info("Partner site proxy: " + ConfigDetails().proxyHost + ":" + str(ConfigDetails().proxyPort) + " [" + str(ConfigDetails().proxyEnabled) + "]") self.svcHttp = HttpConnectionService(ConfigDetails().rpbHost, ConfigDetails().rpbHostPort, UserDetails()) self.svcHttp.application = ConfigDetails().rpbApplication if ConfigDetails().proxyEnabled: self.svcHttp.setupProxy(ConfigDetails().proxyHost, ConfigDetails().proxyPort, ConfigDetails().noProxy) if ConfigDetails().proxyAuthEnabled: self.svcHttp.setupProxyAuth(ConfigDetails().proxyAuthLogin, ConfigDetails().proxyAuthPassword) self.lblRPBConnection.setText("[" + UserDetails().username + "]@" + ConfigDetails().rpbHost + "/" + ConfigDetails().rpbApplication + ":" + str(ConfigDetails().rpbHostPort)) try: defaultAccount = self.svcHttp.getMyDefaultAccount() except Exception: self._logger.info("HTTP communication failed.") if defaultAccount.ocusername and defaultAccount.ocusername != "": ocUsername = defaultAccount.ocusername ocPasswordHash = self.svcHttp.getOCAccountPasswordHash() ocSoapBaseUrl = defaultAccount.partnersite.edc.soapbaseurl successful = False try: # Create connection artifact to OC self.ocConnectInfo = OCConnectInfo(ocSoapBaseUrl, ocUsername) self.ocConnectInfo.setPasswordHash(ocPasswordHash) if ConfigDetails().proxyEnabled: self.ocWebServices = OCWebServices( self.ocConnectInfo, ConfigDetails().proxyHost, ConfigDetails().proxyPort, ConfigDetails().noProxy, ConfigDetails().proxyAuthLogin, ConfigDetails().proxyAuthPassword) else: self.ocWebServices = OCWebServices(self.ocConnectInfo) successful, studies = self.ocWebServices.listAllStudies() except: self._logger.info("HTTP OC communication failed.", exc_info=True) if successful: ocUserDetails = OCUserDetails() ocUserDetails.username = ocUsername ocUserDetails.passwordHash = ocPasswordHash ocUserDetails.connected = True else: QtGui.QMessageBox.warning(self, "Error", "Wrong username or password!")
class MainWindow(QMainWindow, MainWindowUI): """Main window view shell Main view shell where the other modules views are registered """ def __init__(self, parent=None): """Constructor of main application widnow """ QMainWindow.__init__(self, parent) self._logger = logging.getLogger(__name__) logging.config.fileConfig("logging.ini", disable_existing_loggers=False) self.setupUi(self) self.statusBar.showMessage("Ready") self._logger.info("RadPlanBio host: " + ConfigDetails().rpbHost + ":" + str(ConfigDetails().rpbHostPort)) self._logger.info("Partner site proxy: " + ConfigDetails().proxyHost + ":" + str(ConfigDetails().proxyPort) + " [" + str(ConfigDetails().proxyEnabled) + "]") self.svcHttp = HttpConnectionService(ConfigDetails().rpbHost, ConfigDetails().rpbHostPort, UserDetails()) self.svcHttp.application = ConfigDetails().rpbApplication if ConfigDetails().proxyEnabled: self.svcHttp.setupProxy(ConfigDetails().proxyHost, ConfigDetails().proxyPort, ConfigDetails().noProxy) if ConfigDetails().proxyAuthEnabled: self.svcHttp.setupProxyAuth(ConfigDetails().proxyAuthLogin, ConfigDetails().proxyAuthPassword) self.lblRPBConnection.setText("[" + UserDetails().username + "]@" + ConfigDetails().rpbHost + "/" + ConfigDetails().rpbApplication + ":" + str(ConfigDetails().rpbHostPort)) try: defaultAccount = self.svcHttp.getMyDefaultAccount() except Exception: self._logger.info("HTTP communication failed.") if defaultAccount.ocusername and defaultAccount.ocusername != "": ocUsername = defaultAccount.ocusername ocPasswordHash = self.svcHttp.getOCAccountPasswordHash() ocSoapBaseUrl = defaultAccount.partnersite.edc.soapbaseurl successful = False try: # Create connection artifact to OC self.ocConnectInfo = OCConnectInfo(ocSoapBaseUrl, ocUsername) self.ocConnectInfo.setPasswordHash(ocPasswordHash) if ConfigDetails().proxyEnabled: self.ocWebServices = OCWebServices( self.ocConnectInfo, ConfigDetails().proxyHost, ConfigDetails().proxyPort, ConfigDetails().noProxy, ConfigDetails().proxyAuthLogin, ConfigDetails().proxyAuthPassword) else: self.ocWebServices = OCWebServices(self.ocConnectInfo) successful, studies = self.ocWebServices.listAllStudies() except: self._logger.info("HTTP OC communication failed.", exc_info=True) if successful: ocUserDetails = OCUserDetails() ocUserDetails.username = ocUsername ocUserDetails.passwordHash = ocPasswordHash ocUserDetails.connected = True else: QtGui.QMessageBox.warning(self, "Error", "Wrong username or password!") def closeEvent(self, event): """Cleaning up """ self._logger.debug("Destroying the application.") ApplicationEntityService().quit() def quit(self): """Quit (exit) event handler """ reply = QtGui.QMessageBox.question(self, "Question", gui.messages.QUE_EXIT, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: self._logger.debug("Destroying the application.") ApplicationEntityService().quit() QtGui.qApp.quit() ## ## ######## ######## ## ## ####### ######## ###### ### ### ## ## ## ## ## ## ## ## ## ## #### #### ## ## ## ## ## ## ## ## ## ## ### ## ###### ## ######### ## ## ## ## ###### ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## ######## ## ## ## ####### ######## ###### def connectToOpenClinica(self): """Connection to OpenClinica SOAP web services """ if not OCUserDetails().connected: QtGui.QMessageBox.warning( self, "Error", "Cannot connect to RadPlanBio - OpenClinica SOAP services!") else: return True
def startup(): """Start the client/upgrade """ logger = logging.getLogger(__name__) logging.config.fileConfig("logging.ini", disable_existing_loggers=False) # Apply app configuration according the config file configure() # Internationalisation # translate() # Log the version of client (useful for remote debuging) logger.info("RPB desktop client version: " + ConfigDetails().version) logger.info("Qt version: " + QT_VERSION_STR) logger.info("PyQt version: " + PYQT_VERSION_STR) # Basic services svcDiagnostic = DiagnosticService() svcDiagnostic.ProxyDiagnostic() svcHttp = HttpConnectionService(ConfigDetails().rpbHost, ConfigDetails().rpbHostPort, UserDetails()) svcHttp.application = ConfigDetails().rpbApplication if ConfigDetails().proxyEnabled: svcHttp.setupProxy(ConfigDetails().proxyHost, ConfigDetails().proxyPort, ConfigDetails().noProxy) if ConfigDetails().proxyAuthEnabled: svcHttp.setupProxyAuth(ConfigDetails().proxyAuthLogin, ConfigDetails().proxyAuthPassword) # App log app = QtGui.QApplication(sys.argv) ConfigDetails().logFilePath = (str( QtCore.QDir.currentPath())) + os.sep + "client.log" # Startup if ConfigDetails().isUpgrading is None or ConfigDetails( ).isUpgrading == "False": # Check whether upgrade was done showNotify = False if ConfigDetails().upgradeFinished is not None and ConfigDetails( ).upgradeFinished == "True": # Start upgrade procedure svcUpgrade = UpgradeService() svcUpgrade.cleanup() msg = "RadPlanBio client has been successfully upgraded" showNotify = True # Continue with standard login dialog loginDialog = LoginDialog(svcHttp) if loginDialog.exec_() == QtGui.QDialog.Accepted: # Main application window ui = MainWindow() ui.show() # Upgrade completed notification if showNotify: reply = QtGui.QMessageBox.information(ui, "Upgrade completed", msg, QtGui.QMessageBox.Ok) if reply == QtGui.QMessageBox.Ok: showNotify = False # Automatic update check at startup if (ConfigDetails().startupUpdateCheck): # Load version from server, user details updated in login dialog latestSoftware = svcHttp.getLatestSoftware( ConfigDetails().identifier) if latestSoftware != None: latestVersion = str(latestSoftware.version) else: latestVersion = ConfigDetails().version cmp = lambda x, y: LooseVersion(x).__cmp__(y) canUpgrade = cmp(ConfigDetails().version, latestVersion) if canUpgrade < 0: ui.upgradePopup() currentExitCode = app.exec_() return currentExitCode else: ApplicationEntityService().quit() QtGui.qApp.quit() else: # Start updater (RadPlanBio-update.exe) if platform.system() == "Windows": if os.path.isfile("./update/RadPlanBio-update.exe"): QtCore.QProcess.startDetached("./update/RadPlanBio-update.exe") else: QtCore.QProcess.startDetached("python ./update/mainUpdate.py") elif platform.system() == "Linux": if os.path.isfile("./update/RadPlanBio-update"): QtCore.QProcess.startDetached("./update/RadPlanBio-update") else: QtCore.QProcess.startDetached("python ./update/mainUpdate.py") else: QtCore.QProcess.startDetached("python ./update/mainUpdate.py") # Close this one ApplicationEntityService().quit() QtGui.qApp.quit()