def startDM(self): """ Start running the DM observer thread and Listener (of the DM observer) thread when the plugin is enabled """ self.dm = DataManager() # common data obj self.DMData = DMData() dmObserver = DMObserver(self.DMData, self.dm) listener = Listener(self.DMData) self.connect(listener, SIGNAL('dataChanged'), self.dataUpdated) #### listener.start() dmObserver.start() uilog.info('dm started')
class UiDataManager(QObject): """ Handle all interaction between the UI and Data Manager. Also stores data as formatted for the UI @param QObject: inherits from QObject Class @type QObject: QObject """ rDataChangedSignal = pyqtSignal() fDataChangedSignal = pyqtSignal() #logging global uilog uilog = Logger.setup(lf='uiLog') def __init__(self, iface, controller): """ Initialise the UI Data Manager @param iface: QgisInterface Abstract base class defining interfaces exposed by QgisApp @type iface: Qgisinterface Object @param controller: instance of the plugins controller @type controller: AimsUI.AimsClient.Gui.Controller """ QObject.__init__(self) self._controller = controller self._iface = iface self.dm = None self._observers = [] self.data = { FEEDS['AF']: {}, FEEDS['AC']: {}, FEEDS['AR']: {}, FEEDS['GC']: {}, FEEDS['GR']: {} } self.groups = ('Replace', 'AddLineage', 'ParcelReferenceData' ) # more to come... self.rDataChangedSignal.connect(self._controller.rDataChanged) self.fDataChangedSignal.connect(self._controller.fDataChanged) def startDM(self): """ Start running the DM observer thread and Listener (of the DM observer) thread when the plugin is enabled """ self.dm = DataManager() # common data obj self.DMData = DMData() dmObserver = DMObserver(self.DMData, self.dm) listener = Listener(self.DMData) self.connect(listener, SIGNAL('dataChanged'), self.dataUpdated) #### listener.start() dmObserver.start() uilog.info('dm started') def killDm(self): """ Close DataManager at plugin unload """ if self.dm: self.dm.close() ### Observer Methods ### def register(self, observer): """ Listeners of the UIDataManager to regiester themselves to this method @param observer: listerning class @type observer: AIMS class objects wishing to listen """ self._observers.append(observer) @pyqtSlot() def dataUpdated(self, data=None, feedType=FEEDS['AR']): """ Slot communicated to when Review data changed. Updates review layer and table data @param data: list of AIMS objects related for feed (as communicated in param feedtype) @type data: list @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef """ self.setData(data, feedType) for observer in self._observers: observer.notify(feedType) def exlopdeGroup(self): """ key groups agianst the group id and groups of single features against each group resultant format == {(groupId, groupObj): {addId: addObj, addId: addObj}, (gro...}} """ gDict = {} for gId, gFeats in self.data[FEEDS['GR']].items(): fDict = {} for gFeat in gFeats.meta._entities: fDict[gFeat._changeId] = gFeat gDict[(gId, gFeats)] = fDict self.data[FEEDS['GR']] = gDict def idProperty(self, feedtype): """ Returns the property that the each object should derive its reference id from @return: Reference to which id should be used to reference each feedtype @rtype: string """ if feedtype == FEEDS['AF']: return '_components_addressId' if feedtype == FEEDS['AR']: return '_changeId' if feedtype == FEEDS['GR']: return '_changeGroupId' def keyData(self, listofFeatures, feedtype): """ Key Data from Data Manager @param dataRefresh: list of AIMS objects related for feed (as communicated in param feedtype) @type dataRefresh: list @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef """ ### was if listofFeatures: but stop the queue being emptied to None if listofFeatures or listofFeatures == []: li = [] keyId = self.idProperty(feedtype) li = dict((getattr(feat, keyId), feat) for feat in listofFeatures) self.data[feedtype] = li # [GroupKey:{AdKey:}] if feedtype == FEEDS['GR']: # key group objects self.exlopdeGroup() def setData(self, dataRefresh, FeedType): # redundant? now straight to keyData? """ Method receives new data from the data manager via the UIDM observer pattern and then starts the data update process @param dataRefresh: list of AIMS objects related for feed (as communicated in param feedtype) @type dataRefresh: list @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef """ self.keyData(dataRefresh, FeedType) def updateRdata(self, respFeature, feedType): """ Between dm threaded interval data deliveries, temp AIMS review objects are created or render irrelevant by user actions. This method updates the main data (self._data) to reflect these changes. @param respFeature: Aims Address object @type respFeature: AIMSDataManager.Address @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef """ # remove from data if respFeature._queueStatus in ('Declined', 'Accepted'): del self.data[FEEDS['AR']][respFeature._changeId] else: # add to data self.data[FEEDS['AR']][respFeature._changeId] = respFeature #uilog.info('new AR record with changeid: {}'.format(respFeature._changeId)) self.rDataChangedSignal.emit() def updateFdata(self, respFeature): """ Between dm threaded interval data deliveries, temp AIMS feature objects are created or render irrelevant by user actions. This method updates the main data (self._data) to reflect these changes. @param respFeature: Aims Address object @type respFeature: AIMSDataManager.Address """ # responses do not have a 'full number' # as it is required for labeling it is set here respFeature.setFullAddressNumber(respFeature.getFullNumber()) self.data[FEEDS['AF']][respFeature._components_addressId] = respFeature self.fDataChangedSignal.emit() def updateGdata(self, respFeature): groupKey = self.matchGroupKey(respFeature._changeGroupId) self.data[FEEDS['GR']][groupKey][respFeature._changeId] = respFeature self.rDataChangedSignal.emit() def matchGroupKey(self, groupId): for groupKey in self.data.get(FEEDS['GR']).keys(): if groupId in groupKey: return groupKey def setBbox(self, sw, ne): """ Intermediate method, passes bboxes from layer manager to the DM @param sw: (x ,y) @type sw: tuple @param ne: (x ,y) @type ne: tuple """ #logging uilog.info('*** BBOX *** New bbox passed to dm.setbb') self.dm.setbb(sw, ne) uilog.info('*** BBOX *** Bbox set') def reviewData(self): """ Returns current group review data @return: Dictionary of group formatted review data @rtype: dictionary """ return self.data.get(FEEDS['AR']) def groupReviewData(self): ''' return (single and group) review data ''' return self.data.get(FEEDS['GR']) def combinedReviewData(self): """ De-nests group review data and combines with standard review data, Returning a complete "Review Data" set @return: Flat dictionary of review items @rtype: dictionary """ groupData = self.groupReviewData() addData = self.reviewData() combinedData = {} combinedData.update(addData) if not groupData: return addData for k, v in groupData.items(): combinedData.update(v) return combinedData def featureData(self): """ Returns feature data return: Dict of aims features {featureID: address obj,...} rtype: dictionary """ return self.data.get(FEEDS['AF']) # --- DM convenience methods--- def addAddress(self, feature, respId=None): """ Passes an new AIMS Feature to DataManager to add the feature to the AIM system @param feature: Aims Address object @type feature: AIMSDataManager.Address @param respId: id used to match response @type respId: integer """ uilog.info( 'obj with respId: {0} passed to convenience method "{1}" '.format( respId, 'addAddress')) self.dm.addAddress(feature, respId) def retireAddress(self, feature, respId=None): """ Passes an AIMS Feature to DataManager for retirement @param feature: Aims Address object @type feature: AIMSDataManager.Address @param respId: id used to match response @type respId: integer """ uilog.info( 'obj with respId: {0} passed to convenience method "{1}" '.format( respId, 'retireAddress')) self.dm.retireAddress(feature, respId) def updateAddress(self, feature, respId=None): """ Passes an AIMS Feature to the DataManager to update a published feature @param feature: Aims Address object @type feature: AIMSDataManager.Address @param respId: id used to match response @type respId: integer """ uilog.info( 'obj with respId: {0} passed to convenience method "{1}" '.format( respId, 'updateAddress')) self.dm.updateAddress(feature, respId) def decline(self, feature, feedType, respId=None): """ Passes an AIMS Feature to the DataManager to decline a review item @param feature: Aims Address object @type feature: AIMSDataManager.Address @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @param respId: id used to match response @type respId: integer """ uilog.info( 'obj with respId: {0} passed to convenience method "{1}" '.format( respId, 'declineAddress')) if feedType == FEEDS['AR']: self.dm.declineAddress(feature, respId) else: self.dm.declineGroup(feature, respId) def accept(self, feature, feedType, respId=None): """ Passes an AIMS Feature to the DataManager to accept a review item @param feature: Aims Address object @type feature: AIMSDataManager.Address @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @param respId: id used to match response @type respId: integer """ if respId: uilog.info( 'obj with respId: {0} passed to convenience method "{1}" '. format(respId, 'acceptAddress')) if feedType == FEEDS['AR']: self.dm.acceptAddress(feature, respId) else: self.dm.acceptGroup(feature, respId) def repairAddress(self, feature, respId=None): """ Passes an updated AIMS Review Feature to the DataManager to be updated on the review feed @param feature: Aims Address object @type feature: AIMSDataManager.Address @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @param respId: id used to match response @type respId: integer """ uilog.info( 'obj with respId: {0} passed to convenience method "{1}" '.format( respId, 'repairAddress')) self.dm.repairAddress(feature, respId) def supplementAddress(self, feature, reqid=None): """ Retrieve properties missing on the feature feed from the last relevant resolution feed feature @param feature: Feature feed item that require supplement data @type feature: AIMSDataManager.Address @param respId: id used to match response @type respId: integer """ self.dm.supplementAddress(feature, reqid) #--- Groups DM Methods --- def repairGroup(self, feature, respId=None): """ Passes an updated AIMS Review Feature to the DataManager to be updated on the review feed @param feature: Aims Address object @type feature: AIMSDataManager.Address @param respId: id used to match response @type respId: integer """ uilog.info( 'obj with respId: {0} passed to convenience method "{1}" '.format( respId, 'repairAddress')) self.dm.repairGroup(feature, respId) # Lineage Related - ON HOLD # def openGroup(self): # self.dm.replaceGroup() # # def updateGroup(self): # self.dm.updateGroup() # # def submitGroup(self): # self.dm.submitGroup() # # def closeGroup(self): # self.dm.closeGroup() # # def addGroup(self): # self.dm.addGroup() # # def removeGroup(self): # self.dm.removeGroup() # def response(self, feedtype=None): """ Returns DataMAnager response for a specific feedtype @param feedType: Type of AIMS feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @return: tuple of AIMSDataManager.Address @rtype: tuple """ return self.dm.response(feedtype) def isNested(self, feat, prop): """ Test if the class object has said nested entity @param feat: Group object @type feat: AIMSDataManager.Address @param prop: address property @type prop: string """ try: return hasattr( getattr(getattr(feat, 'meta'), '_entities')[0], prop) except: return False def nestedEntities(self, feat, prop): """ Returns property from nested object @param feat: Group object @type feat: AIMSDataManager.Address @param prop: address property @type prop: string @return: AIMSDataManager.Address property @rtype: AIMSDataManager.Address property """ return getattr(getattr(getattr(feat, 'meta'), '_entities')[0], prop) def flatEntities(self, feat, prop): """ Returns property from flat object @param feat: Group object @type feat: AIMSDataManager.Address @param prop: address property @type prop: string @return: AIMSDataManager.Address property @rtype: AIMSDataManager.Address property """ att = getattr(feat, prop) return att if att else '' def fullRoad(self, feat, feedtype): """ Compiles a full road name 'label' for the UI @param feat: Group object @type feat: AIMSDataManager.Address @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @return: Individual road components concatenated @rtype: string """ fullRoad = '' for prop in [ '_components_roadPrefix', '_components_roadName', '_components_roadType', '_components_roadSuffix', '_components_waterRoute' ]: addProp = None # Groups have nested entities if feat._changeType in self.groups: if self.isNested(feat, prop): addProp = self.nestedEntities(feat, prop) # retired have nested entities except when the retired # feature is derived from an response object elif feat._changeType == 'Retire': #if self.isNested(feat, prop): if not feat.meta.requestId: if hasattr( getattr(getattr(feat, 'meta'), '_entities')[0], prop): addProp = self.nestedEntities(feat, prop) elif hasattr(feat, prop): addProp = self.flatEntities(feat, prop) # else we have an Add or update of whoms # properties are flat elif hasattr(feat, prop): addProp = self.flatEntities(feat, prop) else: continue if addProp != None: fullRoad += addProp + ' ' return fullRoad.lstrip() def formatGroupTableData(self, obj, groupProperties): """ Returns data formatted for the group table model @param obj: Group object @type obj: AIMSDataManager.Group.GroupResolution @param groupProperties: list of properties required to format table data @type groupProperties: list @return: return list reperesting a group entity. Formatted: ['changeGroupId', 'groupType', 'workflow_sourceOrganisation', 'submitterUserName', 'submittedDate'] @rtype: list """ groupValues = [] for prop in groupProperties: if hasattr(obj, prop): if getattr(obj, prop) != None: groupValues.append(getattr(obj, prop)) #continue else: groupValues.append('') return groupValues def iterFeatProps(self, feat, featProperties, feedtype): """ Iterate over AIMS class objects, Return those relevant to the parent model (that related to the feedtype) @param feat: Address object @type feat: AIMSDataManager.Address @param featProperties: List of properties required to format table data @type featProperties: list @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @return: List of properties as formatted for relevant data model @rtype: list """ fValues = [] fValues.extend([ getattr(feat, '_changeId'), feat.getFullNumber(), self.fullRoad(feat, feedtype) ]) # address and road labels for prop in featProperties: # Groups have nested entities if feat._changeType in self.groups and self.isNested(feat, prop): fValues.append(self.nestedEntities(feat, prop)) # retired have nested entities except when the retired # feature is derived from an response object elif feat._changeType == 'Retire': if self.isNested(feat, prop): fValues.append(self.nestedEntities(feat, prop)) elif hasattr(feat, prop): fValues.append(self.flatEntities(feat, prop)) # else we have an Add or update of whoms # properties are flat elif hasattr(feat, prop): fValues.append(self.flatEntities(feat, prop)) #!= None: else: fValues.append('') return fValues def formatFeatureTableData(self, feat, featProperties, feedtype): """ Returns data formatted for the feature table model @param feature: Aims Address object @type feature: dictionary @param featProperties: List of Address Properties required format Feature Table Data @type featProperties: list @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @return: List representing on row of feature table data @rtype: list """ if feedtype == FEEDS['AR']: return self.iterFeatProps(feat, featProperties, feedtype) else: fValuesList = [] # AR for f in feat.values(): fValues = self.iterFeatProps(f, featProperties, feedtype) fValuesList.append(fValues) return fValuesList def addClassProps(self, feedtype): """ Properties required to format Group and Feature data for the respective table models @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @return: Tuple of properties required to format the data related to the feedtype @rtype: tuple """ prop = { 'AR': { 'kProperties': [ '_changeId', '_changeType', '_workflow_sourceOrganisation', '_workflow_submitterUserName', '_workflow_submittedDate' ], 'vProperties': [ '_components_lifecycle', '_components_townCity', '_components_suburbLocality' ] }, 'GR': { 'kProperties': [ '_changeGroupId', '_groupType', '_workflow_sourceOrganisation', '_submitterUserName', '_submittedDate' ], 'vProperties': [ '_components_lifecycle', '_components_townCity', '_components_suburbLocality' ] } } if feedtype == FEEDS['AR']: return (prop['AR']['kProperties'], prop['AR']['vProperties']) return (prop['GR']['kProperties'], prop['AR']['vProperties']) def formatTableData(self, feedtypes): """ Returns review data as formatted for the review data model @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @return: data formated for feature table model @rtype: dictionary """ fData = {} for feedtype in feedtypes: if self.data[feedtype]: props = self.addClassProps(feedtype) kProperties = props[0] vProperties = props[1] for k, v in self.data.get(feedtype).items(): if feedtype == FEEDS['AR']: if v._queueStatus in ('Declined', 'Accepted'): continue featureValues = [] if feedtype == FEEDS['AR']: groupValues = self.formatGroupTableData(v, kProperties) featureValues = [ self.formatFeatureTableData( v, vProperties, feedtype) ] else: #GR featureValues = [] groupValues = self.formatGroupTableData( k[1], kProperties) featureValues = self.formatFeatureTableData( v, vProperties, feedtype) fData[tuple(groupValues)] = featureValues if fData: return fData else: return {('', '', '', '', ''): [['', '', '', '', '']]} def singleFeatureObj(self, objkey): """ Returns a AIMS Address object that matches the object key @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @param objkey: Feautre id @type objkey: integer """ return self.data.get(FEEDS['AF'])[(objkey)] def singleReviewObj(self, feedtype, objkey): ''' return the value of which is an aims review obj (group and single) for the keyed data ''' if feedtype == FEEDS['AR']: return self.data.get(feedtype).get(objkey) elif feedtype == FEEDS['GR']: for k in self.data.get(feedtype): if objkey == k[0]: return k[1] def currentReviewFeature(self, currentGroup, currentFeatureKey): """ Returns aims feautre object as per supplied data key(s) @param currentGroup: Current group key (groupId, changeType) @type currentGroup: tuple @param currentFeatureKey: Current Feautre id @type currentFeatureKey: integer @return: Address object @rtype: AIMSDataManager.Address """ if currentGroup[1] not in ('Add', 'Update', 'Retire'): for group in self.data.get(FEEDS['GR']).values(): if group.has_key(currentFeatureKey): return group[currentFeatureKey] else: return self.data.get(FEEDS['AR']).get(currentFeatureKey) def reviewItemCoords(self, currentGroup, currentFeatureKey): """ Returns the coords of a review obj @param currentGroup: Current group key (groupId, changeType) @type currentGroup: tuple @param currentFeatureKey: Current Feautre id @type currentFeatureKey: integer @return: list [x,y] @rtype: list """ obj = self.currentReviewFeature(currentGroup, currentFeatureKey) if not obj: return None if obj._changeType not in ('Update', 'Add') and not obj.meta.requestId: pos = obj.meta.entities[0].getAddressPositions( )[0]._position_coordinates else: pos = obj.getAddressPositions()[0]._position_coordinates return pos
class UiDataManager(QObject): """ Handle all interaction between the UI and Data Manager. Also stores data as formatted for the UI @param QObject: inherits from QObject Class @type QObject: QObject """ rDataChangedSignal = pyqtSignal() fDataChangedSignal = pyqtSignal() #logging global uilog uilog = Logger.setup(lf='uiLog') def __init__(self, iface, controller): """ Initialise the UI Data Manager @param iface: QgisInterface Abstract base class defining interfaces exposed by QgisApp @type iface: Qgisinterface Object @param controller: instance of the plugins controller @type controller: AimsUI.AimsClient.Gui.Controller """ QObject.__init__(self) self._controller = controller self._iface = iface self.dm = None self._observers = [] self.data = { FEEDS['AF']:{}, FEEDS['AC']:{}, FEEDS['AR']:{}, FEEDS['GC']:{}, FEEDS['GR']:{} } self.groups = ('Replace', 'AddLineage', 'ParcelReferenceData') # more to come... self.rDataChangedSignal.connect(self._controller.rDataChanged) self.fDataChangedSignal.connect(self._controller.fDataChanged) def startDM(self): """ Start running the DM observer thread and Listener (of the DM observer) thread when the plugin is enabled """ self.dm = DataManager() # common data obj self.DMData = DMData() dmObserver = DMObserver(self.DMData, self.dm) listener = Listener(self.DMData) self.connect(listener, SIGNAL('dataChanged'), self.dataUpdated) #### listener.start() dmObserver.start() uilog.info('dm started') def killDm(self): """ Close DataManager at plugin unload """ if self.dm: self.dm.close() ### Observer Methods ### def register(self, observer): """ Listeners of the UIDataManager to regiester themselves to this method @param observer: listerning class @type observer: AIMS class objects wishing to listen """ self._observers.append(observer) @pyqtSlot() def dataUpdated(self, data = None, feedType = FEEDS['AR']): """ Slot communicated to when Review data changed. Updates review layer and table data @param data: list of AIMS objects related for feed (as communicated in param feedtype) @type data: list @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef """ self.setData(data,feedType) for observer in self._observers: observer.notify(feedType) def exlopdeGroup(self): """ key groups agianst the group id and groups of single features against each group resultant format == {(groupId, groupObj): {addId: addObj, addId: addObj}, (gro...}} """ gDict = {} for gId, gFeats in self.data[FEEDS['GR']].items(): fDict = {} for gFeat in gFeats.meta._entities: fDict[gFeat._changeId]=gFeat gDict[(gId, gFeats)]=fDict self.data[FEEDS['GR']] = gDict def idProperty(self, feedtype): """ Returns the property that the each object should derive its reference id from @return: Reference to which id should be used to reference each feedtype @rtype: string """ if feedtype == FEEDS['AF']: return '_components_addressId' if feedtype == FEEDS['AR']: return '_changeId' if feedtype == FEEDS['GR']: return '_changeGroupId' def keyData(self, listofFeatures, feedtype): """ Key Data from Data Manager @param dataRefresh: list of AIMS objects related for feed (as communicated in param feedtype) @type dataRefresh: list @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef """ ### was if listofFeatures: but stop the queue being emptied to None if listofFeatures or listofFeatures == []: li = [] keyId = self.idProperty(feedtype) li = dict((getattr(feat, keyId), feat) for feat in listofFeatures) self.data[feedtype] = li # [GroupKey:{AdKey:}] if feedtype == FEEDS['GR']: # key group objects self.exlopdeGroup() def setData(self, dataRefresh, FeedType): # redundant? now straight to keyData? """ Method receives new data from the data manager via the UIDM observer pattern and then starts the data update process @param dataRefresh: list of AIMS objects related for feed (as communicated in param feedtype) @type dataRefresh: list @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef """ self.keyData(dataRefresh, FeedType) def updateRdata(self, respFeature, feedType): """ Between dm threaded interval data deliveries, temp AIMS review objects are created or render irrelevant by user actions. This method updates the main data (self._data) to reflect these changes. @param respFeature: Aims Address object @type respFeature: AIMSDataManager.Address @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef """ # remove from data if respFeature._queueStatus in ('Declined', 'Accepted'): del self.data[FEEDS['AR']][respFeature._changeId] else: # add to data self.data[FEEDS['AR']][respFeature._changeId] = respFeature #uilog.info('new AR record with changeid: {}'.format(respFeature._changeId)) self.rDataChangedSignal.emit() def updateFdata(self, respFeature): """ Between dm threaded interval data deliveries, temp AIMS feature objects are created or render irrelevant by user actions. This method updates the main data (self._data) to reflect these changes. @param respFeature: Aims Address object @type respFeature: AIMSDataManager.Address """ # responses do not have a 'full number' # as it is required for labeling it is set here respFeature.setFullAddressNumber(respFeature.getFullNumber()) self.data[FEEDS['AF']][respFeature._components_addressId] = respFeature self.fDataChangedSignal.emit() def updateGdata(self, respFeature): groupKey = self.matchGroupKey(respFeature._changeGroupId) self.data[FEEDS['GR']][groupKey][respFeature._changeId] = respFeature self.rDataChangedSignal.emit() def matchGroupKey(self, groupId): for groupKey in self.data.get(FEEDS['GR']).keys(): if groupId in groupKey: return groupKey def setBbox(self, sw, ne): """ Intermediate method, passes bboxes from layer manager to the DM @param sw: (x ,y) @type sw: tuple @param ne: (x ,y) @type ne: tuple """ #logging uilog.info('*** BBOX *** New bbox passed to dm.setbb') self.dm.setbb(sw, ne) uilog.info('*** BBOX *** Bbox set') def reviewData(self): """ Returns current group review data @return: Dictionary of group formatted review data @rtype: dictionary """ return self.data.get(FEEDS['AR']) def groupReviewData(self): ''' return (single and group) review data ''' return self.data.get(FEEDS['GR']) def combinedReviewData(self): """ De-nests group review data and combines with standard review data, Returning a complete "Review Data" set @return: Flat dictionary of review items @rtype: dictionary """ groupData = self.groupReviewData() addData = self.reviewData() combinedData = {} combinedData.update(addData) if not groupData: return addData for k ,v in groupData.items(): combinedData.update(v) return combinedData def featureData(self): """ Returns feature data return: Dict of aims features {featureID: address obj,...} rtype: dictionary """ return self.data.get(FEEDS['AF']) # --- DM convenience methods--- def addAddress(self, feature, respId = None): """ Passes an new AIMS Feature to DataManager to add the feature to the AIM system @param feature: Aims Address object @type feature: AIMSDataManager.Address @param respId: id used to match response @type respId: integer """ uilog.info('obj with respId: {0} passed to convenience method "{1}" '.format(respId, 'addAddress')) self.dm.addAddress(feature, respId) def retireAddress(self, feature, respId = None): """ Passes an AIMS Feature to DataManager for retirement @param feature: Aims Address object @type feature: AIMSDataManager.Address @param respId: id used to match response @type respId: integer """ uilog.info('obj with respId: {0} passed to convenience method "{1}" '.format(respId, 'retireAddress')) self.dm.retireAddress(feature, respId) def updateAddress(self, feature, respId = None): """ Passes an AIMS Feature to the DataManager to update a published feature @param feature: Aims Address object @type feature: AIMSDataManager.Address @param respId: id used to match response @type respId: integer """ uilog.info('obj with respId: {0} passed to convenience method "{1}" '.format(respId, 'updateAddress')) self.dm.updateAddress(feature, respId) def decline(self, feature, feedType, respId = None): """ Passes an AIMS Feature to the DataManager to decline a review item @param feature: Aims Address object @type feature: AIMSDataManager.Address @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @param respId: id used to match response @type respId: integer """ uilog.info('obj with respId: {0} passed to convenience method "{1}" '.format(respId, 'declineAddress')) if feedType == FEEDS['AR']: self.dm.declineAddress(feature, respId) else: self.dm.declineGroup(feature, respId) def accept(self, feature, feedType, respId = None): """ Passes an AIMS Feature to the DataManager to accept a review item @param feature: Aims Address object @type feature: AIMSDataManager.Address @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @param respId: id used to match response @type respId: integer """ if respId: uilog.info('obj with respId: {0} passed to convenience method "{1}" '.format(respId, 'acceptAddress')) if feedType == FEEDS['AR']: self.dm.acceptAddress(feature, respId) else: self.dm.acceptGroup(feature, respId) def repairAddress(self, feature, respId = None): """ Passes an updated AIMS Review Feature to the DataManager to be updated on the review feed @param feature: Aims Address object @type feature: AIMSDataManager.Address @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @param respId: id used to match response @type respId: integer """ uilog.info('obj with respId: {0} passed to convenience method "{1}" '.format(respId, 'repairAddress')) self.dm.repairAddress(feature, respId) def supplementAddress(self, feature, reqid=None): """ Retrieve properties missing on the feature feed from the last relevant resolution feed feature @param feature: Feature feed item that require supplement data @type feature: AIMSDataManager.Address @param respId: id used to match response @type respId: integer """ self.dm.supplementAddress(feature, reqid) #--- Groups DM Methods --- def repairGroup(self, feature, respId = None): """ Passes an updated AIMS Review Feature to the DataManager to be updated on the review feed @param feature: Aims Address object @type feature: AIMSDataManager.Address @param respId: id used to match response @type respId: integer """ uilog.info('obj with respId: {0} passed to convenience method "{1}" '.format(respId, 'repairAddress')) self.dm.repairGroup(feature, respId) # Lineage Related - ON HOLD # def openGroup(self): # self.dm.replaceGroup() # # def updateGroup(self): # self.dm.updateGroup() # # def submitGroup(self): # self.dm.submitGroup() # # def closeGroup(self): # self.dm.closeGroup() # # def addGroup(self): # self.dm.addGroup() # # def removeGroup(self): # self.dm.removeGroup() # def response (self, feedtype = None): """ Returns DataMAnager response for a specific feedtype @param feedType: Type of AIMS feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @return: tuple of AIMSDataManager.Address @rtype: tuple """ return self.dm.response(feedtype) def isNested(self, feat, prop): """ Test if the class object has said nested entity @param feat: Group object @type feat: AIMSDataManager.Address @param prop: address property @type prop: string """ try: return hasattr(getattr(getattr(feat, 'meta'), '_entities')[0],prop) except: return False def nestedEntities(self, feat, prop): """ Returns property from nested object @param feat: Group object @type feat: AIMSDataManager.Address @param prop: address property @type prop: string @return: AIMSDataManager.Address property @rtype: AIMSDataManager.Address property """ return getattr(getattr(getattr(feat, 'meta'), '_entities')[0],prop) def flatEntities(self, feat, prop): """ Returns property from flat object @param feat: Group object @type feat: AIMSDataManager.Address @param prop: address property @type prop: string @return: AIMSDataManager.Address property @rtype: AIMSDataManager.Address property """ att = getattr(feat,prop) return att if att else '' def fullRoad(self, feat, feedtype): """ Compiles a full road name 'label' for the UI @param feat: Group object @type feat: AIMSDataManager.Address @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @return: Individual road components concatenated @rtype: string """ fullRoad = '' for prop in ['_components_roadPrefix', '_components_roadName', '_components_roadType', '_components_roadSuffix', '_components_waterRoute']: addProp = None # Groups have nested entities if feat._changeType in self.groups: if self.isNested(feat, prop): addProp = self.nestedEntities(feat, prop) # retired have nested entities except when the retired # feature is derived from an response object elif feat._changeType == 'Retire': #if self.isNested(feat, prop): if not feat.meta.requestId: if hasattr(getattr(getattr(feat, 'meta'), '_entities')[0],prop): addProp = self.nestedEntities(feat, prop) elif hasattr(feat,prop): addProp = self.flatEntities(feat, prop) # else we have an Add or update of whoms # properties are flat elif hasattr(feat,prop): addProp = self.flatEntities(feat, prop) else: continue if addProp != None: fullRoad+=addProp+' ' return fullRoad.lstrip() def formatGroupTableData(self, obj, groupProperties): """ Returns data formatted for the group table model @param obj: Group object @type obj: AIMSDataManager.Group.GroupResolution @param groupProperties: list of properties required to format table data @type groupProperties: list @return: return list reperesting a group entity. Formatted: ['changeGroupId', 'groupType', 'workflow_sourceOrganisation', 'submitterUserName', 'submittedDate'] @rtype: list """ groupValues = [] for prop in groupProperties: if hasattr(obj, prop): if getattr(obj, prop) != None: groupValues.append(getattr(obj, prop)) #continue else: groupValues.append('') return groupValues def iterFeatProps(self, feat, featProperties, feedtype): """ Iterate over AIMS class objects, Return those relevant to the parent model (that related to the feedtype) @param feat: Address object @type feat: AIMSDataManager.Address @param featProperties: List of properties required to format table data @type featProperties: list @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @return: List of properties as formatted for relevant data model @rtype: list """ fValues = [] fValues.extend([getattr(feat, '_changeId'),feat.getFullNumber(), self.fullRoad(feat ,feedtype)]) # address and road labels for prop in featProperties: # Groups have nested entities if feat._changeType in self.groups and self.isNested(feat, prop): fValues.append(self.nestedEntities(feat, prop)) # retired have nested entities except when the retired # feature is derived from an response object elif feat._changeType == 'Retire': if self.isNested(feat, prop): fValues.append(self.nestedEntities(feat, prop)) elif hasattr(feat,prop): fValues.append(self.flatEntities(feat, prop)) # else we have an Add or update of whoms # properties are flat elif hasattr(feat,prop): fValues.append(self.flatEntities(feat, prop)) #!= None: else: fValues.append('') return fValues def formatFeatureTableData(self, feat, featProperties, feedtype): """ Returns data formatted for the feature table model @param feature: Aims Address object @type feature: dictionary @param featProperties: List of Address Properties required format Feature Table Data @type featProperties: list @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @return: List representing on row of feature table data @rtype: list """ if feedtype == FEEDS['AR']: return self.iterFeatProps(feat, featProperties, feedtype) else: fValuesList = [] # AR for f in feat.values(): fValues = self.iterFeatProps(f, featProperties, feedtype) fValuesList.append(fValues) return fValuesList def addClassProps(self, feedtype): """ Properties required to format Group and Feature data for the respective table models @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @return: Tuple of properties required to format the data related to the feedtype @rtype: tuple """ prop = {'AR':{'kProperties' : ['_changeId', '_changeType', '_workflow_sourceOrganisation', '_workflow_submitterUserName', '_workflow_submittedDate'], 'vProperties' : ['_components_lifecycle', '_components_townCity' , '_components_suburbLocality']}, 'GR':{'kProperties' : ['_changeGroupId', '_groupType', '_workflow_sourceOrganisation', '_submitterUserName', '_submittedDate'], 'vProperties' : ['_components_lifecycle', '_components_townCity' , '_components_suburbLocality']}} if feedtype == FEEDS['AR']: return (prop['AR']['kProperties'],prop['AR']['vProperties']) return (prop['GR']['kProperties'],prop['AR']['vProperties']) def formatTableData(self, feedtypes): """ Returns review data as formatted for the review data model @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @return: data formated for feature table model @rtype: dictionary """ fData = {} for feedtype in feedtypes: if self.data[feedtype]: props = self.addClassProps(feedtype) kProperties = props[0] vProperties = props[1] for k, v in self.data.get(feedtype).items(): if feedtype == FEEDS['AR']: if v._queueStatus in ('Declined', 'Accepted'): continue featureValues = [] if feedtype == FEEDS['AR']: groupValues = self.formatGroupTableData(v,kProperties) featureValues = [self.formatFeatureTableData(v,vProperties, feedtype)] else: #GR featureValues = [] groupValues = self.formatGroupTableData(k[1],kProperties) featureValues = self.formatFeatureTableData(v,vProperties, feedtype) fData[tuple(groupValues)] = featureValues if fData: return fData else: return {('','', '', '', ''): [['', '', '', '', '']]} def singleFeatureObj(self, objkey): """ Returns a AIMS Address object that matches the object key @param feedType: Type of AIMS API feed @type feedType: AIMSDataManager.FeatureFactory.FeedRef @param objkey: Feautre id @type objkey: integer """ return self.data.get(FEEDS['AF'])[(objkey)] def singleReviewObj(self, feedtype, objkey): ''' return the value of which is an aims review obj (group and single) for the keyed data ''' if feedtype == FEEDS['AR']: return self.data.get(feedtype).get(objkey) elif feedtype == FEEDS['GR']: for k in self.data.get(feedtype): if objkey == k[0]: return k[1] def currentReviewFeature(self, currentGroup, currentFeatureKey): """ Returns aims feautre object as per supplied data key(s) @param currentGroup: Current group key (groupId, changeType) @type currentGroup: tuple @param currentFeatureKey: Current Feautre id @type currentFeatureKey: integer @return: Address object @rtype: AIMSDataManager.Address """ if currentGroup[1] not in ('Add', 'Update', 'Retire' ): for group in self.data.get(FEEDS['GR']).values(): if group.has_key(currentFeatureKey): return group[currentFeatureKey] else: return self.data.get(FEEDS['AR']).get(currentFeatureKey) def reviewItemCoords(self, currentGroup, currentFeatureKey): """ Returns the coords of a review obj @param currentGroup: Current group key (groupId, changeType) @type currentGroup: tuple @param currentFeatureKey: Current Feautre id @type currentFeatureKey: integer @return: list [x,y] @rtype: list """ obj = self.currentReviewFeature(currentGroup, currentFeatureKey) if not obj: return None if obj._changeType not in ('Update', 'Add') and not obj.meta.requestId: pos = obj.meta.entities[0].getAddressPositions()[0]._position_coordinates else: pos = obj.getAddressPositions()[0]._position_coordinates return pos