class AdministrativeSpatialUnit(Entity): """ Hierarchy of administrative spatial units. """ TYPE_INFO = 'ADMINISTRATIVE_SPATIAL_UNIT' def __init__(self, profile): Entity.__init__(self, 'admin_spatial_unit_set', profile, is_global=True, is_proxy=True, supports_documents=False) self.user_editable = False self.admin_unit_name = VarCharColumn('name', self, maximum=70) self.admin_unit_code = VarCharColumn('code', self, maximum=10) self.admin_parent_id = ForeignKeyColumn('parent_id', self) self.admin_parent_id.set_entity_relation_attr('parent', self) self.admin_parent_id.set_entity_relation_attr('parent_column', 'id') LOGGER.debug('%s Administrative Spatial Unit set initialized.', self.name) # Add columns self.add_column(self.admin_unit_name) self.add_column(self.admin_unit_code) self.add_column(self.admin_parent_id)
def add_party(self, party): """ Add a party entity to the collection of STR parties. .. versionadded:: 1.5 :param party: Party entity in STR relationship :type party: str or Entity :return: Returns True if the party was successfully added, otherwise False. If there is an existing party in the STR definition with the same name then the function returns False. :rtype: bool """ party_entity = self._obj_from_str(party) if self._party_in_parties(party_entity): return False fk_col_name = self._foreign_key_column_name(party_entity) party_fk = ForeignKeyColumn(fk_col_name, self) party_fk.set_entity_relation_attr('parent', party_entity) party_fk.set_entity_relation_attr('parent_column', 'id') self._party_fk_columns[fk_col_name] = party_fk self.add_column(party_fk) LOGGER.debug( '%s entity has been successfully added as a party in ' 'the %s profile social tenure relationship.', party_entity.name, self.profile.name)
def initialize_custom_attributes_entity(self, tenure_lookup): """ Creates a custom user attributes entity and adds a foreign key column for linking the two entities. .. versionadded:: 1.7 :param tenure_lookup: Valuelist containing tenure types. :type tenure_lookup: str or ValueList :return: Returns the custom attributes entity. A new one is created if did not exist otherwise the an existing one is returned. The entity needs to be added manually to the registry. :rtype: Entity """ tenure_lookup = self._obj_from_str(tenure_lookup) custom_ent_name = self._custom_attributes_entity_name( tenure_lookup.short_name) custom_ent = self.profile.entity(custom_ent_name) # Created only if it does not exist if custom_ent is None: custom_ent = Entity(custom_ent_name, self.profile, supports_documents=False) custom_ent.user_editable = False # Column for linking with primary tenure table str_col = ForeignKeyColumn('social_tenure_relationship_id', custom_ent) str_col.set_entity_relation_attr('parent', self) str_col.set_entity_relation_attr('parent_column', 'id') custom_ent.add_column(str_col) # Validate existence of dummy column self._validate_custom_attr_dummy_column(custom_ent) return custom_ent
def populate_configuration(config): profile = add_basic_profile(config) rel = create_relation(profile) person_entity = add_person_entity(profile) append_person_columns(person_entity) household_entity = add_household_entity(profile) rel.parent = household_entity rel.child = person_entity rel.child_column = 'household_id' rel.parent_column = 'id' profile.add_entity_relation(rel) #Add save option lookup save_options = create_value_list(profile, 'save_options') save_options.add_value('House') save_options.add_value('Bank') save_options.add_value('SACCO') profile.add_entity(save_options) #Add save options to multiple select column to person entity save_options_column = MultipleSelectColumn('save_location', person_entity) save_options_column.value_list = save_options person_entity.add_column(save_options_column) #Add community entity community = add_community_entity(profile) append_community_columns(community) #Append surveyor columns surveyor = add_surveyor_entity(profile) append_surveyor_columns(surveyor) spatial_unit = add_spatial_unit_entity(profile) #Add foreign key linking spatial unit to surveyor surveyor_id_col = ForeignKeyColumn('surveyor_id', spatial_unit) surveyor_id_col.set_entity_relation_attr('parent', surveyor) surveyor_id_col.set_entity_relation_attr('parent_column', 'id') spatial_unit.add_column(surveyor_id_col) #Set STR entities profile.set_social_tenure_attr(SocialTenure.PARTY, [person_entity, community]) profile.set_social_tenure_attr(SocialTenure.SPATIAL_UNIT, spatial_unit)
def add_spatial_unit(self, spatial_unit): """ Add a spatial unit entity to the collection of STR parties. .. versionadded:: 1.7 :param spatial_unit: Spatial unit entity in STR relationship. :type spatial_unit: str or Entity :return: Returns True if the spatial unit was successfully added, otherwise False. If there is an existing spatial unit in the STR definition with the same name or no geometry column then the function returns False. :rtype: bool """ sp_unit_entity = self._obj_from_str(spatial_unit) if sp_unit_entity is None: return False if self._sp_unit_in_sp_units(sp_unit_entity): return False if not sp_unit_entity.has_geometry_column(): return False fk_col_name = self._foreign_key_column_name(sp_unit_entity) sp_unit_fk = ForeignKeyColumn(fk_col_name, self) sp_unit_fk.on_delete_action = ForeignKeyColumn.CASCADE sp_unit_fk.set_entity_relation_attr('parent', sp_unit_entity) sp_unit_fk.set_entity_relation_attr('parent_column', 'id') self._spatial_unit_fk_columns[fk_col_name] = sp_unit_fk self.add_column(sp_unit_fk) # Link spatial unit to the default tenure type lookup self.add_spatial_tenure_mapping( sp_unit_entity, self.tenure_type_collection ) LOGGER.debug('%s entity has been successfully added as a spatial ' 'unit in the %s profile social tenure relationship.', sp_unit_entity.name, self.profile.name) return True
def _set_parent(self, parent): parent_entity = self._obj_from_str(parent) if parent_entity is None: LOGGER.debug( 'Failed to set the parent property in %s association ' 'entity.', self.name) return #check if there is an 'id' column parent_id = self._entity_id_column(parent_entity) LOGGER.debug('Attempting to set %s entity as the party.', parent_entity.name) if parent_id is None: err = self.tr('%s does not have an id column. This is required ' 'in order to link it to the social tenure ' 'relationship table.' % (parent_entity.name)) LOGGER.debug(err) raise AttributeError(err) #Set foreign key reference fk_name = u'{0}_{1}'.format(parent_entity.name, 'id') foreign_key_reference = ForeignKeyColumn(fk_name, self) #Set parent attributes foreign_key_reference.set_entity_relation_attr('parent', parent_entity) foreign_key_reference.set_entity_relation_attr('parent_column', 'id') LOGGER.debug( '%s entity has been successfully set as the parent in ' 'the %s association entity in the %s profile.', parent_entity.name, self.name, self.profile.name) return foreign_key_reference
def _set_parent(self, parent): parent_entity = self._obj_from_str(parent) if parent_entity is None: LOGGER.debug('Failed to set the parent property in %s association ' 'entity.', self.name) return # Check if there is an 'id' column parent_id = self._entity_id_column(parent_entity) LOGGER.debug('Attempting to set %s entity as the party.', parent_entity.name) if parent_id is None: err = self.tr('%s does not have an id column. This is required ' 'in order to link it to the social tenure ' 'relationship table.'%(parent_entity.name)) LOGGER.debug(err) raise AttributeError(err) # Set foreign key reference fk_name = u'{0}_{1}'.format(parent_entity.name, 'id') foreign_key_reference = ForeignKeyColumn(fk_name, self) # Set parent attributes foreign_key_reference.set_entity_relation_attr('parent', parent_entity) foreign_key_reference.set_entity_relation_attr('parent_column', 'id') LOGGER.debug('%s entity has been successfully set as the parent in ' 'the %s association entity in the %s profile.', parent_entity.name, self.name, self.profile.name) return foreign_key_reference
class SocialTenure(Entity): """ Represents the relationship between party and spatial unit entities through social tenure relationship. It also supports the attachment of supporting documents. Main class that represents 'people-land' relationships. """ TYPE_INFO = 'SOCIAL_TENURE' PARTY, SPATIAL_UNIT, SOCIAL_TENURE_TYPE, START_DATE, END_DATE = range(0, 5) BASE_STR_VIEW = 'vw_social_tenure_relationship' tenure_type_list = 'tenure_type' view_creator = view_updater view_remover = view_deleter def __init__(self, name, profile, supports_documents=True, layer_display=''): Entity.__init__(self, name, profile, supports_documents=supports_documents) self.user_editable = False self._party = None self._spatial_unit = None self._view_name = None self.party_foreign_key = ForeignKeyColumn('party_id', self) self.spatial_unit_foreign_key = ForeignKeyColumn( 'spatial_unit_id', self) self.tenure_type_lookup = LookupColumn('tenure_type', self) # Added in v1.5 self.validity_start_column = DateColumn('validity_start', self, index=True) self.validity_end_column = DateColumn('validity_end', self, index=True) self.tenure_share_column = PercentColumn('tenure_share', self) self.layer_display_name = layer_display self._value_list = self._prepare_tenure_type_value_list() # Add the value list to the table collection self.profile.add_entity(self._value_list) # Add columns to the collection self.add_column(self.spatial_unit_foreign_key) self.add_column(self.tenure_type_lookup) self.add_column(self.validity_start_column) self.add_column(self.validity_end_column) self.add_column(self.tenure_share_column) # Added in v1.5 self._party_fk_columns = OrderedDict() #Names of party entities that have been removed self.removed_parties = [] # Specify if a spatial unit should only be linked to one party self.multi_party = True LOGGER.debug('Social Tenure Relationship initialized for %s profile.', self.profile.name) def layer_display(self): """ :return: Name to show in the Layers TOC. :rtype: str """ if self.layer_display_name: return self.layer_display_name return self._view_name_from_entity(self.spatial_unit) @property def parties(self): """ :return: Returns a collection of party entities. .. versionadded:: 1.5 :rtype: list """ return [ pc.parent for pc in self._party_fk_columns.values() if not pc.parent is None ] @parties.setter def parties(self, parties): """ Adds the collection of parties to the STR definition. Result will be suppressed. .. versionadded:: 1.5 :param parties: Collection of party entities. :type parties: list """ for p in parties: self.add_party(p) @property def start_date(self): """ :return: Returns a tuple of the minimum and maximum start dates. .. versionadded:: 1.5 ":rtype: tuple(min_start_date, max_start_date) """ return self.validity_start_column.minimum, \ self.validity_start_column.maximum @property def end_date(self): """ :return: Returns a tuple of the minimum and maximum end dates. .. versionadded:: 1.5 ":rtype: tuple(min_end_date, max_end_date) """ return self.validity_end_column.minimum, \ self.validity_end_column.maximum @start_date.setter def start_date(self, start_date_range): """ Set the minimum and maximum validity start dates. :param start_date_range: A tuple containing the minimum and maximum dates respectively. This will only be applied in the database if the columns have not yet been created. .. versionadded:: 1.5 :type start_date_range: tuple(min_start_date, max_start_date) """ if len(start_date_range) < 2: raise ConfigurationException( 'A tuple of minimum and maximum start dates expected.') min_date, max_date = start_date_range[0], start_date_range[1] if min_date > max_date: raise ConfigurationException( 'Minimum start date is greater than maximum start date.') self.validity_start_column.minimum = min_date self.validity_start_column.maximum = max_date @end_date.setter def end_date(self, end_date_range): """ Set the minimum and maximum validity end dates. :param end_date_range: A tuple containing the minimum and maximum dates respectively. This will only be applied in the database if the columns have not yet been created. .. versionadded:: 1.5 :type end_date_range: tuple(min_end_date, max_end_date) """ if len(end_date_range) < 2: raise ConfigurationException( 'A tuple of minimum and maximum end dates expected.') min_date, max_date = end_date_range[0], end_date_range[1] if min_date > max_date: raise ConfigurationException( 'Minimum end date is greater than maximum end date.') self.validity_end_column.minimum = min_date self.validity_end_column.maximum = max_date def _view_name_from_entity(self, entity): # Construct view name from entity name if entity is not None: return u'{0}_{1}'.format(entity.name, SocialTenure.BASE_STR_VIEW) @property def views(self): """ :return: Returns a collection of view names and corresponding primary entities for each view. The primary entities include the respective party and spatial unit entities in the STR definition. .. versionadded:: 1.5 :rtype: dict(view_name, primary entity) """ v = {} for p in self.parties: view_name = self._view_name_from_entity(p) v[view_name] = p #Include spatial unit if not self.spatial_unit is None: sp_view = self._view_name_from_entity(self.spatial_unit) v[sp_view] = self.spatial_unit return v @property def party_columns(self): """ :return: Returns a collection of STR party columns. .. versionadded:: 1.5 :rtype: OrderedDict(column_name, ForeignKeyColumn) """ return self._party_fk_columns def add_party(self, party): """ Add a party entity to the collection of STR parties. .. versionadded:: 1.5 :param party: Party entity in STR relationship :type party: str or Entity :return: Returns True if the party was successfully added, otherwise False. If there is an existing party in the STR definition with the same name then the function returns False. :rtype: bool """ party_entity = self._obj_from_str(party) if self._party_in_parties(party_entity): return False fk_col_name = self._foreign_key_column_name(party_entity) party_fk = ForeignKeyColumn(fk_col_name, self) party_fk.set_entity_relation_attr('parent', party_entity) party_fk.set_entity_relation_attr('parent_column', 'id') self._party_fk_columns[fk_col_name] = party_fk self.add_column(party_fk) LOGGER.debug( '%s entity has been successfully added as a party in ' 'the %s profile social tenure relationship.', party_entity.name, self.profile.name) def _foreign_key_column_name(self, party_entity): #Appends 'id' suffix to the entity's short name. fk_col_name = u'{0}_id'.format(party_entity.short_name.lower()) return fk_col_name def clear_removed_parties(self): """ Clears the collection of STR party entities that have been removed from the collection. """ self.removed_parties = [] def remove_party(self, party): """ Remove a party entity from the existing STR collection. .. versionadded:: 1.5 :param party: Party entity in STR relationship :type party: str or Entity :return: Returns True if the party was successfully removed, otherwise False. If there is no corresponding party in the collection then the function returns False. :rtype: bool """ party_entity = self._obj_from_str(party) if not self._party_in_parties(party_entity): return False fk_col_name = self._foreign_key_column_name(party_entity) #Remove column from the collection status = self.remove_column(fk_col_name) if not status: return False #Remove from internal collection if fk_col_name in self._party_fk_columns: del self._party_fk_columns[fk_col_name] self.removed_parties.append(party_entity.short_name) return True def _party_in_parties(self, party): #Check if a party is in the STR collection. party_names = [p.name for p in self.parties] if party.name in party_names: return True return False def is_str_party_entity(self, entity): """ Checks if the specified entity is a party entity in the social tenure relationship definition. .. versionadded:: 1.5 :param entity: Entity to assert if its part of the STR party definition. :type entity: Entity :return: True if the entity is part of the party collection in the STR definition, otherwise False. :rtype: bool """ return self._party_in_parties(entity) def is_str_entity(self, entity): """ Checks if the entity is a spatial or party entity in the STR collection. :param entity: Entity to assert if its a party or spatial unit. :type entity: Entity :return:True if the entity is an STR entity in the STR definition, otherwise False. :rtype: bool """ if self.is_str_party_entity(entity): return True if entity == self.spatial_unit: return True return False @property def view_name(self): """ .. deprecated:: 1.5 Use :func:`views` instead to get a list of view names. """ return self._view_name @property def party(self): """ .. deprecated:: 1.5 Use :func:`parties` instead to get a list of party entities. """ return self._party @property def spatial_unit(self): return self._spatial_unit @property def tenure_type_collection(self): return self._value_list @tenure_type_collection.setter def tenure_type_collection(self, value_list): #Copy the look up values from the given value list value_list_entity = self._obj_from_str(value_list) self._value_list.copy_from(value_list_entity) @party.setter def party(self, party): """ .. deprecated:: 1.5 Use :func:`add_party` instead to get a list of party entities. """ party_entity = self._obj_from_str(party) if party_entity is None: return #Check if there is an 'id' column party_id = self._entity_id_column(party_entity) LOGGER.debug('Attempting to set %s entity as the party.', party_entity.name) if party_id is None: err = self.tr('%s does not have an id column. This is required ' 'in order to link it to the social tenure ' 'relationship table.' % (party_entity.name)) LOGGER.debug(err) raise AttributeError(err) self._party = party_entity #Set parent attributes self.party_foreign_key.set_entity_relation_attr('parent', self._party) self.party_foreign_key.set_entity_relation_attr('parent_column', 'id') LOGGER.debug( '%s entity has been successfully set as the party in ' 'the %s profile social tenure relationship.', party_entity.name, self.profile.name) @spatial_unit.setter def spatial_unit(self, spatial_unit): """ Sets the corresponding spatial unit entity in the social tenure relationship. :param spatial_unit: Spatial unit entity. .. note:: The spatial unit entity must contain a geometry column else it will not be set. """ spatial_unit_entity = self._obj_from_str(spatial_unit) if spatial_unit_entity is None: return #check if there is an 'id' column sp_unit_id = self._entity_id_column(spatial_unit_entity) LOGGER.debug('Attempting to set %s entity as the spatial unit.', spatial_unit_entity.name) if sp_unit_id is None: err = self.tr('%s does not have an id column. This is required ' 'in order to link it to the social tenure ' 'relationship table.' % (spatial_unit_entity.name)) LOGGER.debug(err) raise AttributeError(err) if not spatial_unit_entity.has_geometry_column(): spatial_unit_entity = None return #err = self.tr('%s does not have a geometry column. This is required' #' when setting the spatial unit entity in a ' #'social tenure relationship definition.' #%(spatial_unit_entity.name)) #LOGGER.debug(err) #raise AttributeError(err) self._spatial_unit = spatial_unit_entity #Set parent attributes self.spatial_unit_foreign_key.set_entity_relation_attr( 'parent', self._spatial_unit) self.spatial_unit_foreign_key.set_entity_relation_attr( 'parent_column', 'id') LOGGER.debug( '%s entity has been successfully set as the spatial ' 'unit in the %s profile social tenure relationship.', spatial_unit_entity.name, self.profile.name) def _obj_from_str(self, item): """Create corresponding table item from string.""" obj = item if isinstance(item, (str, unicode)): if not item: return None obj = self.profile.entity(item) return obj def _entity_id_column(self, entity): """ Check if the entity has an ID column and return it, else returns None. """ return entity.column('id') def _prepare_tenure_type_value_list(self): #Create tenure types lookup table tenure_value_list = ValueList(self.tenure_type_list, self.profile) #Set lookup column reference value list self.tenure_type_lookup.value_list = tenure_value_list return tenure_value_list def valid(self): """ :return: Returns True if the party and spatial unit entities have been set, else returns False. :rtype: bool """ if len(self._party_fk_columns) == 0: return False if self._spatial_unit is None: return False return True def delete_view(self, engine): """ Deletes the basic view associated with the current social tenure object. :param engine: SQLAlchemy connectable object. :type engine: Engine """ self.view_remover(engine) def create_view(self, engine): """ Creates a basic view linking all the social tenure relationship entities. :param engine: SQLAlchemy connectable object. :type engine: Engine """ self.view_creator(engine)
class EntitySupportingDocument(Entity): """ An association class that provides the link between a given Entity and a SupportingDocument class. """ TYPE_INFO = 'ENTITY_SUPPORTING_DOCUMENT' def __init__(self, profile, parent_entity): self.parent_entity = parent_entity name = self._entity_short_name(parent_entity.short_name) Entity.__init__(self, name, profile, supports_documents=False) self.user_editable = False #Supporting document ref column self.document_reference = ForeignKeyColumn('supporting_doc_id', self) normalize_name = self.parent_entity.short_name.replace(' ', '_').lower() # Entity reference column entity_ref_name = u'{0}_{1}'.format(normalize_name, 'id') self.entity_reference = ForeignKeyColumn(entity_ref_name, self) # Document types vl_name = self._doc_type_name(normalize_name) self._doc_types_value_list = self._doc_type_vl(vl_name) if self._doc_types_value_list is None: self._doc_types_value_list = self.profile.create_value_list( vl_name) self.profile.add_entity(self._doc_types_value_list) #Add a default type self._doc_types_value_list.add_value(self.tr('General')) #Document type column self.doc_type = LookupColumn('document_type', self) self.doc_type.value_list = self._doc_types_value_list #Append columns self.add_column(self.document_reference) self.add_column(self.entity_reference) self.add_column(self.doc_type) LOGGER.debug('Updating foreign key references in %s entity.', self.name) #Update foreign key references self._update_fk_references() LOGGER.debug('%s entity successfully initialized', self.name) def _entity_short_name(self, parent_entity_name): name = u'{0}_{1}'.format(parent_entity_name, 'supporting_document') return name def _doc_type_name(self, normalize_name): vl_name = u'check_{0}_document_type'.format(normalize_name) return vl_name def _doc_type_vl(self, name): #Search for the document type value list based on the given name value_lists = self.profile.value_lists() doc_type_vl = [v for v in value_lists if v.short_name == name] #Return first item if len(doc_type_vl) > 0: return doc_type_vl[0] return None def _update_fk_references(self): #Update ForeignKey references. #check if there is an 'id' column entity_id = self._entity_id_column(self.parent_entity) LOGGER.debug( 'Attempting to set %s entity as the parent entity to ' 'this supporting document reference.', self.parent_entity.name) if entity_id is None: err = self.tr('%s does not have an id column. This is required ' 'in order to link it to the supporting document ' 'table through this association ' 'table.' % (self.parent_entity.name)) LOGGER.debug(err) raise AttributeError(err) self.entity_reference.set_entity_relation_attr('parent', self.parent_entity) self.entity_reference.set_entity_relation_attr('parent_column', 'id') #Supporting document reference self.document_reference.set_entity_relation_attr( 'parent', self.profile.supporting_document) self.document_reference.set_entity_relation_attr('parent_column', 'id') def rename(self, shortname): """ Updates the supporting document references when the parent entity is renamed. This renames the shortname and name, foreign key column and the name of the document type lookup. :param shortname: Shortname of the parent entity. :type shortname: str """ #Remove the object then re-insert so as to update index doc_entity = self.profile.entities.pop(self.short_name) supporting_docs_shortname = self._entity_short_name(shortname).replace( ' ', '_').lower() #Update shortname and name super(EntitySupportingDocument, self).rename(supporting_docs_shortname) #Re-insert the entity self.profile.add_entity(self, True) #Get entity relations and update entity references parent_relations = self.profile.parent_relations(self) child_relations = self.profile.child_relations(self) #Update relations for pr in parent_relations: pr.parent = self for cr in child_relations: cr.child = self #Rename lookup for supporting documents lookup if not self._doc_types_value_list is None: norm_parent_short_name = self.parent_entity.short_name.replace( ' ', '_').lower() vl_name = self._doc_type_name(norm_parent_short_name) self._doc_types_value_list.rename_entity(vl_name) def document_types(self): """ :return: Returns a collection of document type names and corresponding codes. :rtype: OrderedDict """ return self._doc_types_value_list.values def document_types_non_hex(self): # Added in version 1.7 """ :return: Returns a collection of document type names and corresponding codes. :rtype: OrderedDict """ non_hex_values = [] for v in self._doc_types_value_list.values: cv = self._doc_types_value_list.values[v] if cv.updated_value == '': txt = cv.value else: txt = cv.updated_value non_hex_values.append(txt) return non_hex_values def document_path(self): """ :return: Returns a subpath for locating supporting documents using the profile and entity names respectively. This is concatenated to the root path to locate documents for this particular entity. :rtype: str """ return u'{0}/{1}'.format(self.profile.key, self.parent_entity.name) @property def document_type_entity(self): """ :return: ValueList object containing the entity types. :rtype: ValueList """ return self._doc_types_value_list def _entity_id_column(self, entity): """ Check if the entity has an ID column and return it, else returns None. """ return entity.column('id')
def populate_configuration(config): profile = add_basic_profile(config) rel = create_relation(profile) person_entity = add_person_entity(profile) append_person_columns(person_entity) household_entity = add_household_entity(profile) rel.parent = household_entity rel.child = person_entity rel.child_column = 'household_id' rel.parent_column = 'id' profile.add_entity_relation(rel) # Add save option lookup save_options = create_value_list(profile, 'save_options') save_options.add_value('House') save_options.add_value('Bank') save_options.add_value('SACCO') profile.add_entity(save_options) # Add save options to multiple select column to person entity save_options_column = MultipleSelectColumn('save_location', person_entity) save_options_column.value_list = save_options person_entity.add_column(save_options_column) # Add community entity community = add_community_entity(profile) append_community_columns(community) # Append surveyor columns surveyor = add_surveyor_entity(profile) append_surveyor_columns(surveyor) spatial_unit = add_spatial_unit_entity(profile) spatial_unit_2 = add_spatial_unit_entity_2(profile) # Add foreign key linking spatial unit to surveyor surveyor_id_col = ForeignKeyColumn('surveyor_id', spatial_unit) surveyor_id_col.set_entity_relation_attr('parent', surveyor) surveyor_id_col.set_entity_relation_attr('parent_column', 'id') spatial_unit.add_column(surveyor_id_col) # Set STR entities profile.set_social_tenure_attr(SocialTenure.PARTY, [person_entity, community]) profile.set_social_tenure_attr(SocialTenure.SPATIAL_UNIT, [spatial_unit, spatial_unit_2]) # Create and add secondary tenure lookup sec_tenure_vl = add_secondary_tenure_value_list(profile) # Map secondary tenure to spatial unit 2 profile.social_tenure.add_spatial_tenure_mapping(spatial_unit_2, sec_tenure_vl) # Create custom attr entity for primary tenure lookup primary_tenure_vl = profile.social_tenure.tenure_type_collection p_custom_ent = profile.social_tenure.custom_attribute_entity( primary_tenure_vl) if p_custom_ent is None: profile.social_tenure.initialize_custom_attributes_entity( primary_tenure_vl) # Set entity p_custom_ent = profile.social_tenure.custom_attribute_entity( primary_tenure_vl) # Add custom attributes constitution_ref_col = TextColumn('constitution_ref', p_custom_ent) p_custom_ent.add_column(constitution_ref_col) # Create secondary tenure custom attributes entity s_custom_ent = profile.social_tenure.custom_attribute_entity(sec_tenure_vl) if s_custom_ent is None: profile.social_tenure.initialize_custom_attributes_entity( sec_tenure_vl) # Set entity s_custom_ent = profile.social_tenure.custom_attribute_entity( sec_tenure_vl) application_date_col = DateColumn('application_date', s_custom_ent) s_custom_ent.add_column(application_date_col)
class SocialTenure(Entity): """ Represents the relationship between party and spatial unit entities through social tenure relationship. It also supports the attachment of supporting documents. Main class that represents 'people-land' relationships. """ TYPE_INFO = 'SOCIAL_TENURE' PARTY, SPATIAL_UNIT, SOCIAL_TENURE_TYPE, START_DATE, END_DATE = range( 0, 5 ) BASE_STR_VIEW = 'vw_social_tenure_relationship' CUSTOM_ATTRS_ENTITY = 'attrs' tenure_type_list = 'tenure_type' CUSTOM_TENURE_DUMMY_COLUMN = 'dummy_custom_str' view_creator = view_updater view_remover = view_deleter def __init__(self, name, profile, supports_documents=True, layer_display=''): Entity.__init__(self, name, profile, supports_documents=supports_documents) self.user_editable = False self._party = None self._spatial_unit = None self._view_name = None self.party_foreign_key = ForeignKeyColumn('party_id', self) self.spatial_unit_foreign_key = ForeignKeyColumn( 'spatial_unit_id', self ) self.tenure_type_lookup = LookupColumn('tenure_type', self) # Added in v1.5 self.validity_start_column = DateColumn( 'validity_start', self, index=True ) self.validity_end_column = DateColumn( 'validity_end', self, index=True ) self.tenure_share_column = PercentColumn('tenure_share', self) self.layer_display_name = layer_display self._value_list = self._prepare_tenure_type_value_list() # Add the value list to the table collection self.profile.add_entity(self._value_list) # Add columns to the collection self.add_column(self.tenure_type_lookup) self.add_column(self.validity_start_column) self.add_column(self.validity_end_column) self.add_column(self.tenure_share_column) # Added in v1.5 self._party_fk_columns = OrderedDict() # Names of party entities that have been removed self.removed_parties = [] # Added in v1.7 self._spatial_unit_fk_columns = OrderedDict() # Mapping of spatial units and corresponding tenure types self._sp_units_tenure = {} # Mapping of tenure type lookup columns self._tenure_type_sec_lk_columns = {} # Tenure type custom attribute entities # key: tenure lookup short name # value: custom attributes entity self._custom_attr_entities = {} # Specify if a spatial unit should only be linked to one party self.multi_party = True LOGGER.debug('Social Tenure Relationship initialized for %s profile.', self.profile.name) @property def custom_attribute_entities(self): """ :return: Returns the collection of custom attribute entities. .. versionadded:: 1.7 :rtype: dict(str, Entity) """ return self._custom_attr_entities def has_custom_attribute_entities(self): """ :return: Returns True if there exists a custom tenure attributes entity, otherwise False. .. versionadded:: 1.7 :rtype: bool """ if len(self.custom_attributes_entities) == 0: return False return True def _custom_attributes_entity_name(self, t_type_s_name): # Build entity name using tenure type short name return u'{0}_{1}_{2}'.format( t_type_s_name, 'str', self.CUSTOM_ATTRS_ENTITY ) def initialize_custom_attributes_entity(self, tenure_lookup): """ Creates a custom user attributes entity and adds a foreign key column for linking the two entities. .. versionadded:: 1.7 :param tenure_lookup: Valuelist containing tenure types. :type tenure_lookup: str or ValueList :return: Returns the custom attributes entity. A new one is created if did not exist otherwise the an existing one is returned. The entity needs to be added manually to the registry. :rtype: Entity """ tenure_lookup = self._obj_from_str(tenure_lookup) custom_ent_name = self._custom_attributes_entity_name( tenure_lookup.short_name ) custom_ent = self.profile.entity(custom_ent_name) # Created only if it does not exist if custom_ent is None: custom_ent = Entity( custom_ent_name, self.profile, supports_documents=False ) custom_ent.user_editable = False # Column for linking with primary tenure table str_col = ForeignKeyColumn('social_tenure_relationship_id', custom_ent) str_col.set_entity_relation_attr('parent', self) str_col.set_entity_relation_attr('parent_column', 'id') custom_ent.add_column(str_col) # Validate existence of dummy column self._validate_custom_attr_dummy_column(custom_ent) return custom_ent def _validate_custom_attr_dummy_column(self, custom_entity): # Check if the dummy column has been added to the custom tenure entity # Insert dummy column so that the table is not flagged as a m2m dummy_col = custom_entity.column(self.CUSTOM_TENURE_DUMMY_COLUMN) if dummy_col is None: dummy_col = VarCharColumn( self.CUSTOM_TENURE_DUMMY_COLUMN, custom_entity, maximum=1 ) custom_entity.add_column(dummy_col) if pg_table_exists(custom_entity.name): custom_ent_cols = table_column_names(custom_entity.name) if dummy_col.name not in custom_ent_cols: custom_table = alchemy_table(custom_entity.name) varchar_updater(dummy_col, custom_table, custom_ent_cols) def add_tenure_attr_custom_entity(self, tenure_lookup, entity): """ Adds a mapping that links the tenure lookup to the custom attributes entity. :param tenure_lookup: Valuelist containing tenure types. :type tenure_lookup: str or ValueList :param entity: Custom attributes entity. :type entity: str or Entity """ tenure_lookup = self._obj_from_str(tenure_lookup) entity = self._obj_from_str(entity) # Validate existence of dummy column self._validate_custom_attr_dummy_column(entity) self._custom_attr_entities[tenure_lookup.short_name] = entity def custom_attribute_entity(self, tenure_lookup): """ Get the custom attribute entity. .. versionadded:: 1.7 :param tenure_lookup: Tenure type valuelist. :type tenure_lookup: str or ValueList :return: Returns the custom attributes entity corresponding to the given tenure type. None if not found. :rtype: Entity """ t_type = self._obj_from_str(tenure_lookup) return self._custom_attr_entities.get(t_type.short_name, None) def spu_custom_attribute_entity(self, spu): """ Retrieves the custom attributes entity for the given spatial unit. .. versionadded:: 1.7 :param spu: Spatial unit entity. :type spu: str or Entity :return: Returns the custom attributes entity for the given spatial unit, None if not found. :rtype: Entity """ t_type = self.spatial_unit_tenure_lookup(spu) if t_type is None: return None return self.custom_attribute_entity(t_type) def remove_custom_attributes_entity(self, tenure_lookup): """ Removes the custom attributes entity that corresponds to the given tenure lookup. .. versionadded:: 1.7 :param tenure_lookup: Tenure type valuelist. :type tenure_lookup: ValueList :returns: True if the entity was successfully removed, otherwise False. :rtype: bool """ custom_ent_name = self._custom_attributes_entity_name( tenure_lookup.short_name ) return self.profile.remove_entity(custom_ent_name) def remove_custom_attributes_entity_by_spu(self, spu): """ Removes the custom attributes entity that corresponds to the given spatial unit. .. versionadded:: 1.7 :param tenure_lookup: Spatial unit entity. :type tenure_lookup: Entity :returns: True if the entity was successfully removed, otherwise False. :rtype: bool """ t_type = self.spatial_unit_tenure_lookup(spu) if t_type is None: return False return self.remove_custom_attributes_entity(t_type) def layer_display(self): """ :return: Name to show in the Layers TOC. .. deprecated:: 1.5 :rtype: str """ if self.layer_display_name: return self.layer_display_name return self._view_name_from_entity(self.spatial_unit) @property def parties(self): """ :return: Returns a collection of party entities. .. versionadded:: 1.5 :rtype: list """ return [pc.parent for pc in self._party_fk_columns.values() if not pc.parent is None] @parties.setter def parties(self, parties): """ Adds the collection of parties to the STR definition. Result will be suppressed. .. versionadded:: 1.5 :param parties: Collection of party entities. :type parties: list """ for p in parties: self.add_party(p) @property def spatial_units(self): """ :return: Returns a collection of spatial unit entities. .. versionadded:: 1.7 :rtype: list """ return [sp.parent for sp in self._spatial_unit_fk_columns.values() if not sp.parent is None] @spatial_units.setter def spatial_units(self, sp_units): """ Adds the collection of spatial units to the STR definition. Result will be suppressed. .. versionadded:: 1.7 :param sp_units: Collection of spatial unit entities. :type sp_units: list """ for sp in sp_units: self.add_spatial_unit(sp) @property def start_date(self): """ :return: Returns a tuple of the minimum and maximum start dates. .. versionadded:: 1.5 ":rtype: tuple(min_start_date, max_start_date) """ return self.validity_start_column.minimum, \ self.validity_start_column.maximum @property def end_date(self): """ :return: Returns a tuple of the minimum and maximum end dates. .. versionadded:: 1.5 ":rtype: tuple(min_end_date, max_end_date) """ return self.validity_end_column.minimum, \ self.validity_end_column.maximum @start_date.setter def start_date(self, start_date_range): """ Set the minimum and maximum validity start dates. :param start_date_range: A tuple containing the minimum and maximum dates respectively. This will only be applied in the database if the columns have not yet been created. .. versionadded:: 1.5 :type start_date_range: tuple(min_start_date, max_start_date) """ if len(start_date_range) < 2: raise ConfigurationException( 'A tuple of minimum and maximum start dates expected.' ) min_date, max_date = start_date_range[0], start_date_range[1] if min_date > max_date: raise ConfigurationException( 'Minimum start date is greater than maximum start date.' ) self.validity_start_column.minimum = min_date self.validity_start_column.maximum = max_date @end_date.setter def end_date(self, end_date_range): """ Set the minimum and maximum validity end dates. :param end_date_range: A tuple containing the minimum and maximum dates respectively. This will only be applied in the database if the columns have not yet been created. .. versionadded:: 1.5 :type end_date_range: tuple(min_end_date, max_end_date) """ if len(end_date_range) < 2: raise ConfigurationException( 'A tuple of minimum and maximum end dates expected.' ) min_date, max_date = end_date_range[0], end_date_range[1] if min_date > max_date: raise ConfigurationException( 'Minimum end date is greater than maximum end date.' ) self.validity_end_column.minimum = min_date self.validity_end_column.maximum = max_date def _view_name_from_entity(self, entity): # Construct view name from entity name if entity is not None: return u'{0}_{1}'.format( entity.name, SocialTenure.BASE_STR_VIEW ) @property def views(self): """ :return: Returns a collection of view names and corresponding primary entities for each view. The primary entities include the respective party and spatial unit entities in the STR definition. .. versionadded:: 1.5 :rtype: dict(view_name, primary entity) """ v = {} for p in self.parties: view_name = self._view_name_from_entity(p) v[view_name] = p # Include spatial units for sp in self.spatial_units: sp_view = self._view_name_from_entity(sp) v[sp_view] = sp return v @property def spatial_unit_columns(self): """ :return: Returns a collection of STR spatial unit columns. .. versionadded: 1.7 :rtype: OrderedDict(column_name, ForeignKeyColumn) """ return self._spatial_unit_fk_columns def add_spatial_unit(self, spatial_unit): """ Add a spatial unit entity to the collection of STR parties. .. versionadded:: 1.7 :param spatial_unit: Spatial unit entity in STR relationship. :type spatial_unit: str or Entity :return: Returns True if the spatial unit was successfully added, otherwise False. If there is an existing spatial unit in the STR definition with the same name or no geometry column then the function returns False. :rtype: bool """ sp_unit_entity = self._obj_from_str(spatial_unit) if sp_unit_entity is None: return False if self._sp_unit_in_sp_units(sp_unit_entity): return False if not sp_unit_entity.has_geometry_column(): return False fk_col_name = self._foreign_key_column_name(sp_unit_entity) sp_unit_fk = ForeignKeyColumn(fk_col_name, self) sp_unit_fk.on_delete_action = ForeignKeyColumn.CASCADE sp_unit_fk.set_entity_relation_attr('parent', sp_unit_entity) sp_unit_fk.set_entity_relation_attr('parent_column', 'id') self._spatial_unit_fk_columns[fk_col_name] = sp_unit_fk self.add_column(sp_unit_fk) # Link spatial unit to the default tenure type lookup self.add_spatial_tenure_mapping( sp_unit_entity, self.tenure_type_collection ) LOGGER.debug('%s entity has been successfully added as a spatial ' 'unit in the %s profile social tenure relationship.', sp_unit_entity.name, self.profile.name) return True def remove_spatial_unit(self, spatial_unit): """ Remove a spatial unit entity from the STR collection. .. versionadded:: 1.7 :param spatial_unit: Spatial unit entity in STR relationship :type spatial_unit: str or Entity :return: Returns True if the spatial unit was successfully removed, otherwise False. If there is no corresponding spatial unit in the collection then the function returns False. :rtype: bool """ sp_unit_entity = self._obj_from_str(spatial_unit) if sp_unit_entity is None: return False if not self._sp_unit_in_sp_units(sp_unit_entity): return False fk_col_name = self._foreign_key_column_name(sp_unit_entity) # Remove tenure mapping associated with the spatial unit self.remove_spatial_unit_tenure_mapping(sp_unit_entity) # Remove column from the collection status = self.remove_column(fk_col_name) if not status: return False # Remove from internal collection if fk_col_name in self._spatial_unit_fk_columns: del self._spatial_unit_fk_columns[fk_col_name] return True @property def spatial_units_tenure(self): """ :return: Returns a collection of spatial unit names and the corresponding lookup tables containing the valid tenure types. :rtype: dict """ return self._sp_units_tenure def add_spatial_tenure_mapping(self, spatial_unit, tenure_lookup): """ Sets the tenure type lookup for the specified spatial unit. Any previous mapping for the spatial unit is overridden. .. versionadded:: 1.7 :param spatial_unit: Spatial unit entity :type spatial_unit: str or Entity :param tenure_lookup: Tenure type lookup :type tenure_lookup: str or ValueList """ sp_unit = self._obj_from_str(spatial_unit) if sp_unit is None: sp_unit_name = spatial_unit else: sp_unit_name = sp_unit.short_name tenure_vl = self._obj_from_str(tenure_lookup) # Use short name as key in the collection self._sp_units_tenure[sp_unit_name] = tenure_vl # Add tenure type lookup column to the collection if tenure_vl == self.tenure_type_collection: tenure_lk_col = self.tenure_type_lookup else: tenure_lk_col = self._create_tenure_type_lookup_column(tenure_vl) self.add_column(tenure_lk_col) self._tenure_type_sec_lk_columns[tenure_vl.short_name] = tenure_lk_col def _create_tenure_type_lookup_column(self, tenure_vl): # Returns a lookup column whose parent is the given tenure value list. col_name = tenure_vl.short_name.replace('check_', '').replace( ' ', '_' ).lower() tenure_lookup_col = LookupColumn(col_name, self) tenure_lookup_col.value_list = tenure_vl return tenure_lookup_col def spatial_unit_tenure_lookup(self, spatial_unit): """ Retrieves the tenure lookup for the given spatial unit. .. versionadded:: 1.7 :param spatial_unit: Spatial unit whose corresponding tenure lookup is to be retrieved. :type spatial_unit: str or Entity :return: Returns the tenure lookup for the given spatial unit, otherwise None. :rtype: ValueList """ sp_unit = self._obj_from_str(spatial_unit) return self._sp_units_tenure.get(sp_unit.short_name, None) def remove_spatial_unit_tenure_mapping(self, spatial_unit): """ Removes the tenure lookup linkage for the given spatial unit. .. versionadded:: 1.7 :param spatial_unit: Spatial unit whose corresponding tenure lookup reference is to be removed. :type spatial_unit: str or Entity :return: Returns True is the delinking was successful, otherwise False. False if there was no existing link to be removed. :rtype: False """ sp_unit = self._obj_from_str(spatial_unit) if sp_unit.short_name in self._sp_units_tenure: tenure_vl = self._sp_units_tenure[sp_unit.short_name] del self._sp_units_tenure[sp_unit.short_name] # Remove custom attributes entity associated with the tenure type self.remove_custom_attributes_entity_by_spu(sp_unit) # Check if the tenure value list is defined for other spatial # units. remove_column = True for tvl in self._sp_units_tenure.values(): if tvl.name == tenure_vl.name: remove_column = False # Remove tenure lookup column if remove_column: vl_short_name = tenure_vl.short_name if vl_short_name in self._tenure_type_sec_lk_columns: rm_lk_col = self._tenure_type_sec_lk_columns[vl_short_name] # Delink column del self._tenure_type_sec_lk_columns[vl_short_name] self.remove_column(rm_lk_col) return True return False def spatial_unit_tenure_column(self, spatial_unit): """ Search and retrieve the lookup column corresponding to the given spatial unit. .. versionadded:: 1.7 :param spatial_unit: Spatial unit entity :type spatial_unit: str or Entity :return: Returns the tenure lookup column corresponding to the given spatial unit, otherwise None if there is no existing mapping. :rtype: LookupColumn """ tenure_vl = self.spatial_unit_tenure_lookup(spatial_unit) if tenure_vl is None: return None return self._tenure_type_sec_lk_columns.get( tenure_vl.short_name, None ) @property def party_columns(self): """ :return: Returns a collection of STR party columns. .. versionadded:: 1.5 :rtype: OrderedDict(column_name, ForeignKeyColumn) """ return self._party_fk_columns def add_party(self, party): """ Add a party entity to the collection of STR parties. .. versionadded:: 1.5 :param party: Party entity in STR relationship :type party: str or Entity :return: Returns True if the party was successfully added, otherwise False. If there is an existing party in the STR definition with the same name then the function returns False. :rtype: bool """ party_entity = self._obj_from_str(party) if party_entity is None: return False if self._party_in_parties(party_entity): return False fk_col_name = self._foreign_key_column_name(party_entity) party_fk = ForeignKeyColumn(fk_col_name, self) party_fk.on_delete_action = ForeignKeyColumn.CASCADE party_fk.set_entity_relation_attr('parent', party_entity) party_fk.set_entity_relation_attr('parent_column', 'id') self._party_fk_columns[fk_col_name] = party_fk self.add_column(party_fk) LOGGER.debug('%s entity has been successfully added as a party in ' 'the %s profile social tenure relationship.', party_entity.name, self.profile.name) return True def _foreign_key_column_name(self, entity): # Appends 'id' suffix to the entity's short name. fk_col_name = u'{0}_id'.format(entity.short_name.lower()).replace( ' ', '_' ) return fk_col_name def clear_removed_parties(self): """ Clears the collection of STR party entities that have been removed from the collection. """ self.removed_parties = [] def remove_party(self, party): """ Remove a party entity from the existing STR collection. .. versionadded:: 1.5 :param party: Party entity in STR relationship :type party: str or Entity :return: Returns True if the party was successfully removed, otherwise False. If there is no corresponding party in the collection then the function returns False. :rtype: bool """ party_entity = self._obj_from_str(party) if party_entity is None: return False if not self._party_in_parties(party_entity): return False fk_col_name = self._foreign_key_column_name(party_entity) #Remove column from the collection status = self.remove_column(fk_col_name) if not status: return False #Remove from internal collection if fk_col_name in self._party_fk_columns: del self._party_fk_columns[fk_col_name] self.removed_parties.append(party_entity.short_name) return True def _party_in_parties(self, party): # Check if a party is in the STR collection. if party is None: return False party = self._obj_from_str(party) party_names = [p.name for p in self.parties] if party.name in party_names: return True return False def _sp_unit_in_sp_units(self, spatial_unit): # Check if a party is in the STR collection. spatial_unit = self._obj_from_str(spatial_unit) sp_unit_names = [s.name for s in self.spatial_units] if spatial_unit.name in sp_unit_names: return True return False def is_str_party_entity(self, entity): """ Checks if the specified entity is a party entity in the social tenure relationship definition. .. versionadded:: 1.5 :param entity: Entity to assert if its part of the STR party definition. :type entity: Entity :return: True if the entity is part of the party collection in the STR definition, otherwise False. :rtype: bool """ return self._party_in_parties(entity) def is_str_spatial_unit_entity(self, entity): """ Checks if the specified entity is a spatial unit entity in the social tenure relationship definition. .. versionadded:: 1.7 :param entity: Entity to assert if its part of the STR spatial unit definition. :type entity: Entity :return: Returns True if the entity is part of the spatial unit collection in the STR definition, otherwise False. :rtype: bool """ return self._sp_unit_in_sp_units(entity) def is_str_entity(self, entity): """ Checks if the entity is a spatial or party entity in the STR collection. :param entity: Entity to assert if its a party or spatial unit. :type entity: Entity :return:True if the entity is an STR entity in the STR definition, otherwise False. :rtype: bool """ if self.is_str_party_entity(entity): return True if self.is_str_spatial_unit_entity(entity): return True return False @property def view_name(self): """ .. deprecated:: 1.5 Use :func:`views` instead to get a list of view names. """ return self._view_name @property def party(self): """ .. deprecated:: 1.5 Use :func:`parties` instead to get a list of party entities. """ return self._party @property def spatial_unit(self): """ .. deprecated:: 1.7 Use :func:`spatial_units` instead to get a list of spatial unit entities. """ return self._spatial_unit @property def tenure_type_collection(self): """ :return: Returns the primary tenure lookup. :rtype: ValueList """ return self._value_list @tenure_type_collection.setter def tenure_type_collection(self, value_list): # Copy the look up values from the given value list value_list_entity = self._obj_from_str(value_list) self._value_list.copy_from(value_list_entity) @party.setter def party(self, party): """ .. deprecated:: 1.5 Use :func:`add_party` instead to get a list of party entities. """ party_entity = self._obj_from_str(party) if party_entity is None: return #Check if there is an 'id' column party_id = self._entity_id_column(party_entity) LOGGER.debug('Attempting to set %s entity as the party.', party_entity.name) if party_id is None: err = self.tr('%s does not have an id column. This is required ' 'in order to link it to the social tenure ' 'relationship table.'%(party_entity.name)) LOGGER.debug(err) raise AttributeError(err) self._party = party_entity #Set parent attributes self.party_foreign_key.set_entity_relation_attr('parent', self._party) self.party_foreign_key.set_entity_relation_attr('parent_column', 'id') LOGGER.debug('%s entity has been successfully set as the party in ' 'the %s profile social tenure relationship.', party_entity.name, self.profile.name) @spatial_unit.setter def spatial_unit(self, spatial_unit): """ Sets the corresponding spatial unit entity in the social tenure relationship. .. deprecated:: 1.7 :param spatial_unit: Spatial unit entity. .. note:: The spatial unit entity must contain a geometry column else it will not be set. """ spatial_unit_entity = self._obj_from_str(spatial_unit) if spatial_unit_entity is None: return #check if there is an 'id' column sp_unit_id = self._entity_id_column(spatial_unit_entity) LOGGER.debug('Attempting to set %s entity as the spatial unit.', spatial_unit_entity.name) if sp_unit_id is None: err = self.tr('%s does not have an id column. This is required ' 'in order to link it to the social tenure ' 'relationship table.'%(spatial_unit_entity.name)) LOGGER.debug(err) raise AttributeError(err) if not spatial_unit_entity.has_geometry_column(): spatial_unit_entity = None return #err = self.tr('%s does not have a geometry column. This is required' #' when setting the spatial unit entity in a ' #'social tenure relationship definition.' #%(spatial_unit_entity.name)) #LOGGER.debug(err) #raise AttributeError(err) self._spatial_unit = spatial_unit_entity #Set parent attributes self.spatial_unit_foreign_key.set_entity_relation_attr( 'parent', self._spatial_unit ) self.spatial_unit_foreign_key.set_entity_relation_attr( 'parent_column', 'id' ) LOGGER.debug('%s entity has been successfully set as the spatial ' 'unit in the %s profile social tenure relationship.', spatial_unit_entity.name, self.profile.name) def _obj_from_str(self, item): """Create corresponding table item from string.""" obj = item if isinstance(item, (str, unicode)): if not item: return None obj = self.profile.entity(item) return obj def _entity_id_column(self, entity): """ Check if the entity has an ID column and return it, else returns None. """ return entity.column('id') def _prepare_tenure_type_value_list(self): #Create tenure types lookup table tenure_value_list = ValueList(self.tenure_type_list, self.profile) #Set lookup column reference value list self.tenure_type_lookup.value_list = tenure_value_list return tenure_value_list def valid(self): """ :return: Returns True if the party and spatial unit entities have been set, else returns False. :rtype: bool """ if len(self._party_fk_columns) == 0: return False if len(self._spatial_unit_fk_columns) == 0: return False return True def delete_view(self, engine): """ Deletes the basic view associated with the current social tenure object. :param engine: SQLAlchemy connectable object. :type engine: Engine """ self.view_remover(engine) def create_view(self, engine): """ Creates a basic view linking all the social tenure relationship entities. :param engine: SQLAlchemy connectable object. :type engine: Engine """ self.view_creator(engine)
class SocialTenure(Entity): """ Represents the relationship between party and spatial unit entities through social tenure relationship. It also supports the attachment of supporting documents. Main class that represents 'people-land' relationships. """ TYPE_INFO = 'SOCIAL_TENURE' PARTY, SPATIAL_UNIT, SOCIAL_TENURE_TYPE = range(0,3) BASE_STR_VIEW = 'vw_social_tenure_relationship' tenure_type_list = 'tenure_type' view_creator = view_updater view_remover = view_deleter def __init__(self, name, profile, supports_documents=True, layer_display=''): Entity.__init__(self, name, profile, supports_documents=supports_documents) self.user_editable = False self._party = None self._spatial_unit = None self._view_name = u'{0}_{1}'.format( self.profile.name.replace(' ', '_').lower(), SocialTenure.BASE_STR_VIEW ) self.party_foreign_key = ForeignKeyColumn('party_id', self) self.spatial_unit_foreign_key = ForeignKeyColumn('spatial_unit_id', self) self.tenure_type_lookup = LookupColumn('tenure_type', self) self.layer_display_name = layer_display self._value_list = self._prepare_tenure_type_value_list() #Add the value list to the table collection self.profile.add_entity(self._value_list) #Add columns to the collection self.add_column(self.party_foreign_key) self.add_column(self.spatial_unit_foreign_key) self.add_column(self.tenure_type_lookup) #Specify if a spatial unit should only be linked to one party self.multi_party = True LOGGER.debug('Social Tenure Relationship initialized for %s profile.', self.profile.name) def layer_display(self): """ :return: Name to show in the Layers TOC. :rtype: str """ if self.layer_display_name: return self.layer_display_name return self.view_name @property def view_name(self): return self._view_name @property def party(self): return self._party @property def spatial_unit(self): return self._spatial_unit @property def tenure_type_collection(self): return self._value_list @tenure_type_collection.setter def tenure_type_collection(self, value_list): #Copy the look up values from the given value list value_list_entity = self._obj_from_str(value_list) self._value_list.copy_from(value_list_entity) @party.setter def party(self, party): party_entity = self._obj_from_str(party) if party_entity is None: return #Check if there is an 'id' column party_id = self._entity_id_column(party_entity) LOGGER.debug('Attempting to set %s entity as the party.', party_entity.name) if party_id is None: err = self.tr('%s does not have an id column. This is required ' 'in order to link it to the social tenure ' 'relationship table.'%(party_entity.name)) LOGGER.debug(err) raise AttributeError(err) self._party = party_entity #Set parent attributes self.party_foreign_key.set_entity_relation_attr('parent', self._party) self.party_foreign_key.set_entity_relation_attr('parent_column', 'id') LOGGER.debug('%s entity has been successfully set as the party in ' 'the %s profile social tenure relationship.', party_entity.name, self.profile.name) @spatial_unit.setter def spatial_unit(self, spatial_unit): """ Sets the corresponding spatial unit entity in the social tenure relationship. :param spatial_unit: Spatial unit entity. .. note:: The spatial unit entity must contain a geometry column else it will not be set. """ spatial_unit_entity = self._obj_from_str(spatial_unit) if spatial_unit_entity is None: return #check if there is an 'id' column sp_unit_id = self._entity_id_column(spatial_unit_entity) LOGGER.debug('Attempting to set %s entity as the spatial unit.', spatial_unit_entity.name) if sp_unit_id is None: err = self.tr('%s does not have an id column. This is required ' 'in order to link it to the social tenure ' 'relationship table.'%(spatial_unit_entity.name)) LOGGER.debug(err) raise AttributeError(err) if not spatial_unit_entity.has_geometry_column(): err = self.tr('%s does not have a geometry column. This is required' ' when setting the spatial unit entity in a ' 'social tenure relationship definition.' %(spatial_unit_entity.name)) LOGGER.debug(err) raise AttributeError(err) self._spatial_unit = spatial_unit_entity #Set parent attributes self.spatial_unit_foreign_key.set_entity_relation_attr('parent', self._spatial_unit) self.spatial_unit_foreign_key.set_entity_relation_attr( 'parent_column', 'id') LOGGER.debug('%s entity has been successfully set as the spatial unit ' 'in the %s profile social tenure relationship.', spatial_unit_entity.name, self.profile.name) def _obj_from_str(self, item): """Create corresponding table item from string.""" obj = item if isinstance(item, (str, unicode)): if not item: return None obj = self.profile.entity(item) return obj def _entity_id_column(self, entity): """ Check if the entity has an ID column and return it, else returns None. """ return entity.column('id') def _prepare_tenure_type_value_list(self): #Create tenure types lookup table tenure_value_list = ValueList(self.tenure_type_list, self.profile) #Set lookup column reference value list self.tenure_type_lookup.value_list = tenure_value_list return tenure_value_list def valid(self): """ :return: Returns True if the party and spatial unit entities have been set, else returns False. :rtype: bool """ if self._party is None: return False if self._spatial_unit is None: return False return True def delete_view(self, engine): """ Deletes the basic view associated with the current social tenure object. :param engine: SQLAlchemy connectable object. :type engine: Engine """ self.view_remover(engine) def create_view(self, engine): """ Creates a basic view linking all the social tenure relationship entities. :param engine: SQLAlchemy connectable object. :type engine: Engine """ self.view_creator(engine)
def populate_configuration(config): profile = add_basic_profile(config) rel = create_relation(profile) person_entity = add_person_entity(profile) append_person_columns(person_entity) household_entity = add_household_entity(profile) rel.parent = household_entity rel.child = person_entity rel.child_column = 'household_id' rel.parent_column = 'id' profile.add_entity_relation(rel) #Add save option lookup save_options = create_value_list(profile, 'save_options') save_options.add_value('House') save_options.add_value('Bank') save_options.add_value('SACCO') profile.add_entity(save_options) #Add save options to multiple select column to person entity save_options_column = MultipleSelectColumn('save_location', person_entity) save_options_column.value_list = save_options person_entity.add_column(save_options_column) #Add community entity community = add_community_entity(profile) append_community_columns(community) # Append surveyor columns surveyor = add_surveyor_entity(profile) append_surveyor_columns(surveyor) spatial_unit = add_spatial_unit_entity(profile) spatial_unit_2 = add_spatial_unit_entity_2(profile) # Add foreign key linking spatial unit to surveyor surveyor_id_col = ForeignKeyColumn('surveyor_id', spatial_unit) surveyor_id_col.set_entity_relation_attr('parent', surveyor) surveyor_id_col.set_entity_relation_attr('parent_column', 'id') spatial_unit.add_column(surveyor_id_col) # Set STR entities profile.set_social_tenure_attr(SocialTenure.PARTY, [person_entity, community]) profile.set_social_tenure_attr( SocialTenure.SPATIAL_UNIT, [spatial_unit, spatial_unit_2] ) # Create and add secondary tenure lookup sec_tenure_vl = add_secondary_tenure_value_list(profile) # Map secondary tenure to spatial unit 2 profile.social_tenure.add_spatial_tenure_mapping(spatial_unit_2, sec_tenure_vl) # Create custom attr entity for primary tenure lookup primary_tenure_vl = profile.social_tenure.tenure_type_collection p_custom_ent = profile.social_tenure.custom_attribute_entity( primary_tenure_vl ) if p_custom_ent is None: profile.social_tenure.initialize_custom_attributes_entity( primary_tenure_vl ) # Set entity p_custom_ent = profile.social_tenure.custom_attribute_entity( primary_tenure_vl ) # Add custom attributes constitution_ref_col = TextColumn('constitution_ref', p_custom_ent) p_custom_ent.add_column(constitution_ref_col) # Create secondary tenure custom attributes entity s_custom_ent = profile.social_tenure.custom_attribute_entity( sec_tenure_vl ) if s_custom_ent is None: profile.social_tenure.initialize_custom_attributes_entity( sec_tenure_vl ) # Set entity s_custom_ent = profile.social_tenure.custom_attribute_entity( sec_tenure_vl ) application_date_col = DateColumn('application_date', s_custom_ent) s_custom_ent.add_column(application_date_col)