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)
Beispiel #2
0
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)
Beispiel #3
0
    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)
Beispiel #4
0
    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)
Beispiel #5
0
    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 = '{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)
Beispiel #6
0
    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
Beispiel #7
0
    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
Beispiel #8
0
    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
Beispiel #9
0
    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
Beispiel #10
0
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)
Beispiel #11
0
    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
Beispiel #12
0
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)
Beispiel #13
0
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)
Beispiel #14
0
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')
Beispiel #15
0
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)
Beispiel #16
0
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)
Beispiel #17
0
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)