Ejemplo n.º 1
0
class AddressEditorWidget(Ui_AddressEditorWidget, QWidget):

    warningStyle = "* { color : red; font-weight: bold }"

    def __init__(self, parent=None, controller=None):
        QWidget.__init__(self, parent)
        self.setupUi(self)

        self._address = None
        for radius in (50, 100, 200, 500, 1000, 2000):
            self.uReplaceSearchRadius.addItem(str(radius))
        self.uReplaceSearchRadius.setCurrentIndex(0)
        self.uSelectReplacementButton.clicked.connect(self.selectReplacement)
        self.uUnselectReplacementButton.clicked.connect(self.unselectReplacement)
        self.uReplaceSearchRadius.currentIndexChanged[int].connect(self.searchForAddresses2)

        self._replacementList = DictionaryListModel()
        self._replacementList.setColumns(
            ["linked", "display", "distance", "sameparcel"], ["Replace", "Address", "Offset", "Same parcel"]
        )
        self.uReplacementAddresses.setModel(self._replacementList)
        self.uNewButton.clicked.connect(self.newButtonClicked)
        self.uReplaceButton.clicked.connect(self.replaceButtonClicked)
        self.uIgnoreButton.clicked.connect(self.ignoreButtonClicked)
        self.uAcceptButton.clicked.connect(self.setWarningColour)
        self.uHouseNumber.textChanged.connect(self.houseNumberChanged)
        self.load()
        self.setController(controller)

    def setController(self, controller):
        if not controller:
            controller = Controller.instance()
        self._controller = controller
        controller.addressUpdated.connect(self.addressUpdated)
        controller.addressSelected.connect(self.setAddressIfClean)

    def address(self):
        return self._address

    def setAddressIfClean(self, address):
        if not self.isDirty():
            self.setAddress(address)

    def setAddress(self, address, makeClean=True):
        if address == self._address:
            return True
        if makeClean:
            self.makeClean()
        self._address = address
        self.load()
        return True

    def makeClean(self):
        isclean = True
        if self._address and self.isDirty():
            result = QMessageBox.question(
                self,
                "Save changes",
                "Do you want to save the changes to " + self._address.address(),
                QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel,
            )
            if result == QMessageBox.Cancel:
                isclean = False
            if result == QMessageBox.Yes:
                self.save()
            elif result == QMessageBox.No:
                self.load()
        return isclean

    def notesText(self):
        return str(self.uNotes.document().toPlainText()).strip()

    def isDirty(self):
        if not self._address:
            return False
        if self._status != self._status0:
            return True
        if str(self.uHouseNumber.text()) != self._housenumber0:
            return True
        if self.notesText() != self._notes0:
            return True
        if self.uAcceptButton.isChecked():
            return True
        if self._sad_id != self._sad_id0:
            return True
        return False

    def addressUpdated(self, address):
        if self._address and address and address.id() == self._address.id():
            self.load(True)

    def save(self):
        if self.isDirty():
            address = self._address
            address.setStatus(self._status)
            address.setHousenumber(str(self.uHouseNumber.text()))
            address.setSad_id(self._sad_id)
            address.setAcceptWarnings(self.uAcceptButton.isChecked())
            address.setNotes(self.notesText())
            self._controller.updateAddress(address)

    def load(self, reload=False):
        address = self._address
        self.uAcceptButton.setEnabled(False)
        self.uAcceptButton.setChecked(False)
        self.uNewButton.setEnabled(False)
        self.uNewButton.setChecked(False)
        self.uReplaceButton.setEnabled(False)
        self.uReplaceButton.setChecked(False)
        self.uReplaceButton.setText("Replace")
        self.uIgnoreButton.setEnabled(False)
        self.uIgnoreButton.setChecked(False)
        if not address:
            self._status0 = None
            self._housenumber0 = None
            self._sad_id0 = None
            self._notes0 = None

            self._status = AddressStatus.Undefined
            self._sad_id = None
            self._delete = False
            self.uAddressBeingEdited.setText("No address selected")
            self.uStatus.setText("")
            self.uReplaceLabel.setText("Landonline address to replace")
            self.uHouseNumber.setText("")
            self.uSourceNumber.setText("")
            self.uNotes.setPlainText("")
            self.uWarnings.setText("")
            self.uHouseNumber.setEnabled(False)
            self.uNotes.setEnabled(False)
            self.uSrcComment.setText("")
            self._replacementList.setList([])
            self.uSelectReplacementButton.setEnabled(False)
            self.uUnselectReplacementButton.setEnabled(False)
            return
        self._status0 = address.status()
        self._housenumber0 = address.housenumber()
        self._sad_id0 = address.sad_id()
        self._notes0 = address.notes()

        self._status = address.status()
        self._sad_id = address.sad_id()
        self._delete = address.src_status == AddressStatus.Delete
        self._badroad = not address.roadIsValid()
        self._badgeometry = not address.geometryIsValid()
        self.uIgnoreButton.setEnabled(True)
        if self._delete:
            self.uReplaceButton.setText("Delete")
        else:
            self.uNewButton.setEnabled(True)
        if self._sad_id != None:
            self.uReplaceButton.setEnabled(True)
        if self._status == AddressStatus.New:
            self.uNewButton.setChecked(True)
        elif self._status in (AddressStatus.Replace, AddressStatus.Delete):
            self.uReplaceButton.setChecked(True)
        elif self._status == AddressStatus.Ignore:
            self.uIgnoreButton.setChecked(True)
        if self._badroad:
            self.uNewButton.setEnabled(False)
            self.uReplaceButton.setEnabled(False)
        if self._badgeometry:
            self.uNewButton.setEnabled(False)
            self.uReplaceButton.setEnabled(False)
        self.setStatusLabel()
        self.setRangeLabel()

        self.uAddressBeingEdited.setText(address.address())
        if self._delete:
            self.uReplaceLabel.setText("Landonline address to delete")
        else:
            self.uReplaceLabel.setText("Landonline address to replace with " + address.address())
        self.uHouseNumber.setText(address.housenumber())
        srcnumber = ""
        if address.ismerged():
            srcnumber = "(from merge)"
        else:
            srcnumber = "(Supplied as " + address.src_housenumber() + ")"
        self.uSourceNumber.setText(srcnumber)
        warnings = ", ".join(
            str(CodeLookup.lookup("address_warning", code)) for code in address.warning_codes().split()
        )
        self.uWarnings.setText(warnings)
        if warnings:
            self.uAcceptButton.setEnabled(True)
            self.setWarningColour()
        self.uSrcComment.setText(address.src_comment())
        self.uNotes.setPlainText(address.notes())
        self.uHouseNumber.setEnabled(True)
        self.uNotes.setEnabled(True)
        self.searchForAddresses()

    def houseNumberChanged(self, newnumber):
        self.setRangeLabel()

    def setRangeLabel(self):
        if not self._address:
            self.uRange.setText("")
            self.uRange.setStyleSheet("")
            return
        range_low = None
        range_high = None
        if self.uHouseNumber.text() == self._housenumber0:
            range_low = self._address.range_low()
            range_high = self._address.range_high()
        else:
            range_low, range_high = self.extractNumberRange(str(self.uHouseNumber.text()))
        if range_low is None:
            self.uRange.setText("Invalid")
            self.uRange.setStyleSheet(self.warningStyle)
        elif range_high is None:
            self.uRange.setText("Range: " + str(range_low) + "-")
            self.uRange.setStyleSheet("")
        else:
            self.uRange.setText("Range: " + str(range_low) + "-" + str(range_high))
            self.uRange.setStyleSheet("")

    def newButtonClicked(self):
        self.uncheckButtons(self.uNewButton)
        self.resetStatus()

    def replaceButtonClicked(self):
        self.uncheckButtons(self.uReplaceButton)
        self.resetStatus()

    def ignoreButtonClicked(self):
        self.uncheckButtons(self.uIgnoreButton)
        self.resetStatus()

    def uncheckButtons(self, button):
        if button.isChecked():
            if button != self.uNewButton:
                self.uNewButton.setChecked(False)
            if button != self.uReplaceButton:
                self.uReplaceButton.setChecked(False)
            if button != self.uIgnoreButton:
                self.uIgnoreButton.setChecked(False)

    def resetStatus(self):
        status = AddressStatus.Undefined
        if self._badroad:
            status = AddressStatus.BadRoad
        if self.uNewButton.isChecked():
            status = AddressStatus.New
        elif self.uReplaceButton.isChecked():
            status = AddressStatus.Delete if self._delete else AddressStatus.Replace
        elif self.uIgnoreButton.isChecked():
            status = AddressStatus.Ignore
        if self._badgeometry:
            status = AddressStatus.BadGeometry
        self._status = status
        self.setStatusLabel()

    def setStatus(self, status):
        self._status = status
        self.uNewButton.setChecked(status == AddressStatus.New)
        self.uReplaceButton.setChecked(status in (AddressStatus.Replace, AddressStatus.Delete))
        self.uIgnoreButton.setChecked(status == AddressStatus.Ignore)

    def setStatusLabel(self):
        label = CodeLookup.lookup("address_status", self._status, "None")
        if self._delete and self._status != AddressStatus.Delete:
            label += " (delete)"
        self.uStatus.setText(label)

    def setWarningColour(self):
        style = ""
        if not self.uAcceptButton.isChecked():
            style = self.warningStyle
        self.uWarnings.setStyleSheet(style)

    def selectReplacement(self):
        row = self.uReplacementAddresses.selectedItem()
        if row and not self._badroad:
            self._sad_id = row["sad_id"]
            self.uReplaceButton.setEnabled(True)
            self.uReplaceButton.setChecked(True)
            self.replaceButtonClicked()
        self.resetLinkedAddress()

    def unselectReplacement(self):
        self._sad_id = None
        if self._status == AddressStatus.Replace or self._status == AddressStatus.Delete:
            newstatus = AddressStatus.New if self._status == AddressStatus.Replace else AddressStatus.Ignore
            self.uReplaceButton.setChecked(False)
            self.uReplaceButton.setEnabled(False)
            if self._delete:
                self.uIgnoreButton.setChecked(True)
            else:
                self.uNewButton.setChecked(True)
            self.resetStatus()
        self.uReplaceButton.setEnabled(False)
        self.resetLinkedAddress()

    def searchForAddresses2(self, row):
        self.searchForAddresses()

    def searchForAddresses(self):
        found = False
        if self._address:
            radius = float(self.uReplaceSearchRadius.currentText())
            rlist = self._address.getNearbyAddressPoints(radius)
            found = len(rlist) > 0
            self._replacementList.setList(rlist)
            self.uReplacementAddresses.resizeColumnsToContents()
        else:
            self._replacementList.setList([])
        self.uSelectReplacementButton.setEnabled(found)
        self.uUnselectReplacementButton.setEnabled(found and bool(self._sad_id))

    def resetLinkedAddress(self):
        apts = self._replacementList.list()
        found = False
        for i in range(len(apts)):
            found = True
            apt = apts[i]
            if apt["sad_id"] == self._sad_id:
                if apt["linked"] != "Yes":
                    apt["linked"] = "Yes"
                    self._replacementList.updateItem(i)
            elif apt["linked"] == "Yes":
                apt["linked"] = ""
                self._replacementList.updateItem(i)
        self.uUnselectReplacementButton.setEnabled(found and bool(self._sad_id))

    # Note: This function needs to match the logic in the trigger
    # function _elc_trg_SetAddressStatus, which validates addresses
    def extractNumberRange(self, housenumber):

        numberre = (
            r"^(?:"
            +
            # 12-23, 15A-23B
            r"(\d+)[A-Z]*(?:\-(\d+)[A-Z]*)?|"
            +
            # 1/44-5/44 , 1A/83C-5B/83C
            r"[A-Z]{0,2}\d+[A-Z]*\/((\d+)[A-Z]*)(?:\-[A-Z]{0,2}\d+[A-Z]*\/\3)?|"
            +  # was - r'\d+[A-Z]*\/((\d+)[A-Z]*)(?:\-\d+[A-Z]*\/\3)?|' +
            # R/1234A
            r"R\/(\d+)[A-Z]"
            +
            # If all else fails match anything so that regexp_matches always
            # returns a row.
            r"|.*)$"
        )
        match = re.match(numberre, housenumber)
        range_low = match.group(1) or match.group(4) or match.group(5)
        range_high = match.group(2)
        range_low = int(range_low) if range_low else None
        range_high = int(range_high) if range_high else None
        if range_high == range_low:
            range_high = None
        if range_high != None and range_high < range_low:
            range_high = None
            range_low = None
        return range_low, range_high
