def __init__(self, parent=None): """Default Constructor """ QtGui.QDialog.__init__(self, parent) self.setWindowTitle("About") size = 100 lblIcon = QtGui.QLabel() appIconPath = ":/images/rpb-icon.jpg" myPixmap = QtGui.QPixmap(appIconPath) myScaledPixmap = myPixmap.scaled(size, size, Qt.KeepAspectRatio) lblIcon.setPixmap(myScaledPixmap) copyright = u"\u00A9" # Dialog layout root rootLayout = QtGui.QVBoxLayout(self) # About Text lblAppName = QtGui.QLabel(ConfigDetails().name) lblAppVersion = QtGui.QLabel("version: " + ConfigDetails().version) lblPlatform = QtGui.QLabel("system: " + platform.system()) lblLogFile = QtGui.QLabel("log: " + ConfigDetails().logFilePath) lblEmptyLine = QtGui.QLabel("") lblAppCopyright = QtGui.QLabel(copyright + ConfigDetails().copyright) # Layouting rootLayout.addWidget(lblIcon) rootLayout.addWidget(lblAppName) rootLayout.addWidget(lblAppVersion) rootLayout.addWidget(lblPlatform) rootLayout.addWidget(lblLogFile) rootLayout.addWidget(lblEmptyLine) rootLayout.addWidget(lblAppCopyright)
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() # Log the version of client (useful for remote debuging) logger.info(ConfigDetails().name + " version: " + ConfigDetails().version) # Basic services svcDiagnostic = DiagnosticService() svcDiagnostic.ProxyDiagnostic() # App log app = QtGui.QApplication(sys.argv) ConfigDetails().logFilePath = (str(QtCore.QDir.currentPath())) + os.sep + "client.log" # Continue with standard login dialog loginDialog = LoginDialog() if loginDialog.exec_() == QtGui.QDialog.Accepted: # Main application window ui = MainWindow() ui.show() currentExitCode = app.exec_() return currentExitCode
def __init__(self, parent=None): """Default Constructor """ QtGui.QDialog.__init__(self, parent) self.setWindowTitle("About") size = 100 lblIcon = QtGui.QLabel() appIconPath = ":/images/rpb-icon.jpg" myPixmap = QtGui.QPixmap(appIconPath) myScaledPixmap = myPixmap.scaled(size, size, Qt.KeepAspectRatio) lblIcon.setPixmap(myScaledPixmap) copyright = u"\u00A9" self.svcCrypto = CryptoService() # Dialog layout root rootLayout = QtGui.QVBoxLayout(self) # About Text lblAppName = QtGui.QLabel(ConfigDetails().name) lblAppVersion = QtGui.QLabel("version: " + ConfigDetails().version) lblPlatform = QtGui.QLabel("system: " + platform.system()) lblLogFile = QtGui.QLabel("log: " + ConfigDetails().logFilePath) lblEmptyLine = QtGui.QLabel("") lblAppCopyright = QtGui.QLabel(copyright + ConfigDetails().copyright) # Show encrytpion key # 32 bytes long key for AES-256 self.btnKey = QtGui.QPushButton("Encrytpion Key") if self.svcCrypto.keyExists(): self.btnKey.setToolTip( "Show base64 encoded 32 bytes long key used for AES-256 encryption" ) self.btnKey.setDisabled(False) else: self.btnKey.setToolTip("Encryption key does not exist") self.btnKey.setDisabled(True) self.btnKey.clicked.connect(self.btnKeyClicked) # Show Licence self.btnLicense = QtGui.QPushButton("License") self.btnLicense.setToolTip("Show software license") self.btnLicense.clicked.connect(self.btnLicenseClicked) # Layouting rootLayout.addWidget(lblIcon) rootLayout.addWidget(lblAppName) rootLayout.addWidget(lblAppVersion) rootLayout.addWidget(lblPlatform) rootLayout.addWidget(lblLogFile) rootLayout.addWidget(self.btnKey) rootLayout.addWidget(self.btnLicense) rootLayout.addWidget(lblEmptyLine) rootLayout.addWidget(lblAppCopyright)
def getPatient(self): # Augment with Patient PID self.dicomData.patient.newId = self.PatientID if ConfigDetails().replacePatientNameWith == "pid": self.dicomData.patient.newName = self.PatientID elif ConfigDetails().replacePatientNameWith == "const": self.dicomData.patient.newName = ConfigDetails().constPatientName return self.dicomData.patient
def reloadEvents(self): """Reload OpenClinica events scheduled for selected study subject """ # Setup loading UI self.window().statusBar.showMessage("Loading subject scheduled events...") self.window().enableIndefiniteProgess() self.tabWidget.setEnabled(False) # Define a job # Need to get EventRepeatKeys self._threadPool.append( WorkerThread( self.svcHttp.getStudyCasebookEvents, [ConfigDetails().ocHost, self.getStudyOid(), self._selectedStudySubject.oid] ) ) # Connect slots self.connect( self._threadPool[len(self._threadPool) - 1], QtCore.SIGNAL("finished(QVariant)"), self.loadEventsFinished ) # Start thread self._threadPool[len(self._threadPool) - 1].start()
def reloadSubjects(self): """Reload OpenClinica study subects enrolled into selected study/site in working thread """ # Clear study subjects del self._studySubjects[:] self._studySubjects = [] self._selectedStudySubject = None self._studySubjects = None self._subjectsREST = None # Setup loading UI self.window().statusBar.showMessage("Loading list of study subjects...") self.window().enableIndefiniteProgess() self.tabWidget.setEnabled(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, [ConfigDetails().ocHost, 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 retranslateUi(self, MainWindow): """ """ MainWindow.setWindowTitle( QtGui.QApplication.translate("MainWindow", ConfigDetails().name, None, QtGui.QApplication.UnicodeUTF8)) self.btnLoadOcModule.setText( QtGui.QApplication.translate("MainWindow", "Load OpenClinica module", None, QtGui.QApplication.UnicodeUTF8))
def setupUi(self, MainWindow): """ Prepare complete GUI for application main window """ # Main window size MainWindow.setObjectName(_fromUtf8("MainWindow")) MainWindow.resize(ConfigDetails().width, ConfigDetails().height) appIconPath = ":/images/rpb-icon.jpg" appIcon = QtGui.QIcon() appIcon.addPixmap(QtGui.QPixmap(appIconPath)) MainWindow.setWindowIcon(appIcon) # Central widget is main window in this case self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) # Prepare menu bar UI self._setupMenuBar(MainWindow) # Root layout manager for main window is stack panel rootLayout = QtGui.QVBoxLayout(self.centralwidget) # Prepare tool bar UI rootLayout.addLayout(self._setupToolBar()) # Prepare main tab for modules UI rootLayout.addWidget(self._setupModulesTab()) self._setupWelcomeModule() self._setupStatusBar(MainWindow) # Put defined central widget into ManWindow central widget self.retranslateUi(MainWindow) MainWindow.setCentralWidget(self.centralwidget) QtCore.QMetaObject.connectSlotsByName(MainWindow) # Event handlers QtCore.QObject.connect(self, QtCore.SIGNAL("RESTARTREQUIRED"), self.restart)
def patient(self): """DICOM Patient Getter """ if self._patient is None: patientIdList = self.unique("PatientID") if len(patientIdList) == 1: self._patient = DicomPatient() self._patient.id = patientIdList[0] if len(self.unique("PatientName")) == 1: self._patient.name = self.unique("PatientName")[0] if len(self.unique("PatientSex")) == 1: self._patient.gender = self.unique("PatientSex")[0] if len(self.unique("PatientBirthDate")) == 1: self._patient.dob = self.unique("PatientBirthDate")[0] self._patient.newName = self._deidentConfig.ReplacePatientNameWith if ConfigDetails().retainPatientCharacteristicsOption: self._patient.newGender = self._patient.gender else: self._patient.newGender = self._deidentConfig.ReplaceDefaultWith self._patient.newDob = self._deidentConfig.ReplaceDateWith elif len(patientIdList) > 1: # More patients use the first which is detected self._patient = DicomPatient() self._patient.id = patientIdList[0] self._patient.name = self.unique("PatientName")[0] self._patient.gender = self.unique("PatientSex")[0] self._patient.dob = self.unique("PatientBirthDate")[0] self._patient.newName = self._deidentConfig.ReplacePatientNameWith if ConfigDetails().retainPatientCharacteristicsOption: self._patient.newGender = self._patient.gender else: self._patient.newGender = self._deidentConfig.ReplaceDefaultWith self._patient.newDob = self._deidentConfig.ReplaceDateWith return self._patient
def handleLogin(self): """Send authenticate user message to site server """ username = str(self.txtUsername.text()) password = str(self.txtPassword.text()) passwordHash = hashlib.sha1(password).hexdigest() # Create connection artefact to users main OpenClinica SOAP ocConnectInfo = OCConnectInfo(ConfigDetails().ocWsHost, username) ocConnectInfo.setPasswordHash(passwordHash) if ConfigDetails().proxyEnabled: ocWebServices = OCWebServices(ocConnectInfo, ConfigDetails().proxyHost, ConfigDetails().proxyPort, ConfigDetails().noProxy, ConfigDetails().proxyAuthLogin, ConfigDetails().proxyAuthPassword) else: ocWebServices = OCWebServices(ocConnectInfo) successfull = False try: successfull, studies = ocWebServices.listAllStudies() except: QtGui.QMessageBox.warning( self, "Error", "Cannot communicate with the server, no network connection or the server is not running." ) if (successfull): OCUserDetails().username = username OCUserDetails().passwordHash = passwordHash OCUserDetails().connected = True UserDetails().username = username UserDetails().clearpass = password UserDetails().password = passwordHash self.accept() else: QtGui.QMessageBox.warning(self, 'Error', 'Wrong username or password.')
def __init__(self, parent = None): """Default Constructor """ # Setup GUI QWidget.__init__(self, parent) self.parent = parent self.setupUi(self) # Hide summary of XML for importing data to OC (for user not necessary) self.lblSummary.hide() # 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 = [] # Prepares services and main data for this ViewModel self.prepareServices() # Initialize data structures for UI self._studies = [] self._studySubjects = [] self.reloadData() # Finish UI setup self.lblOcConnection.setText("[" + OCUserDetails().username + "] " + ConfigDetails().ocHost) # Register handlers self.btnReloadStudySubjects.clicked.connect(self.btnReloadStudySubjectsClicked) self.btnNewStudySubject.clicked.connect(self.btnNewStudySubjectClicked) self.btnNewEvent.clicked.connect(self.btnNewEventClicked) self.btnUpload.clicked.connect(self.btnImportClicked) self.cmbStudy.currentIndexChanged["QString"].connect(self.cmbStudyChanged) self.destroyed.connect(self.handleDestroyed)
def configure(): """Read configuration settings from config file """ appConfig = AppConfigurationService(ConfigDetails().configFileName) section = "OpenClinica" if appConfig.hasSection(section): option = "host" if appConfig.hasOption(section, option): ConfigDetails().ocHost = appConfig.get(section)[option] option = "port" if appConfig.hasOption(section, option): ConfigDetails().ocPort = appConfig.get(section)[option] section = "OpenClinica-ws" if appConfig.hasSection(section): option = "host" if appConfig.hasOption(section, option): ConfigDetails().ocWsHost = appConfig.get(section)[option] option = "port" if appConfig.hasOption(section, option): ConfigDetails().ocWsPort = appConfig.get(section)[option] section = "Proxy" if appConfig.hasSection(section): option = "enabled" if appConfig.hasOption(section, option): ConfigDetails().proxyEnabled = appConfig.getboolean(section, option) option = "host" if appConfig.hasOption(section, option): ConfigDetails().proxyHost = appConfig.get(section)[option] option = "port" if appConfig.hasOption(section, option): ConfigDetails().proxyPort = appConfig.get(section)[option] option = "noproxy" if appConfig.hasOption(section, option): ConfigDetails().noProxy = appConfig.get(section)[option] section = "Proxy-auth" if appConfig.hasSection(section): option = "enabled" if appConfig.hasOption(section, option): ConfigDetails().proxyAuthEnabled = appConfig.getboolean(section, option) option = "login" if appConfig.hasOption(section, option): ConfigDetails().proxyAuthLogin = appConfig.get(section)[option] option = "password" if appConfig.hasOption(section, option): ConfigDetails().proxyAuthPassword = appConfig.get(section)[option] section = "GUI" if appConfig.hasSection(section): option = "main.width" if appConfig.hasOption(section, option): ConfigDetails().width = int(appConfig.get(section)[option]) option = "main.height" if appConfig.hasOption(section, option): ConfigDetails().height = int(appConfig.get(section)[option])
def configure(): """Read configuration settings from config file """ appConfig = AppConfigurationService(ConfigDetails().configFileName) section = "RadPlanBioServer" if appConfig.hasSection(section): if appConfig.hasOption(section, "host"): ConfigDetails().rpbHost = appConfig.get(section)["host"] if appConfig.hasOption(section, "port"): ConfigDetails().rpbHostPort = appConfig.get(section)["port"] if appConfig.hasOption(section, "application"): ConfigDetails().rpbApplication = appConfig.get( section)["application"] section = "Proxy" if appConfig.hasSection(section): if appConfig.hasOption(section, "enabled"): ConfigDetails().proxyEnabled = appConfig.getboolean( section, "enabled") if appConfig.hasOption(section, "host"): ConfigDetails().proxyHost = appConfig.get(section)["host"] if appConfig.hasOption(section, "port"): ConfigDetails().proxyPort = appConfig.get(section)["port"] if appConfig.hasOption(section, "noproxy"): ConfigDetails().noProxy = appConfig.get(section)["noproxy"] section = "Proxy-auth" if appConfig.hasSection(section): if appConfig.hasOption(section, "enabled"): ConfigDetails().proxyAuthEnabled = appConfig.getboolean( section, "enabled") if appConfig.hasOption(section, "login"): ConfigDetails().proxyAuthLogin = appConfig.get(section)["login"] if appConfig.hasOption(section, "password"): ConfigDetails().proxyAuthPassword = appConfig.get( section)["password"] section = "GUI" if appConfig.hasSection(section): if appConfig.hasOption(section, "main.width"): ConfigDetails().width = int(appConfig.get(section)["main.width"]) if appConfig.hasOption(section, "main.height"): ConfigDetails().height = int(appConfig.get(section)["main.height"]) section = "DICOM" if appConfig.hasSection(section): if appConfig.hasOption(section, "replacepatientnamewith"): ConfigDetails().replacePatientNameWith = appConfig.get( section)["replacepatientnamewith"] if appConfig.hasOption(section, "constpatientname"): ConfigDetails().constPatientName = appConfig.get( section)["constpatientname"] if appConfig.hasOption(section, "allowmultiplepatientids"): ConfigDetails().allowMultiplePatientIDs = appConfig.getboolean( section, "allowmultiplepatientids") if appConfig.hasOption(section, "retainpatientcharacteristicsoption"): ConfigDetails( ).retainPatientCharacteristicsOption = appConfig.getboolean( section, "retainpatientcharacteristicsoption") if appConfig.hasOption(section, "retainstudydate"): ConfigDetails().retainStudyDate = appConfig.getboolean( section, "retainstudydate") if appConfig.hasOption(section, "retainstudytime"): ConfigDetails().retainStudyTime = appConfig.getboolean( section, "retainstudytime") if appConfig.hasOption(section, "retainseriesdate"): ConfigDetails().retainSeriesDate = appConfig.getboolean( section, "retainseriesdate") if appConfig.hasOption(section, "retainseriestime"): ConfigDetails().retainSeriesTime = appConfig.getboolean( section, "retainseriestime") if appConfig.hasOption(section, "retainstudyseriesdescriptions"): ConfigDetails( ).retainStudySeriesDescriptions = appConfig.getboolean( section, "retainstudyseriesdescriptions") if appConfig.hasOption(section, "autortstructmatch"): ConfigDetails().autoRTStructMatch = appConfig.getboolean( section, "autortstructmatch") if appConfig.hasOption(section, "autortstructref"): ConfigDetails().autoRTStructRef = appConfig.getboolean( section, "autortstructref") if appConfig.hasOption(section, "downloaddicompatientfoldername"): ConfigDetails().downloadDicomPatientFolderName = appConfig.get( section)["downloaddicompatientfoldername"] if appConfig.hasOption(section, "downloaddicomstudyfoldername"): ConfigDetails().downloadDicomStudyFolderName = appConfig.get( section)["downloaddicomstudyfoldername"] section = "AE" if appConfig.hasSection(section): if appConfig.hasOption(section, "name"): ConfigDetails().rpbAE = appConfig.get(section)["name"] if appConfig.hasOption(section, "port"): ConfigDetails().rpbAEport = int(appConfig.get(section)["port"]) if appConfig.hasOption(section, "aetsuffix"): ConfigDetails().rpbAETsuffix = appConfig.get(section)["aetsuffix"] if not ApplicationEntityService().isReady: if ConfigDetails( ).rpbAE is not None and ConfigDetails().rpbAE != "": # Consider AET suffix option when creating AE for client AET = ConfigDetails().rpbAE if ConfigDetails().rpbAETsuffix == "host": AET += str(QtNetwork.QHostInfo.localHostName()) elif ConfigDetails().rpbAETsuffix == "fqdn": AET += str( QtNetwork.QHostInfo.localHostName()) + "." + str( QtNetwork.QHostInfo.localDomainName()) ApplicationEntityService().init(AET, ConfigDetails().rpbAEport) aeCount = 0 section = "RemoteAEs" if appConfig.hasSection(section): if appConfig.hasOption(section, "count"): aeCount = int(appConfig.get(section)["count"]) for i in range(0, aeCount): section = "RemoteAE" + str(i) if appConfig.hasSection(section): address = "" if appConfig.hasOption(section, "address"): address = appConfig.get(section)["address"] port = -1 if appConfig.hasOption(section, "port"): port = int(appConfig.get(section)["port"]) aet = "" if appConfig.hasOption(section, "aet"): aet = appConfig.get(section)["aet"] ConfigDetails().remoteAEs.append( dict(Address=address, Port=port, AET=aet)) section = "SanityTests" if appConfig.hasSection(section): if appConfig.hasOption(section, "patientgendermatch"): ConfigDetails().patientGenderMatch = appConfig.getboolean( section, "patientGenderMatch") if appConfig.hasOption(section, "patientdobmatch"): ConfigDetails().patientDobMatch = appConfig.getboolean( section, "patientDobMatch") section = "General" if appConfig.hasSection(section): if appConfig.hasOption(section, "startupupdatecheck"): ConfigDetails().startupUpdateCheck = appConfig.getboolean( section, "startupupdatecheck") section = "Temp" if appConfig.hasSection(section): if appConfig.hasOption(section, "isupgrading"): ConfigDetails().isUpgrading = appConfig.get(section)["isupgrading"] if appConfig.hasOption(section, "upgradefinished"): ConfigDetails().upgradeFinished = appConfig.get( section)["upgradefinished"]
def setModel(self, patient, dicomDataRoot): """Prepare view models for this dialog """ self._patient = patient if dicomDataRoot is not None: # Take the main selected study from dataRoot for study in dicomDataRoot.children: if study.isChecked: self._study = study break # Consider all selected series from dataRoot for study in dicomDataRoot.children: for serie in study.children: if serie.isChecked: self._studySeries.append(serie) # View model for DICOM patient self.txtPatientId.setText(self._patient.id) self.txtNewPatientId.setText(self._patient.newId) self.txtPatientName.setText(self._patient.name) self.txtNewPatientName.setText(self._patient.newName) self.txtPatientGender.setText(self._patient.gender) self.txtNewPatientGender.setText(self._patient.newGender) self.txtPatientDOB.setText(self._patient.dob) self.txtNewPatientDOB.setText(self._patient.newDob) # View model for DICOM study self.cmbStudyType.addItems(self._studyTypeList) index = self.cmbStudyType.findText(self._study.studyType) if index != -1: self.cmbStudyType.setCurrentIndex(index) self.txtStudyDescription.setText(self._study.description) if self._study.description == "": self.txtNewStudyDescription.setReadOnly(True) # View model for DICOM study series self.seriesModel = DicomStudySeriesTableModel(self._studySeries) self.seriesProxyModel = QtGui.QSortFilterProxyModel() self.seriesProxyModel.setSourceModel(self.seriesModel) self.seriesProxyModel.setDynamicSortFilter(True) self.seriesProxyModel.setFilterCaseSensitivity( QtCore.Qt.CaseInsensitive) # Filtering QtCore.QObject.connect(self.txtSeriesFilter, QtCore.SIGNAL("textChanged(QString)"), self.seriesProxyModel.setFilterRegExp) # Assign to TableView self.tvSeries.setModel(self.seriesProxyModel) self.tvSeries.resizeColumnsToContents() # Selection changed self.cmbStudyType.currentIndexChanged['QString'].connect( self.cmbStudyTypeChanged) self.tvSeries.selectionModel().currentChanged.connect( self.tblSeriesItemChanged) # Automatically copy descriptions: save user some clicks if ConfigDetails().retainStudySeriesDescriptions: self.copyStudyDescButtonClicked() self.btnCopySeriesDescClicked()
def passSanityCheck(self, rpbStudySubject): """Check whether the main subject characteristics of DICOM data is matching chosen RPB subject """ genderPass = False dobPass = False if rpbStudySubject.subject != None and self._patient != None: # Gender match is enabled and RPB subject has gender and DICOM patient has gender if ConfigDetails().patientGenderMatch and \ rpbStudySubject.subject.gender is not None and rpbStudySubject.subject.gender != "" and \ self._patient.gender is not None and self._patient.gender != "" and self._patient.gender != "O": # Gender is the same if rpbStudySubject.subject.gender.lower( ) == self._patient.gender.lower(): genderPass = True self._logger.info("Gender sanity check passed.") else: self._logger.error("RPB subject gender: " + rpbStudySubject.subject.gender.lower()) self._logger.error("DICOM patient gender: " + self._patient.gender.lower()) # Gender is not provided so pass the test else: genderPass = True self._logger.info("Gender sanity check was skipped.") # DOB match is enabled and full date of birth collected and RPB subject has DOB and DICOM patient has DOB if ConfigDetails().patientDobMatch and\ rpbStudySubject.subject.dateOfBirth is not None and rpbStudySubject.subject.dateOfBirth != "" and\ self._patient.dob != None and self._patient.dob != "": # Convert from strings dates format = "%Y%m%d" dicomdob = datetime.strptime(self._patient.dob, format) deidentdob = datetime.strptime( self._deidentConfig.ReplaceDateWith, format) format = "%Y-%m-%d" edcdob = datetime.strptime(rpbStudySubject.subject.dateOfBirth, format) # DOB is the same if edcdob == dicomdob: dobPass = True self._logger.info("Full DOB sanity check passed.") # DICOM DOB was deidentified before elif dicomdob == deidentdob: dobPass = True self._logger.info( "DOB sanity chack was skipped, because provided DICOM DOB is already de-identifed." ) else: self._logger.error("RPB subject date of birth: " + str(edcdob)) self._logger.error("DICOM patient date of birth: " + str(dicomdob)) # DOB match is enabled and only year of birth collected and RPB subject has year of birth and DICOM patient has DOB elif ConfigDetails().patientDobMatch and\ rpbStudySubject.subject.yearOfBirth is not None and rpbStudySubject.subject.yearOfBirth != "" and\ self._patient.dob != None and self._patient.dob != "": # Year of birth is the same if rpbStudySubject.subject.yearOfBirth == self._patient[:4]: dobPass = True self._logger.info("Year of birth sanity check passed.") else: self._logger.error("RPB subject year of birth: " + rpbStudySubject.subject.yearOfBirth) self._logger.error("DICOM patient year of birth: " + self._patient[:4]) # DOB is not provided so pass the test else: dobPass = True self._logger.info("DOB sanity check was skipped.") return genderPass and dobPass
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 setModel(self, originalRoiNameDict, formalizedRTStructs, leftRightContours, extraTextContours, oarContours, tvContours, tvMultipleContours): """Fills the entries of the table with the ROI names and checkboxes for the default names """ self.__originalRoiNameDict = originalRoiNameDict self.__originalRoiNumberList = originalRoiNameDict.keys() self.__formalizedRTStructs = formalizedRTStructs self._extraTextContours = extraTextContours self._leftRightContours = leftRightContours self._oarContours = oarContours self._tvContours = tvContours self._tvMultipleContours = tvMultipleContours # Each ROI will be presented on separate row Nrows = len(self.__originalRoiNameDict.values()) Ncols = 6 # Set number of rows and columns and set widths and titles of rows self.tableWidget.setRowCount(Nrows) self.tableWidget.setColumnCount(Ncols) self.tableWidget.setColumnWidth(0, 200) self.tableWidget.setColumnWidth(1, 200) self.tableWidget.setColumnWidth(2, 200) self.tableWidget.setColumnWidth(3, 120) self.tableWidget.setColumnWidth(4, 120) self.tableWidget.setColumnWidth(5, 200) self.tableWidget.setHorizontalHeaderLabels(["Original Name", "Replace Name" ,"Assigned Name", "Additional Info", "Margin (mm)", "Prescription dose (cGy)"]) # Full table: standardised combined string self.txtStandardisedList = [] # Fill table: original ROI name on the left, default name comboboxes on the right self.comboBoxList = [] # Fill the table: additional info txt box self.cmbExtraList = [] # Fill the table: margin txt box self.cmbMarginList = [] # Fill the table: dose txt box self.cmbDoseList = [] # Create rows for i in xrange(Nrows): # Key for Original ROI Name dictionary - it is the number of original ROI in DCM key = self.__originalRoiNumberList[i] # Standard txtStandard = QtGui.QLabel() txtStandard.setStyleSheet(self.greenStyle) # Assigned name combobox = ExtendedCombo() combobox.setStyleSheet(self.orangeStyle) # Organ laterality and extra cmbExtra = QtGui.QComboBox() cmbExtra.setStyleSheet(self.orangeStyle) cmbExtra.setEnabled(False) cmbExtra.setEditable(False) # Margin cmbMargin = QtGui.QComboBox() cmbMargin.setStyleSheet(self.orangeStyle) cmbMargin.setEnabled(False) cmbMargin.setEditable(False) # Dose cmbDose = QtGui.QComboBox() cmbDose.setStyleSheet(self.orangeStyle) cmbDose.setEnabled(False) cmbDose.setEditable(False) # Use formalized RTStructs as data set for combobox rtsNames = [] for item in self.__formalizedRTStructs: combobox.addItem(item.name, item.identifier) rtsNames.append(item.name) # Put original ROI name into first column self.tableWidget.setItem( i, 0, QtGui.QTableWidgetItem( self.__originalRoiNameDict[key][0]) ) # Original name is not editable self.tableWidget.item(i, 0).setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.tableWidget.item(i, 0).setBackgroundColor(QtGui.QColor(gui.colours.RED)) # Put combo boxes to extra columns self.tableWidget.setCellWidget(i, 1, txtStandard) self.tableWidget.setCellWidget(i, 2, combobox) self.tableWidget.setCellWidget(i, 3, cmbExtra) self.tableWidget.setCellWidget(i, 4, cmbMargin) self.tableWidget.setCellWidget(i, 5, cmbDose) # Automatic matching of RTStructs names is configurable j = 0 # Select first otherwise if ConfigDetails().autoRTStructMatch: # Find most promising agreement (first) to ROIName in default_values closest = get_close_matches(\ self.__originalRoiNameDict[key][0],\ rtsNames, 1, 0)[0] # Find position of closest name in j = rtsNames.index(closest) combobox.setCurrentIndex(j) self.logger.debug(self.__originalRoiNameDict[key][0] + \ " initally mapped to: " + \ self.formalizedRTStructs[j].identifier.encode("utf-8")) # Save presselected options to RoiNameDic self.__originalRoiNameDict[self.__originalRoiNumberList[i]].\ append(self.formalizedRTStructs[j].identifier) # Show initial mapping in GUI txtStandard.setText(self.formalizedRTStructs[j].identifier.encode("utf-8")) # Enable additional info for L/R if self.__formalizedRTStructs[j].name in self._leftRightContours: cmbExtra.setEnabled(True) cmbExtra.setEditable(False) cmbExtra.clear() for lrItem in self._leftRightModel: cmbExtra.addItem(lrItem[0], lrItem[1]) # Enable additional info form multiple TV elif self.__formalizedRTStructs[j].name in self._tvMultipleContours: cmbExtra.setEnabled(True) cmbExtra.setEditable(False) cmbExtra.clear() for multiItem in self._tvMultipleModel: cmbExtra.addItem(multiItem[0], multiItem[1]) # Enable non standard additional info for contours of common type elif self.__formalizedRTStructs[j].name in self._extraTextContours: cmbExtra.setEnabled(True) cmbExtra.setEditable(True) cmbExtra.clear() else: cmbExtra.setEnabled(False) cmbExtra.setEditable(False) cmbExtra.clear() # Enable margin info for organ at risk OAR as well as TV if self.__formalizedRTStructs[j].name in self._oarContours: cmbMargin.setEnabled(True) cmbMargin.setEditable(True) cmbMargin.setValidator(QtGui.QIntValidator(0, 99, cmbMargin)) cmbMargin.clear() for marginItem in self._oarMarginModel: cmbMargin.addItem(marginItem[0], marginItem[1]) elif self.__formalizedRTStructs[j].name in self._tvContours: cmbMargin.setEnabled(True) cmbMargin.setEditable(True) cmbMargin.setValidator(QtGui.QIntValidator(0, 99, cmbMargin)) cmbMargin.clear() else: cmbMargin.setEnabled(False) cmbMargin.setEditable(False) cmbMargin.clear() # Enable dose info for target volume TV if self.__formalizedRTStructs[j].name in self._tvContours: cmbDose.setEnabled(True) cmbDose.setEditable(True) cmbDose.setValidator(QtGui.QIntValidator(0, 90000, cmbDose)) cmbDose.clear() else: cmbDose.setEnabled(False) cmbDose.setEditable(False) cmbDose.clear() # Handler self.connect(combobox, QtCore.SIGNAL("currentIndexChanged(QString)"), self.updateMapping) self.connect(cmbExtra, QtCore.SIGNAL("editTextChanged(QString)"), self.updateDetailsMappingText) self.connect(cmbExtra, QtCore.SIGNAL("currentIndexChanged(int)"), self.updateDetailsMappingCombo) self.connect(cmbMargin, QtCore.SIGNAL("currentIndexChanged(int)"), self.updateMarginMappingCombo) self.connect(cmbMargin, QtCore.SIGNAL("editTextChanged(QString)"), self.updateMarginMappingText) self.connect(cmbDose, QtCore.SIGNAL("editTextChanged(QString)"), self.updateDoseMappingText) # Append combobox to list self.txtStandardisedList.append(txtStandard) self.comboBoxList.append(combobox) self.cmbExtraList.append(cmbExtra) self.cmbMarginList.append(cmbMargin) self.cmbDoseList.append(cmbDose)
def downloadDicomData(self, data, thread): """Get DICOM data from server """ downloadDir = data[0] # self._downloadDir studyIdentifier = data[1] # self.getStudyIdentifier() svcHttp = data[2] # Create study/site folder thread.emit(QtCore.SIGNAL("log(QString)"), "Creating download study folder...") studyPath = downloadDir + os.sep + studyIdentifier if not os.path.isdir(studyPath): os.mkdir(studyPath) # Give me and list of what DICOM studies are available and how to access them thread.emit(QtCore.SIGNAL("log(QString)"), "Querying list of available DICOM data...") patientsDicomData = svcHttp.getAllSubjectsDicomData(studyIdentifier) thread.emit(QtCore.SIGNAL("log(QString)"), str(len(patientsDicomData)) + " patients available...") # For each patient for p in patientsDicomData: # Create patient folder patientFolderName = "" if ConfigDetails().downloadDicomPatientFolderName == "pid": patientFolderName = p.uniqueIdentifier elif ConfigDetails().downloadDicomPatientFolderName == "ssid": patientFolderName = p.studySubjectId thread.emit( QtCore.SIGNAL("log(QString)"), "Processing patient: " + p.uniqueIdentifier + " (" + p.studySubjectId + ")") patientPath = studyPath + os.sep + patientFolderName if not os.path.isdir(patientPath): os.mkdir(patientPath) # For each dicom study for dicom in p.dicomData: studyFolderName = "" if ConfigDetails().downloadDicomStudyFolderName == "oid": studyFolderName = dicom.oid elif ConfigDetails().downloadDicomStudyFolderName == "label": studyFolderName = dicom.label thread.emit( QtCore.SIGNAL("log(QString)"), "Unzipping DICOM study data: " + studyFolderName + " please wait...") # Unzip and list of files data = svcHttp.unzipDicomData(dicom.webApiUrl) # Download files count = len(data[0].dicomData[0].fileUrls) downloaded = 0 thread.emit( QtCore.SIGNAL("log(QString)"), str(count) + " files in DICOM study: " + studyFolderName) # Create DICOM study folder - based on crfItem name thread.emit( QtCore.SIGNAL("log(QString)"), "Downloading DICOM study data: " + studyFolderName + "...") studyPath = patientPath + os.sep + studyFolderName if not os.path.isdir(studyPath): os.mkdir(studyPath) for fileUrl in data[0].dicomData[0].fileUrls: svcHttp.downloadDicomData(fileUrl, studyPath) downloaded += 1 thread.emit(QtCore.SIGNAL("taskUpdated"), [downloaded, count]) # Cleanup unzipped files from server svcHttp.clearDicomData(dicom.webApiUrl) thread.emit(QtCore.SIGNAL('log(QString)'), 'Finished!') thread.emit(QtCore.SIGNAL('message(QString)'), "Download job was successful.") return True
def checkTreatmentPlanData(self, thread=None): """Checks the completeness of DICOM study data (treatment plan) Only applicable if study is treatment plan """ modalities = self.dicomData.getModalities() if "CT" in modalities and \ "RTSTRUCT" in modalities and \ "RTPLAN" in modalities and \ "RTDOSE" in modalities: ct_dicomData = self.dicomData.belongingTo("Modality", "CT") li_SOPInstanceUID_CT = [] for elem in ct_dicomData: li_SOPInstanceUID_CT.append(elem["SOPInstanceUID"]) # Check how many RTSTRUCT is in the folder rtstruct_dicomData = self.dicomData.belongingTo( "Modality", "RTSTRUCT") if len(rtstruct_dicomData) > 1: thread.emit(QtCore.SIGNAL("message(QString)"), gui.messages.ERR_MULTIPLE_RTSTRUCT) return False rtstruct_dicomData = rtstruct_dicomData[0] if "FrameOfReferenceUID" not in rtstruct_dicomData: msg = "Structure set is not defined on top of CT data. " msg += "Upload of inconsistent treatment plan is not possible!" thread.emit(QtCore.SIGNAL("message(QString)"), msg) return False # Check whether all CT images to where RTSTRUCT refers are provided missingCT = False missingCT_UID = [] for elem in rtstruct_dicomData["ContourImageSequence"]: if elem not in li_SOPInstanceUID_CT: missingCT = True missingCT_UID.append(elem) if missingCT: msg = "Structure set (RTSTRUCT) is referencing CT images which are not within the provided data set: " msg += str(missingCT_UID) thread.emit(QtCore.SIGNAL("message(QString)"), msg) return False rtplan_dicomData = self.dicomData.belongingTo("Modality", "RTPLAN") # SOP instance UID of RTSTRUCT rtstruct_SOPInstanceUID = rtstruct_dicomData["SOPInstanceUID"] # Collect SOP instance UID from RTPLAN (one or more) rtplan_SOPInstanceUID = [] # Each plan sould refer max 1 RTSTRUCT planRefStructUID = [] for elem in rtplan_dicomData: rtplan_SOPInstanceUID.append(elem["SOPInstanceUID"]) planRefStructUID.append( elem["ReferencedSOPInstanceUID_RTSTRUCT"]) # Collect SOP instance UID from RTDOSE referencing to RTPLAN # Collect SOP instance UID from RTDOSE referencing to RTSTRUCT rtdose_dicomData = self.dicomData.belongingTo("Modality", "RTDOSE") self._doseCount = len(rtdose_dicomData) doseRefPlanUID = [] doseRefStructUID = [] for elem in rtdose_dicomData: doseRefPlanUID.append(elem["ReferencedSOPInstanceUID_RTPLAN"]) # Is not mandatory so it does not have to be there if "ReferencedSOPInstanceUID_RTSTRUCT" in elem: doseRefStructUID.append( elem["ReferencedSOPInstanceUID_RTSTRUCT"]) # Remove duplicate values doseRefStructUID = list(set(doseRefStructUID)) doseRefPlanUID = list(set(doseRefPlanUID)) # Check whether the all RTPLANs referenced from RTDOSEs exists for refRtPlanUID in doseRefPlanUID: if refRtPlanUID not in rtplan_SOPInstanceUID: msg = "One of RTDOSE is referencing to unknown RTPLAN. " msg += "Upload of inconsistent treatment plan is not possible!" thread.emit(QtCore.SIGNAL("message(QString)"), msg) return False # Check whether the RTSTRUCT referenced from RTDOSEs exists for refRtStructUID in doseRefStructUID: if refRtStructUID != rtstruct_SOPInstanceUID: msg = "One of RTDOSE is referencing to unknown RTSTRUCT. " msg += "Upload of inconsistent treatment plan is not possible!" thread.emit(QtCore.SIGNAL("message(QString)"), msg) return False # How many RTPLAN and RTDOSE SOP instances have been detected if rtplan_dicomData is not None: self._planCount = len(rtplan_dicomData) if rtdose_dicomData is not None: self._doseCount = len(rtdose_dicomData) # RTDOSE DoseSummationType should be uniform accross treatment planning data self._summNumberOfBeams = 0 if len(self.dicomData.unique("DoseSummationType")) == 1: self._doseSumType = self.dicomData.unique( "DoseSummationType")[0] if self._doseSumType == "BEAM": for i in xrange(self._planCount): # Summ number of beams from each plan if "BeamNumbers" in rtplan_dicomData[i]: self._summNumberOfBeams += rtplan_dicomData[i][ "BeamNumbers"] # Check whether the RTSTRUCT is referenced from all RTPLANs if not ConfigDetails().autoRTStructRef: for refRtStructUID in planRefStructUID: if refRtStructUID != rtstruct_SOPInstanceUID: msg = "One of RTPLAN is referencing to unknown RTSTRUCT. " msg += "Upload of inconsistent treatment plan is not possible!" thread.emit(QtCore.SIGNAL("message(QString)"), msg) return False return True else: missingModality = "" li_ModalityFull = ["RTSTRUCT", "RTPLAN", "CT", "RTDOSE"] for elem in li_ModalityFull: if elem not in modalities: missingModality = elem msg = "Treatment plan data is not complete. " msg += missingModality + " modality is missing. " msg += "Upload of not complete treatment plan is not possible. " msg += "Please correct your treatment plan and try it again." thread.emit(QtCore.SIGNAL("message(QString)"), msg) return False
for study in studies: selectedStudy = study sucessfull, studyMetadata = ocWebServices.getStudyMetadata(selectedStudy) break # Load subject for whole study or only site if it is multicentre study if selectedStudy and selectedStudy.isMulticentre: studySubjects = ocWebServicees.listAllStudySubjectsByStudySite( [selectedStudy, selectedStudySite, studyMetadata] ) else: studySubjects = ocWebServices.listAllStudySubjectsByStudy( [selectedStudy, studyMetadata] ) ConfigDetails().ocHost = "http://skripcak.net:8080/OpenClinica" ConfigDetails().ocPort = "80" UserDetails().username = OCUserDetails().username UserDetails().clearpass = password svcHttp = HttpConnectionService( ConfigDetails().ocHost, ConfigDetails().ocPort, UserDetails() ) restSubjects = svcHttp.getStudyCasebookSubjects( [ConfigDetails().ocHost, selectedStudy.oid] )
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()
def __init__(self): """Default constructor """ # Setup GUI QtGui.QDialog.__init__(self) self.setWindowTitle("OpenClinica - Login") appIconPath = ':/images/rpb-icon.jpg' appIcon = QtGui.QIcon() appIcon.addPixmap(QtGui.QPixmap(appIconPath)) self.setWindowIcon(appIcon) toolBarButtonSize = 15 # Dialog layout root rootLayout = QtGui.QVBoxLayout(self) # Login grid loginLayout = QtGui.QGridLayout() loginLayout.setSpacing(10) rootLayout.addLayout(loginLayout) # Connection lblConnection = QtGui.QLabel("Connection:") self.txtConnection = QtGui.QLineEdit() self.txtConnection.setText(ConfigDetails().ocHost + "/") self.txtConnection.setMinimumWidth(300) self.txtConnection.setDisabled(True) # User label lblUsername = QtGui.QLabel("Username:"******"Password:"******"Login") self.btnLogin.setIcon(loginIcon) self.btnLogin.setToolTip("Login") self.btnLogin.setIconSize( QtCore.QSize(toolBarButtonSize, toolBarButtonSize)) self.btnLogin.clicked.connect(self.handleLogin) # Add to connection layout loginLayout.addWidget(lblConnection, 0, 0) loginLayout.addWidget(self.txtConnection, 0, 2) loginLayout.addWidget(lblUsername, 1, 0) loginLayout.addWidget(self.txtUsername, 1, 1, 1, 2) loginLayout.addWidget(lblPassword, 2, 0) loginLayout.addWidget(self.txtPassword, 2, 1, 1, 2) loginLayout.addWidget(self.btnLogin, 3, 1, 1, 2) self.txtUsername.setFocus() #------------------------------------------------------------------ #------------------ ViewModel ------------------------------------- self.userName = "" self.password = ""
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 prepareServices(self): """Prepare services for this module """ # HTTP connection to RadPlanBio server (Database) self.svcHttp = HttpConnectionService(ConfigDetails().ocHost, ConfigDetails().ocPort, UserDetails()) if ConfigDetails().proxyEnabled: self.svcHttp.setupProxy(ConfigDetails().proxyHost, ConfigDetails().proxyPort, ConfigDetails().noProxy) if ConfigDetails().proxyAuthEnabled: self.svcHttp.setupProxyAuth(ConfigDetails().proxyAuthLogin, ConfigDetails().proxyAuthPassword) # Create connection artefact to users main OpenClinica SOAP self.ocConnectInfo = OCConnectInfo(ConfigDetails().ocWsHost, 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.svcOdm = OdmFileDataService()
if successfull: for study in studies: selectedStudy = study sucessfull, studyMetadata = ocWebServices.getStudyMetadata( selectedStudy) break # Load subject for whole study or only site if it is multicentre study if selectedStudy and selectedStudy.isMulticentre: studySubjects = ocWebServicees.listAllStudySubjectsByStudySite( [selectedStudy, selectedStudySite, studyMetadata]) else: studySubjects = ocWebServices.listAllStudySubjectsByStudy( [selectedStudy, studyMetadata]) ConfigDetails().ocHost = "http://skripcak.net:8080/OpenClinica" ConfigDetails().ocPort = "80" UserDetails().username = OCUserDetails().username UserDetails().clearpass = password svcHttp = HttpConnectionService(ConfigDetails().ocHost, ConfigDetails().ocPort, UserDetails()) restSubjects = svcHttp.getStudyCasebookSubjects( [ConfigDetails().ocHost, selectedStudy.oid]) # Enhance with subject OID for studySubject in studySubjects: for sREST in restSubjects: if sREST.studySubjectId == studySubject.label:
def __init__(self, destination, patient, dicomDataRoot, mappingRoiDic): """Default constructor """ self._svcCrypto = CryptoService() # Configuration of deidentification self._deidentConfig = DeidentConfig() self._deidentConfig.ReplacePatientNameWith = patient.newName self._deidentConfig.RetainPatientCharacteristicsOption = ConfigDetails( ).retainPatientCharacteristicsOption # Load the deident model according to configuration self._modelLoader = DeidentModelLoader(self._deidentConfig) # Get profile and options from loaded model self._profile = self._modelLoader.GetProfile() self._options = self._modelLoader.GetOptions() self._destination = destination self._lastGeneratedUid = None self._errorMessage = "" self._sourceSize = 0 # List of original DICOM UID for elements with name in li_NameUID self.li_UID = blist([]) # List of anonymised DICOM UID for element with name in li_NameUID self.li_UID_anonym = blist([]) self.__patient = patient if dicomDataRoot is not None: self._dicomDataRoot = dicomDataRoot self.__series = [] # Take the main selected study from dataRoot for study in dicomDataRoot.children: if study.isChecked: self.__study = study break # Consider all selected series from dataRoot for study in dicomDataRoot.children: for serie in study.children: if serie.isChecked: self.__series.append(serie) self.__mappingRoiDic = mappingRoiDic # These list for now define the Basic Profile # Which fields are UIDs and should be replaced by randomly generated UID self.li_NameUID = [ 'Concatenation UID', 'Context Group Extension Creator UID', 'Creator Version UID', 'Creator-Version UID', 'Device UID', 'Dimension Organization UID', 'Dose Reference UID', 'Failed SOP Instance UID List', 'Fiducial UID', 'Frame of Reference UID', 'Instance Creator UID', 'Irradiation Event UID', 'Large Palette Color Lookup Table UID', 'Media Storage SOP Instance UID', 'Palette Color Lookup Table UID', 'Referenced Frame of Reference UID', 'Referenced General Purpose Scheduled Procedure Step Transaction UID', 'Referenced SOP Instance UID', 'Referenced SOP Instance UID in File', 'Related Frame of Reference UID', 'Requested SOP Instance UID', 'Series Instance UID', 'SOP Instance UID', 'Storage Media File-set UID', 'Synchronization Frame of Reference UID', 'Template Extension Creator UID', 'Template Extension Organization UID', 'Transaction UID', 'UID' ] # Which fields should be removed self.li_NameRemove = [ 'Acquisition Comments', 'Acquisition Context Sequence', 'Acquisition Protocol Description', 'Actual Human Performers Sequence', "Additional Patient's History", "Additional Patient History", 'Admission ID', 'Admitting Date', 'Admitting Diagnoses Code Sequence', 'Admitting Diagnoses Description', 'Admitting Time', 'Affected SOP Instance UID', 'Allergies', 'Arbitrary', 'Author Observer Sequence', 'Branch of Service', 'Cassette ID', 'Comments on Performed Procedure Step', 'Comments on the Performed Procedure Step', 'Confidentiality Constraint on Patient Data Description', "Content Creator's Identification Code", "Content Creator's Identification Code Sequence", 'Content Sequence', 'Contribution Description', 'Country of Residence', 'Current Patient Location', 'Curve Data', 'Curve Date', 'Curve Time', 'Custodial Organization Sequence', 'Data Set Trailing Padding', 'Derivation Description', 'Detector ID', 'Digital Signature UID', 'Digital Signatures Sequence', 'Discharge Diagnosis Description', 'Distribution Address', 'Distribution Address', 'Ethnic Group', 'Frame Comments', 'Gantry ID', 'Generator ID', 'Human Performers Name', "Human Performer's Name", 'Human Performers Organization', "Human Performer's Organization", 'Icon Image Sequence', 'Identifying Comments', 'Image Comments', 'Image Presentation Comments', 'Image Service Request Comments', "Imaging Service Request Comments", 'Impressions', 'Institution Address', 'Institutional Department Name', 'Insurance Plan Identification', 'Intended Recipients of Results Identification Sequence', 'Interpretation Approver Sequence', 'Interpretation Author', 'Interpretation Diagnosis Description', 'Interpretation ID Issuer', 'Interpretation Recorder', 'Interpretation Text', 'Interpretation Transcriber', 'Issuer of Admission ID', 'Issuer of Patient ID', 'Issuer of Service Episode ID', 'Last Menstrual Date', 'MAC', 'Medical Alerts', 'Medical Record Locator', 'Military Rank', 'Modified Attributes Sequence', 'Modified Image Description', 'Modifying Device ID', 'Modifying Device Manufacturer', 'Name of Physician(s) Reading Study', 'Names of Intended Recipients of Results', 'Occupation', 'Original Attributes Sequence', 'Order Callback Phone Number', 'Order Entered By', 'Order Enterer Location', "Order Enterer's Location", 'Other Patient IDs', 'Other Patient IDs Sequence', 'Other Patient Names', 'Overlay Comments', 'Overlay Data', 'Overlay Date', 'Overlay Time', 'Participant Sequence', 'Patient Address', "Patient's Address", "Patient's Age", 'Patient Comments', 'Patient State', 'Patient Transport Arrangements', "Patient's Birth Name", "Patient's Birth Time", "Patient's Institution Residence", "Patient's Insurance Plan Code Sequence", "Patient's Mother's Birth Name", "Patient's Primary Language Code Sequence", "Patient's Primary Language Modifier Code Sequence", "Patient's Religious Preference", "Patient's Size", "Patient's Telephone Numbers", "Patient's Weight", 'Performed Location', 'Performed Procedure Step Description', 'Performed Procedure Step ID', 'Performed Procedure Step Start Date', 'Performed Procedure Step Start Time', 'Performed Station AE Title', 'Performed Station Geographic Location Code Sequence', 'Performed Station Name', 'Performed Station Name Code Sequence', "Performing Physicians' Identification Sequence", "Performing Physician Identification Sequence", "Performing Physicians' Name", "Performing Physician's Name", 'Person Address', "Person's Address", 'Person Telephone Numbers', "Person's Telephone Numbers", 'Physician Approving Interpretation', 'Physician Reading Study Identification Sequence', "Physician(s) Reading Study Identification Sequence", 'Physician(s) of Record', 'Physician(s) of Record Identification Sequence', 'Plate ID', 'Procedure Code Sequence', 'Pre-Medication', 'Pregnancy Status', 'Reason for Imaging Service Request', 'Reason for the Imaging Service Request', 'Reason for Study', 'Referenced Digital Signature Sequence', 'Referenced Patient Alias Sequence', 'Referenced Patient Sequence', 'Referenced Performed Procedure Step Sequence', 'Referenced SOP Instance MAC Sequence', 'Referenced Study Sequence', "Referring Physician's Address", "Referring Physician's Identification Sequence", "Referring Physician Identification Sequence", "Referring Physician's Telephone Numbers", 'Region of Residence', 'Request Attributes Sequence', 'Requested Contrast Agent', 'Requested Procedure Comments', 'Requested Procedure ID', 'Requested Procedure Code Sequence', 'Requested Procedure Location', 'Requesting Physician', 'Requesting Service', 'Responsible Person', 'Results Comments', 'Results Distribution List Sequence', 'Results ID Issuer', 'Scheduled Human Performers Sequence', 'Scheduled Patient Institution Residence', 'Scheduled Performing Physician Identification Sequence', 'Scheduled Performing Physician Name', "Scheduled Performing Physician's Name", 'Scheduled Procedure Step End Date', 'Scheduled Procedure Step End Time', 'Scheduled Procedure Step Description', 'Scheduled Procedure Step Location', 'Scheduled Procedure Step Start Date', 'Scheduled Procedure Step Start Time', 'Scheduled Station AE Title', 'Scheduled Station Geographic Location Code Sequence', 'Scheduled Station Name', 'Scheduled Station Name Code Sequence', 'Scheduled Study Location', 'Scheduled Study Location AE Title', 'Service Episode Description', 'Service Episode ID', 'Smoking Status', 'Source Image Sequence', 'Special Needs', 'Study Comments', 'Study ID', 'Study ID Issuer', 'Text Comments', 'Text String', 'Timezone Offset From UTC', 'Topic Author', 'Topic Key Words', "Topic Keywords", 'Topic Subject', 'Topic Title', 'Verifying Organization', 'Visit Comments' ] # Which fields sshould be replaces by default value self.li_NameReplace = [ 'Accession Number', 'Acquisition Date', 'Acquisition Date Time', "Acquisition DateTime", 'Acquisition Device Processing Description', 'Acquisition Time', "Content Creator's Name", 'Content Date', 'Content Time', 'Contrast Bolus Agent', "Contrast/Bolus Agent", 'Device Serial Number', 'Filler Order Number of Imaging Service Request', "Filler Order Number / Imaging Service Request", 'Graphic Annotation Sequence', 'Institution Code Sequence', 'Institution Name', "Operators' Identification Sequence", "Operator Identification Sequence", "Operators' Name", "Patient's Sex", 'Patient Sex Neutered', "Patient's Sex Neutered", "Patient's Birth Date", 'Person Identification Code Sequence', 'Placer Order Number of Imaging Service Request', "Placer Order Number / Imaging Service Request", 'Protocol Name', "Referring Physician's Name", 'Requested Procedure Description', 'Reviewer Name', "Series Date", "Series Time", 'Station Name', "Study Date", 'Study ID', "Study Time", 'Verifying Observer Identification Code Sequence', 'Verifying Observer Name', 'Verifying Observer Sequence' ] # Study/Series Date and Time seems to be important for later analysis if ConfigDetails().retainStudyDate: self.li_NameReplace.remove("Study Date") if ConfigDetails().retainStudyTime: self.li_NameReplace.remove("Study Time") if ConfigDetails().retainSeriesDate: self.li_NameReplace.remove("Series Date") if ConfigDetails().retainSeriesTime: self.li_NameReplace.remove("Series Time")