Ejemplo n.º 2
0
class ManageSourceTypes( QWidget, Ui_ManageSourceTypes ):

    def __init__( self, parent=None, controller=None ):
        QWidget.__init__(self,parent)
        self.setupUi(self)
        self._editItem = None

        self._readerClasses=[]
        self._isDirty = False
        self.uNew.clicked.connect( self.newItemClicked )
        self.uSave.clicked.connect( self.saveItemClicked )
        self.uCancel.clicked.connect( self.cancelItemClicked )
        self.uDelete.clicked.connect( self.deleteItemClicked )
        self.uListModel = DictionaryListModel()
        self.uListView.setModel( self.uListModel )
        self.setListColumns( self.uListModel )
        self.uListView.rowSelected.connect( self.selectItem )

        self.uName.textChanged.connect( self.enableButtons2 )
        self.uReaderClass.currentIndexChanged[int].connect( self.enableButtons2 )
        self.uReaderClass.currentIndexChanged[int].connect( self.setReaderClass )
        self.uFileExtension.textChanged.connect( self.enableButtons2 )
        self.uCoordSys.currentIndexChanged[int].connect( self.enableButtons2 )
        self._params = DictionaryListModel(
            columns=['label','value'],
            headers=['Name','Value'])
        self._params.setEditColumns(['value'])
        self._params.setReadonlyColour( self.palette().color(QPalette.Window))
        self._params.dataChanged.connect( self.enableButtons3 )
        self.uParameterList.setModel(self._params)
        self.uParameterList.rowSelected.connect( self.showParamDesc )

        self.setController( controller )
        self.load()
        self.enableButtons()

    def setController( self, controller=None ):
        if not controller:
            controller=Controller()
        self._controller = controller
        self._controller.sourceTypesUpdated.connect( self.loadList )

    def enableButtons3( self, index1, index2 ):
        self.enableButtons()

    def enableButtons2( self, text ):
        self.enableButtons()

    def enableButtons( self ):
        isDirty = self.isDirty()
        isValid = False
        if isDirty:
            isValid = self.checkIsValid()
        canCancel = isDirty or (self.uListView.rowCount() > 0 and self._editItem == None)
        self.uNew.setEnabled( not isDirty and self._editItem != None )
        self.uDelete.setEnabled( not isDirty )
        self.uSave.setEnabled( isValid )
        self.uCancel.setEnabled( canCancel ) 
        if not self._editItem:
            self.uEditStatus.setText("Create a new source type")
        elif isDirty:
            self.uEditStatus.setText("Editing " + self._editItem['name'])
        else:
            self.uEditStatus.setText("")

    def newItemClicked( self ):
        if not self.canChangeItem():
            return
        self.clearItem()

    def deleteItemClicked( self ):
        if self.isDirty():
            return
        self.deleteItem( self.uListView.selectedItem())
        self._controller.updateSourceTypes()

    def cancelItemClicked( self ):
        self.loadItem( self.uListView.selectedItem())

    def saveItemClicked( self ):
        if not self.isDirty():
            return
        self.saveItem()

    def selectItem( self, rowno ):
        if not self.canChangeItem():
            return
        item = self.uListView.selectedItem()
        self.loadItem( item )

    def canChangeItem(self):
        if not self.isDirty():
            return True
        result = QMessageBox.question(self,"Save source type",
                "Do you want to save the changes\n" +
                "Select yes to save, no to discard edits,  or cancel to continue editing",
                QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel )
        if result == QMessageBox.Cancel:
            return False
        if result == QMessageBox.Yes:
            return self.saveItem()
        return True

#-------------------------------------------------

    def load( self ):
        self._readerClasses[:] = []
        for sc in Reader.__subclasses__():
            self._readerClasses.append(
                dict(name=sc.__name__,params=sc.params())
                )
        self._readerClasses.sort( key=lambda x: x['name'] )
        for i, c in enumerate( self._readerClasses ):
            self.uReaderClass.addNameValue( c['name'], i )

        for r in Database.execute('select srid, name from elc_getSpatialReferenceSystems() order by name'):
            self.uCoordSys.addNameValue(r[1],r[0])

        self.loadList()

    def setReaderClass( self, row ):
        nreader = self.uReaderClass.selectedValue()
        if nreader != None:
            reader = self._readerClasses[nreader]
            prms = reader['params']
            for p in prms:
                p['required'] = p.get('required',False)
                p['value'] = ''
                p['startvalue'] = ''
                p['label'] = p['name'] + ('*' if p['required'] else '')
            self._params.setList(prms)

    def showParamDesc( self, row ):
        item = self.uParameterList.selectedItem()
        if item:
            self.uParamDesc.setText( item['description'] )
        else:
            self.uParamDesc.setText('')

    def loadList( self ):
        self.uListModel.setList( ReaderType.list())

    def setListColumns( self, listModel ):
        listModel.setColumns(['id','name','readerclass'],['Id','Name','Type'])

    def clearParams( self ):
        for i,p in enumerate(self._params.list()):
            p['value'] = ''
            p['startvalue'] = ''
            self._params.updateItem(i)

    def clearItem( self ):
        self._editItem = None
        self.uName.setText('')
        self.uReaderClass.setCurrentIndex(0)
        self.uFileExtension.setText('')
        self.clearParams()

    def loadItem( self, item ):
        if not item:
            return
        self._editItem = item
        self.uName.setText( item['name'] )
        for i in range( len( self._readerClasses ) ):
            if self._readerClasses[i]['name'] == item['readerclass']:
                self.uReaderClass.selectValue(i)
                break
        self.uFileExtension.setText(item['file_ext'])
        self.uCoordSys.selectValue( item['srs_id'])
        self.clearParams()
        for stp in item['params']:
            for i,prm in enumerate(self._params.list()):
                if prm['name'] == stp['name']:
                    prm['value'] = stp['value']
                    prm['startvalue'] = stp['value']
                    self._params.updateItem(i)

    @attempt( default = False )
    def saveItem( self ):
        if not self.checkIsValid():
            return False
        params = ReaderType.encodeParams( self._params.list())
        if self._editItem: 
            id = Database.executeScalar('elc_UpdateSourceType',
                             self._editItem['id'],
                             str(self.uName.text()),
                             str(self.uFileExtension.text()),
                             self.uCoordSys.selectedValue(),
                             str(self.uReaderClass.currentText()),
                             params)
        else:
            id = Database.executeScalar('elc_CreateSourceType',
                             str(self.uName.text()),
                             str(self.uFileExtension.text()),
                             self.uCoordSys.selectedValue(),
                             str(self.uReaderClass.currentText()),
                             params)
        self.clearItem()
        ReaderType.reload()
        self._controller.updateSourceTypes()
        self.uListView.selectId( id )
        return True

    @attempt
    def deleteItem( self, item ):
        if not self._editItem:
            return
        if QMessageBox.question(
            self,
            "Delete source type",
            "Are you sure you want to remove the source type " + self._editItem['name'] + '?', 
            QMessageBox.Yes | QMessageBox.No) != QMessageBox.Yes:
            return
        Database.execute('elc_DeleteSourceType',self._editItem['id'])
        ReaderType.reload()
        self._controller.updateSourceTypes()

    def paramsDirty( self ):
        for p in self._params.list():
            if p['value'] != p['startvalue']: return True
        return False

    def isDirty( self ):
        if self._editItem:
            if str(self.uName.text()) != self._editItem['name']: return True
            if str(self.uFileExtension.text()) != self._editItem['file_ext']: return True
            if str(self.uReaderClass.currentText()) != self._editItem['readerclass']: return True
            if self.uCoordSys.selectedValue() != self._editItem['srs_id']: return True
        else:
            if str(self.uName.text()) != '': return True
            if str(self.uFileExtension.text()) != '': return True

        if self.paramsDirty(): return True
        return False

    def paramsValid( self ):
        for p in self._params.list():
            if 'required' in p and p['required'] and not p['value']:
                return False
        return True

    def checkIsValid( self ):
        return (
            str(self.uName.text()) != '' and 
            str(self.uFileExtension.text()) != '' and 
            self.uReaderClass.selectedValue() != None and
            self.uCoordSys.selectedValue() != None and
            self.paramsValid()
            )