Ejemplo n.º 1
0
class Emplacement(models.Model, BaseModel, SourcesMixin, SuperlativeDateMixin,
                  GetComplexFieldNameMixin):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.startdate = ComplexFieldContainer(self, EmplacementStartDate)
        self.enddate = ComplexFieldContainer(self, EmplacementEndDate)
        self.realstart = ComplexFieldContainer(self, EmplacementRealStart)
        self.open_ended = ComplexFieldContainer(self, EmplacementOpenEnded)
        self.organization = ComplexFieldContainer(self,
                                                  EmplacementOrganization)
        self.site = ComplexFieldContainer(self, EmplacementSite)
        self.aliases = ComplexFieldListContainer(self, EmplacementAlias)

        self.complex_fields = [
            self.startdate, self.enddate, self.organization, self.site,
            self.open_ended, self.realstart
        ]

        self.complex_lists = [self.aliases]

        self.required_fields = [
            "Emplacement_EmplacementOrganization",
            "Emplacement_EmplacementSite",
        ]

    def get_value(self):
        return '{0} ({1})'.format(self.organization.get_value(),
                                  self.site.get_value())
Ejemplo n.º 2
0
class Composition(models.Model, BaseModel, SourcesMixin,
                  GetComplexFieldNameMixin):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.parent = ComplexFieldContainer(self, CompositionParent)
        self.child = ComplexFieldContainer(self, CompositionChild)
        self.startdate = ComplexFieldContainer(self, CompositionStartDate)
        self.realstart = ComplexFieldContainer(self, CompositionRealStart)
        self.enddate = ComplexFieldContainer(self, CompositionEndDate)
        self.open_ended = ComplexFieldContainer(self, CompositionOpenEnded)
        self.classification = ComplexFieldContainer(self,
                                                    CompositionClassification)

        self.complex_fields = [
            self.parent, self.child, self.startdate, self.realstart,
            self.enddate, self.open_ended, self.classification
        ]

        self.complex_lists = []

        self.required_fields = [
            "Composition_CompositionParent", "Composition_CompositionChild"
        ]

    def get_value(self):
        return '{0} parent of {1}'.format(self.parent.get_value(),
                                          self.child.get_value())
Ejemplo n.º 3
0
class Geosite(models.Model, BaseModel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = ComplexFieldContainer(self, GeositeName)

        # OSM Name/ID of the smallest containing administrative unit
        self.admin_name = ComplexFieldContainer(self, GeositeAdminName)
        self.admin_id = ComplexFieldContainer(self, GeositeAdminId)

        # Larger administrative units (name only)
        self.adminlevel1 = ComplexFieldContainer(self, GeositeAdminLevel1)
        self.adminlevel2 = ComplexFieldContainer(self, GeositeAdminLevel2)

        # Coordinates and OSM Name/ID of the exact location
        self.coordinates = ComplexFieldContainer(self, GeositeCoordinates)
        self.location_name = ComplexFieldContainer(self, GeositeLocationName)
        self.location_id = ComplexFieldContainer(self, GeositeLocationId)

        self.division_id = ComplexFieldContainer(self, GeositeDivisionId)

        self.complex_fields = [
            self.name, self.admin_name, self.admin_id, self.adminlevel1,
            self.adminlevel2, self.coordinates, self.location_name,
            self.location_id, self.division_id
        ]

        self.complex_lists = []

        self.required_fields = ["Geosite_GeositeName"]

    def get_value(self):
        return self.name.get_value()

    def __str__(self):
        return str(self.name)
Ejemplo n.º 4
0
class Area(models.Model, BaseModel, GetComplexFieldNameMixin):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = ComplexFieldContainer(self, AreaName)
        self.code = ComplexFieldContainer(self, AreaCode)
        self.osmname = ComplexFieldContainer(self, AreaOSMName)
        self.osmid = ComplexFieldContainer(self, AreaOSMId)
        self.geometry = ComplexFieldContainer(self, AreaGeometry)
        self.division_id = ComplexFieldContainer(self, AreaDivisionId)

        self.complex_fields = [
            self.name, self.code, self.osmname, self.geometry,
            self.division_id, self.osmid
        ]

        self.complex_lists = []

        self.required_fields = [
            "Area_AreaName",
        ]

    def get_value(self):
        return self.name.get_value()

    def __str__(self):
        return str(self.name)

    @classmethod
    def from_id(cls, id_):
        try:
            area = cls.objects.get(id=id_)
            return area
        except cls.DoesNotExist:
            return None
Ejemplo n.º 5
0
class Area(models.Model, BaseModel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = ComplexFieldContainer(self, AreaName)
        self.code = ComplexFieldContainer(self, AreaCode)
        self.osmname = ComplexFieldContainer(self, AreaOSMName)
        self.osmid = ComplexFieldContainer(self, AreaOSMId)
        self.geometry = ComplexFieldContainer(self, AreaGeometry)
        self.division_id = ComplexFieldContainer(self, AreaDivisionId)

        self.complex_fields = [self.name, self.code, self.osmname,
                               self.geometry, self.division_id, self.osmid]

        self.complex_lists = []

        self.required_fields = [
            "Area_AreaName",
        ]

    def get_value(self):
        return self.name.get_value()

    def __str__(self):
        return str(self.name)

    @classmethod
    def from_id(cls, id_):
        try:
            area = cls.objects.get(id=id_)
            return area
        except cls.DoesNotExist:
            return None
Ejemplo n.º 6
0
class MembershipOrganization(models.Model, BaseModel, SourcesMixin, GetComplexFieldNameMixin):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.member = ComplexFieldContainer(self, MembershipOrganizationMember)
        self.organization = ComplexFieldContainer(self, MembershipOrganizationOrganization)
        self.firstciteddate = ComplexFieldContainer(self, MembershipOrganizationFirstCitedDate)
        self.realstart = ComplexFieldContainer(self, MembershipOrganizationRealStart)
        self.lastciteddate = ComplexFieldContainer(self, MembershipOrganizationLastCitedDate)
        self.open_ended = ComplexFieldContainer(self, MembershipOrganizationOpenEnded)

        self.complex_fields = [self.member, self.organization,
                               self.firstciteddate, self.lastciteddate,
                               self.realstart, self.open_ended]

        self.complex_lists = []

        self.required_fields = [
            "MembershipOrganization_MembershipOrganizationMember",
            "MembershipOrganization_MembershipOrganizationOrganization",
        ]

    @classmethod
    def from_id(cls, id_):
        try:
            membership = cls.objects.get(id=id_)
            return membership
        except cls.DoesNotExist:
            return None

    def get_value(self):
        return '{0} member of {1}'.format(self.member.get_value(),
                                          self.organization.get_value())

    @classmethod
    def create(cls, dict_values, lang=get_language()):
        membership = cls()
        membership.update(dict_values, lang)
        return membership
Ejemplo n.º 7
0
class MembershipOrganization(models.Model, BaseModel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.member = ComplexFieldContainer(self, MembershipOrganizationMember)
        self.organization = ComplexFieldContainer(self, MembershipOrganizationOrganization)
        self.firstciteddate = ComplexFieldContainer(self, MembershipOrganizationFirstCitedDate)
        self.realstart = ComplexFieldContainer(self, MembershipOrganizationRealStart)
        self.lastciteddate = ComplexFieldContainer(self, MembershipOrganizationLastCitedDate)
        self.realend = ComplexFieldContainer(self, MembershipOrganizationRealEnd)

        self.complex_fields = [self.member, self.organization,
                               self.firstciteddate, self.lastciteddate,
                               self.realstart, self.realend]

        self.complex_lists = []

        self.required_fields = [
            "MembershipOrganization_MembershipOrganizationMember",
            "MembershipOrganization_MembershipOrganizationOrganization",
        ]

    @classmethod
    def from_id(cls, id_):
        try:
            membership = cls.objects.get(id=id_)
            return membership
        except cls.DoesNotExist:
            return None

    def get_value(self):
        return '{0} member of {1}'.format(self.member.get_value(),
                                          self.organization.get_value())

    @classmethod
    def create(cls, dict_values, lang=get_language()):
        membership = cls()
        membership.update(dict_values, lang)
        return membership
Ejemplo n.º 8
0
class Composition(models.Model, BaseModel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.parent = ComplexFieldContainer(self, CompositionParent)
        self.child = ComplexFieldContainer(self, CompositionChild)
        self.startdate = ComplexFieldContainer(self, CompositionStartDate)
        self.realstart = ComplexFieldContainer(self, CompositionRealStart)
        self.enddate = ComplexFieldContainer(self, CompositionEndDate)
        self.open_ended = ComplexFieldContainer(self, CompositionOpenEnded)
        self.classification = ComplexFieldContainer(self, CompositionClassification)

        self.complex_fields = [self.parent, self.child, self.startdate,
                               self.realstart, self.enddate, self.open_ended,
                               self.classification]

        self.complex_lists = []

        self.required_fields = [
            "Composition_CompositionParent", "Composition_CompositionChild"
        ]

    def get_value(self):
        return '{0} parent of {1}'.format(self.parent.get_value(),
                                          self.child.get_value())
Ejemplo n.º 9
0
class Emplacement(models.Model, BaseModel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.startdate = ComplexFieldContainer(self, EmplacementStartDate)
        self.enddate = ComplexFieldContainer(self, EmplacementEndDate)
        self.realstart = ComplexFieldContainer(self, EmplacementRealStart)
        self.open_ended = ComplexFieldContainer(self, EmplacementOpenEnded)
        self.organization = ComplexFieldContainer(self, EmplacementOrganization)
        self.site = ComplexFieldContainer(self, EmplacementSite)
        self.aliases = ComplexFieldListContainer(self, EmplacementAlias)

        self.complex_fields = [self.startdate, self.enddate, self.organization,
                               self.site, self.open_ended, self.realstart]

        self.complex_lists = [self.aliases]

        self.required_fields = [
            "Emplacement_EmplacementOrganization",
            "Emplacement_EmplacementSite",
        ]

    def get_value(self):
        return '{0} ({1})'.format(self.organization.get_value(),
                                  self.site.get_value())
Ejemplo n.º 10
0
class Person(models.Model, BaseModel, VersionsMixin):

    uuid = models.UUIDField(default=uuid.uuid4,
                            editable=False,
                            db_index=True)

    published = models.BooleanField(default=False)

    def __init__(self, *args, **kwargs):
        self.name = ComplexFieldContainer(self, PersonName)
        self.aliases = ComplexFieldListContainer(self, PersonAlias)
        self.division_id = ComplexFieldContainer(self, PersonDivisionId)
        self.gender = ComplexFieldContainer(self, PersonGender)
        self.date_of_birth = ComplexFieldContainer(self, PersonDateOfBirth)
        self.date_of_death = ComplexFieldContainer(self, PersonDateOfDeath)
        self.deceased = ComplexFieldContainer(self, PersonDeceased)
        self.biography = ComplexFieldContainer(self, PersonBiography)
        self.notes = ComplexFieldContainer(self, PersonNotes)
        self.external_links = ComplexFieldListContainer(self, PersonExternalLink)

        self.complex_fields = [
            self.name,
            self.division_id,
            self.gender,
            self.date_of_birth,
            self.date_of_death,
            self.deceased,
            self.biography,
            self.notes,
        ]
        self.complex_lists = [self.aliases, self.external_links]

        self.required_fields = [
            "Person_PersonName",
        ]

        super().__init__(*args, **kwargs)

    def get_value(self):
        return self.name.get_value()

    def __str__(self):
        try:
            return str(self.personname_set.first().value)
        except AttributeError:
            return str(self.uuid)

    def get_absolute_url(self):
        return reverse('view-person', kwargs={'slug': self.uuid})

    @cached_property
    def memberships(self):
        '''
        Return all of this person's memberships, in a custom sorting order.

        Order by first cited date descending, then last cited date descending,
        with nulls last.
        '''
        mems = self.membershippersonmember_set\
                   .select_related('object_ref')\
                   .annotate(lcd=Coalesce('object_ref__membershippersonfirstciteddate__value',
                                          'object_ref__membershippersonlastciteddate__value',
                                          models.Value('1000-0-0')))\
                   .order_by('-lcd')

        return mems

    @property
    def last_cited(self):
        '''
        Get the global last citation date for this person, leaving out nulls.
        '''
        order = '-object_ref__membershippersonlastciteddate__value'
        memberships = self.membershippersonmember_set.order_by(order)
        for membership in memberships:
            # Filter nulls
            lcd = membership.object_ref.lastciteddate.get_value()
            if lcd:
                return lcd

    @property
    def first_cited(self):
        '''
        Get the global first citation date for this person, leaving out nulls.
        '''
        order = 'object_ref__membershippersonfirstciteddate__value'
        memberships = self.membershippersonmember_set.order_by(order)
        for membership in memberships:
            fcd = membership.object_ref.firstciteddate.get_value()
            if fcd:
                return fcd
Ejemplo n.º 11
0
class Area(models.Model, BaseModel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = ComplexFieldContainer(self, AreaName)
        self.code = ComplexFieldContainer(self, AreaCode)
        self.geoname = ComplexFieldContainer(self, AreaGeoName)
        self.geometry = ComplexFieldContainer(self, AreaGeometry)

        self.complex_fields = [
            self.name, self.code, self.geoname, self.geometry
        ]

        self.required_fields = [
            "Area_AreaName",
        ]

    def get_value(self):
        return self.name.get_value()

    def __str__(self):
        return str(self.name)

    @classmethod
    def from_id(cls, id_):
        try:
            area = cls.objects.get(id=id_)
            return area
        except cls.DoesNotExist:
            return None

    def validate(self, dict_values):
        errors = {}

        coordinates = dict_values['Area_AreaGeometry'].get("value")
        if coordinates:
            try:
                poly = geos.fromstr(coordinates)
                if not isinstance(poly, geos.Polygon):
                    errors["Area_AreaGeometry"] = (
                        "The geometry must be a polygon, not a point")
            except TypeError:
                errors["Area_AreaGeometry"] = ("Invalid data for a polygon")

        (base_errors, values) = super().validate(dict_values)
        errors.update(base_errors)

        return (errors, values)

    @classmethod
    def search(cls, terms):
        order_by = terms.get('orderby')
        if not order_by:
            order_by = 'areaname__value'
        elif order_by in ['name']:
            order_by = 'area' + order_by + '__value'

        direction = terms.get('direction')
        if not direction:
            direction = 'ASC'

        dirsym = ''
        if direction == 'DESC':
            dirsym = '-'

        area_query = (Area.objects.annotate(
            Max(order_by)).order_by(dirsym + order_by + "__max"))

        name = terms.get('name')
        if name:
            area_query = area_query.filter(areaname__value__icontains=name)

        code = terms.get('classification')
        if code:
            area_query = area_query.filter(areacode__value=code)

        latitude = terms.get('latitude')
        longitude = terms.get('longitude')
        if latitude and longitude:
            try:
                latitude = float(latitude)
                longitude = float(longitude)
            except ValueError:
                latitude = 0
                longitude = 0

            point = geos.Point(latitude, longitude)
            radius = terms.get('radius')
            if radius:
                try:
                    radius = float(radius)
                except ValueError:
                    radius = 0
                area_query = area_query.filter(
                    areageometry__value__dwithin=(point, radius))
            else:
                area_query = area_query.filter(
                    areageometry__value__bbcontains=point)

        return area_query
Ejemplo n.º 12
0
class Violation(models.Model, BaseModel, SourcesMixin, VersionsMixin,
                GetComplexFieldNameMixin):
    uuid = models.UUIDField(default=uuid.uuid4, editable=False, db_index=True)

    published = models.BooleanField(default=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # Dates and status
        self.startdate = ComplexFieldContainer(self, ViolationStartDate)
        self.first_allegation = ComplexFieldContainer(
            self, ViolationFirstAllegation)
        self.enddate = ComplexFieldContainer(self, ViolationEndDate)
        self.last_update = ComplexFieldContainer(self, ViolationLastUpdate)
        self.status = ComplexFieldContainer(self, ViolationStatus)

        # Location fields
        self.locationdescription = ComplexFieldContainer(
            self, ViolationLocationDescription)
        self.adminlevel1 = ComplexFieldContainer(self, ViolationAdminLevel1)
        self.adminlevel2 = ComplexFieldContainer(self, ViolationAdminLevel2)
        self.osmname = ComplexFieldContainer(self, ViolationOSMName)
        self.osmid = ComplexFieldContainer(self, ViolationOSMId)
        self.division_id = ComplexFieldContainer(self, ViolationDivisionId)
        self.location = ComplexFieldContainer(self, ViolationLocation)
        self.location_name = ComplexFieldContainer(self, ViolationLocationName)
        self.location_id = ComplexFieldContainer(self, ViolationLocationId)

        # Descriptions and other attributes
        self.description = ComplexFieldContainer(self, ViolationDescription)
        self.perpetrator = ComplexFieldListContainer(self,
                                                     ViolationPerpetrator)
        self.perpetratororganization = ComplexFieldListContainer(
            self, ViolationPerpetratorOrganization)
        self.perpetratorclassification = ComplexFieldListContainer(
            self, ViolationPerpetratorClassification)
        self.types = ComplexFieldListContainer(self, ViolationType)

        self.complex_fields = [
            self.startdate, self.first_allegation, self.enddate,
            self.last_update, self.status, self.locationdescription,
            self.adminlevel1, self.adminlevel2, self.location,
            self.description, self.division_id
        ]

        self.complex_lists = [
            self.perpetrator, self.perpetratororganization,
            self.perpetratorclassification, self.types
        ]

        self.required_fields = [self.description, self.startdate, self.enddate]

    def get_value(self):
        return self.description.get_value()

    @property
    def related_entities(self):
        """
        Return a list of dicts with metadata for all of the entities linked to
        this Violation.

        Metadata dicts must have the following keys:
            - name
            - entity_type
            - url (a link to edit the entity)
        """
        related_entities = []

        # Second-highest administrative level for the location of the violation.
        if self.adminlevel1.get_value():
            location = self.adminlevel1.get_value().value
            related_entities.append({
                'name':
                location.name,
                'entity_type':
                _('AdminLevel1'),
                'url':
                reverse('edit-violation', args=[self.uuid])
            })

        # Highest administrative level for the location of the violation.
        if self.adminlevel2.get_value():
            location = self.adminlevel2.get_value().value
            related_entities.append({
                'name':
                location.name,
                'entity_type':
                _('AdminLevel2'),
                'url':
                reverse('edit-violation', args=[self.uuid])
            })

        # The location of the violation.
        if self.location.get_value():
            location = self.location.get_value().value
            related_entities.append({
                'name':
                location.name,
                'entity_type':
                _('Location'),
                'url':
                reverse('edit-violation', args=[self.uuid])
            })

        # The perpetrators of the violation (personnel).
        perpetrators = self.perpetrator.get_list()
        if perpetrators:
            perpetrators = [
                perp.get_value() for perp in perpetrators if perp.get_value()
            ]
            for perpetrator in perpetrators:
                person = perpetrator.value
                related_entities.append({
                    'name':
                    person.name.get_value().value,
                    'entity_type':
                    _('Perpetrator'),
                    'url':
                    reverse('edit-violation', args=[self.uuid])
                })

        # The perpetrators of the violation (organizations).
        perpetratororganizations = self.perpetratororganization.get_list()
        if perpetratororganizations:
            perpetrators = [
                perp.get_value() for perp in perpetratororganizations
                if perp.get_value()
            ]
            for perpetrator in perpetrators:
                org = perpetrator.value
                related_entities.append({
                    'name':
                    org.name.get_value().value,
                    'entity_type':
                    _('PerpetratorOrganization'),
                    'url':
                    reverse('edit-violation', args=[self.uuid])
                })

        return related_entities
Ejemplo n.º 13
0
class MembershipPerson(models.Model, BaseModel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.member = ComplexFieldContainer(self, MembershipPersonMember)
        self.organization = ComplexFieldContainer(self, MembershipPersonOrganization)
        self.role = ComplexFieldContainer(self, MembershipPersonRole)
        self.title = ComplexFieldContainer(self, MembershipPersonTitle)
        self.rank = ComplexFieldContainer(self, MembershipPersonRank)
        self.realstart = ComplexFieldContainer(self, MembershipPersonRealStart)
        self.realend = ComplexFieldContainer(self, MembershipPersonRealEnd)
        self.startcontext = ComplexFieldContainer(self, MembershipPersonStartContext)
        self.endcontext = ComplexFieldContainer(self, MembershipPersonEndContext)
        self.firstciteddate = ComplexFieldContainer(self, MembershipPersonFirstCitedDate)
        self.lastciteddate = ComplexFieldContainer(self, MembershipPersonLastCitedDate)

        self.complex_fields = [self.member, self.organization, self.role,
                               self.title, self.rank, self.realstart, self.realend,
                               self.startcontext, self.endcontext,
                               self.firstciteddate, self.lastciteddate]

        self.complex_lists = []

        self.required_fields = [
            "MembershipPerson_MembershipPersonMember",
            "MembershipPerson_MembershipPersonOrganization",
        ]

    @property
    def short_description(self):
        '''
        Get a description string (as HTML) for information on this membership,
        such as:

        "General Officer Commanding, Major General, Commander of 82 Division
        (Military/Army, Nigeria) on 1st January 2013"
        '''

        obj = self

        # Start with the epithets (title, rank, and role)
        description = '<strong>'

        epithets = [obj.title.get_value(),
                    obj.rank.get_value(),
                    obj.role.get_value()]

        epithets = [str(ep.value) for ep in epithets if ep is not None]

        if len(epithets) == 1:
            description += epithets[0] + '</strong>'

        elif len(epithets) == 2 or len(epithets) == 3:
            separator = '</strong>, <strong>'
            description += separator.join(epithets) + '</strong>'

        else:
            # Length must be 0, so use a generic title
            description = _('Member')

        # Member organization
        description += ' '

        organization = obj.organization.get_value().value

        href = reverse('view-organization', args=(organization.uuid,))

        # Translators: This is part of the string "{Rank} of {unit}"
        joiner = _('of')

        org_string = joiner + ' <strong><a href="{href}">{org}</a></strong>'

        description += org_string.format(org=organization,
                                         href=href)

        # Classifications
        description += ' '

        classifications = organization.classification.get_list()
        if classifications:
            classifications = '/'.join(str(clss) for clss in classifications
                                       if clss is not None)

            description += '(%s)' % classifications

        # Last cited date
        description += ' '

        last_cited = obj.lastciteddate.get_value()
        if last_cited:
            # Translators: This is part of the "Last seen as" string for a person,
            # as in "last seen as {rank} of {unit} on {date}"
            date_joiner = _('on')
            date_string = date_joiner + ' <strong>%s</strong>' % last_cited
            description += date_string

        return description

    @classmethod
    def from_id(cls, id_):
        try:
            membership = cls.objects.get(id=id_)
            return membership
        except cls.DoesNotExist:
            return None

    def get_value(self):
        return '{0} member of {1}'.format(self.member.get_value(),
                                          self.organization.get_value())

    @classmethod
    def create(cls, dict_values, lang=get_language()):
        membership = cls()
        membership.update(dict_values, lang)
        return membership
Ejemplo n.º 14
0
class Person(models.Model, BaseModel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = ComplexFieldContainer(self, PersonName)
        self.alias = ComplexFieldContainer(self, PersonAlias)
        self.deathdate = ComplexFieldContainer(self, PersonDeathDate)
        self.complex_fields = [self.name, self.alias, self.deathdate]

        self.required_fields = [
            "Person_PersonName",
        ]

    def get_value(self):
        return self.name.get_value()

    def __str__(self):
        return str(self.name)

    @classmethod
    def search(cls, terms):
        order_by = terms.get('orderby')
        if not order_by:
            order_by = 'personname__value'
        elif order_by in ['name']:
            order_by = 'person' + order_by + '__value'

        direction = terms.get('direction')
        if not direction:
            direction = 'ASC'

        dirsym = ''
        if direction == 'DESC':
            dirsym = '-'

        person_query = (cls.objects.annotate(
            Max(order_by)).order_by(dirsym + order_by + "__max"))

        name = terms.get('name')
        if name:
            person_query = person_query.filter(
                personname__value__icontains=name)

        alias_val = terms.get('alias')
        if alias_val:
            person_query = person_query.filter(
                personalias__value__icontains=alias_val)

        deathdate_year = terms.get('deathdate_year')
        if deathdate_year:
            person_query = person_query.filter(
                persondeathdate__value__startswith=deathdate_year)

        deathdate_month = terms.get('deathdate_month')
        if deathdate_month:
            person_query = person_query.filter(
                persondeathdate__value__contains="-" + deathdate_month + "-")

        deathdate_day = terms.get('deathdate_day')
        if deathdate_day:
            person_query = person_query.filter(
                persondeathdate__value__endswith=deathdate_day)

        role = terms.get('role_membership')
        if role:
            person_query = person_query.filter(
                membershippersonmember__object_ref__membershiprole__value__value
                =role)

        latitude = terms.get('latitude')
        longitude = terms.get('longitude')
        if latitude and longitude:
            try:
                latitude = float(latitude)
                longitude = float(longitude)
            except ValueError:
                latitude = 0
                longitude = 0

            point = Point(latitude, longitude)
            radius = terms.get('radius')
            if radius:
                try:
                    radius = float(radius)
                except ValueError:
                    radius = 0
                person_query = person_query.filter(
                    membershippersonmember__object_ref__membershiporganization__value__associationorganization__object_ref__associationarea__value__areageometry__value__dwithin
                    =(point, radius))
            else:
                person_query = person_query.filter(
                    membershippersonmember__object_ref__membershiporganization__value__associationorganization__object_ref__associationarea__value__areageometry__value__bbcontains
                    =point)

        return person_query
Ejemplo n.º 15
0
class MembershipPerson(models.Model, BaseModel, SourcesMixin, GetComplexFieldNameMixin):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.member = ComplexFieldContainer(self, MembershipPersonMember)
        self.organization = ComplexFieldContainer(self, MembershipPersonOrganization)
        self.role = ComplexFieldContainer(self, MembershipPersonRole)
        self.title = ComplexFieldContainer(self, MembershipPersonTitle)
        self.rank = ComplexFieldContainer(self, MembershipPersonRank)
        self.realstart = ComplexFieldContainer(self, MembershipPersonRealStart)
        self.realend = ComplexFieldContainer(self, MembershipPersonRealEnd)
        self.startcontext = ComplexFieldContainer(self, MembershipPersonStartContext)
        self.endcontext = ComplexFieldContainer(self, MembershipPersonEndContext)
        self.firstciteddate = ComplexFieldContainer(self, MembershipPersonFirstCitedDate)
        self.lastciteddate = ComplexFieldContainer(self, MembershipPersonLastCitedDate)

        self.complex_fields = [self.member, self.organization, self.role,
                               self.title, self.rank, self.realstart, self.realend,
                               self.startcontext, self.endcontext,
                               self.firstciteddate, self.lastciteddate]

        self.complex_lists = []

        self.required_fields = [
            "MembershipPerson_MembershipPersonMember",
            "MembershipPerson_MembershipPersonOrganization",
        ]

    @property
    def short_description(self):
        '''
        Get a description string (as HTML) for information on this membership,
        such as:

        "General Officer Commanding, Major General, Commander of 82 Division
        (Military/Army, Nigeria) on 1st January 2013"
        '''

        obj = self

        # Start with the epithets (title, rank, and role)
        description = '<strong>'

        epithets = [obj.title.get_value(),
                    obj.rank.get_value(),
                    obj.role.get_value()]

        epithets = [str(ep.value) for ep in epithets if ep is not None]

        if len(epithets) == 1:
            description += epithets[0] + '</strong>'

        elif len(epithets) == 2 or len(epithets) == 3:
            separator = '</strong>, <strong>'
            description += separator.join(epithets) + '</strong>'

        else:
            # Length must be 0, so use a generic title
            description = _('Member')

        # Member organization
        description += ' '

        organization = obj.organization.get_value().value

        href = reverse('view-organization', args=(organization.uuid,))

        # Translators: This is part of the string "{Rank} of {unit}"
        joiner = _('of')

        org_string = joiner + ' <strong><a href="{href}">{org}</a></strong>'

        description += org_string.format(org=organization,
                                         href=href)

        # Classifications
        description += ' '

        classifications = organization.classification.get_list()
        if classifications:
            classifications = '/'.join(str(clss) for clss in classifications
                                       if clss is not None)

            description += '(%s)' % classifications

        # Last cited date
        description += ' '

        last_cited = obj.lastciteddate.get_value()
        if last_cited:
            # Translators: This is part of the "Last seen as" string for a person,
            # as in "last seen as {rank} of {unit} on {date}"
            date_joiner = _('on')
            date_string = date_joiner + ' <strong>%s</strong>' % last_cited
            description += date_string

        return description

    @classmethod
    def from_id(cls, id_):
        try:
            membership = cls.objects.get(id=id_)
            return membership
        except cls.DoesNotExist:
            return None

    def get_value(self):
        return '{0} member of {1}'.format(self.member.get_value(),
                                          self.organization.get_value())

    @classmethod
    def create(cls, dict_values, lang=get_language()):
        membership = cls()
        membership.update(dict_values, lang)
        return membership
Ejemplo n.º 16
0
class Organization(models.Model, BaseModel, VersionsMixin):

    uuid = models.UUIDField(default=uuid.uuid4,
                            editable=False,
                            db_index=True)

    published = models.BooleanField(default=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = ComplexFieldContainer(self, OrganizationName)
        self.aliases = ComplexFieldListContainer(self, OrganizationAlias)
        self.classification = ComplexFieldListContainer(self, OrganizationClassification)
        self.division_id = ComplexFieldContainer(self, OrganizationDivisionId)
        self.headquarters = ComplexFieldContainer(self, OrganizationHeadquarters)
        self.firstciteddate = ComplexFieldContainer(self, OrganizationFirstCitedDate)
        self.lastciteddate = ComplexFieldContainer(self, OrganizationLastCitedDate)
        self.realstart = ComplexFieldContainer(self, OrganizationRealStart)
        self.open_ended = ComplexFieldContainer(self, OrganizationOpenEnded)

        self.complex_fields = [self.name, self.division_id, self.firstciteddate,
                               self.lastciteddate, self.realstart, self.open_ended,
                               self.headquarters]

        self.complex_lists = [self.aliases, self.classification]

        self.required_fields = [
            "Organization_OrganizationName",
        ]

    def get_value(self):
        return self.name.get_value()

    def __str__(self):
        return str(self.name)

    @property
    def associations(self):
        '''
        Return all of this organization's associations, in a custom sorting order.

        Order by first cited date descending, then last cited date descending,
        with nulls last.
        '''
        assocs = self.associationorganization_set\
                     .annotate(lcd=Coalesce('object_ref__associationstartdate__value',
                                            'object_ref__associationenddate__value',
                                            models.Value('1000-0-0')))\
                     .order_by('-lcd')

        return assocs

    @property
    def emplacements(self):
        '''
        Return all of this organization's emplacements, in a custom sorting order.

        Order by first cited date descending, then last cited date descending,
        with nulls last.
        '''
        empls = self.emplacementorganization_set\
                    .annotate(lcd=Coalesce('object_ref__emplacementstartdate__value',
                                           'object_ref__emplacementenddate__value',
                                           models.Value('1000-0-0')))\
                    .order_by('-lcd')

        return empls

    @property
    def personnel(self):
        '''
        Returns all personnel ever assigned to a unit

        Objects returned are MembershipPerson objects
        '''
        return [o.object_ref for o in self.membershippersonorganization_set.all()]

    @property
    def alias_list(self):
        return ', '.join(a.get_value().value for a in self.aliases.get_list())
Ejemplo n.º 17
0
class Geosite(models.Model, BaseModel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = ComplexFieldContainer(self, GeositeName)
        self.adminlevel1 = ComplexFieldContainer(self, GeositeAdminLevel1)
        self.adminlevel2 = ComplexFieldContainer(self, GeositeAdminLevel2)
        self.coordinates = ComplexFieldContainer(self, GeositeCoordinates)
        self.geoname = ComplexFieldContainer(self, GeositeGeoname)
        self.geonameid = ComplexFieldContainer(self, GeositeGeonameId)

        self.complex_fields = [
            self.name, self.adminlevel1, self.adminlevel2, self.coordinates,
            self.geoname, self.geonameid
        ]

        self.required_fields = ["Geosite_GeositeName"]

    def get_value(self):
        return self.name.get_value()

    def __str__(self):
        return str(self.name)

    def validate(self, dict_values):
        errors = {}

        coordinates = dict_values['Geosite_GeositeCoordinates'].get("value")
        if coordinates:
            coord = json.loads(coordinates).get("coordinates")
            if coord:
                try:
                    geos.Point(coord)
                except TypeError:
                    errors["Geosite_GeositeCoordinates"] = (
                        "The coordinates must be a point, not a polygon")

        (base_errors, values) = super().validate(dict_values)
        errors.update(base_errors)

        return (errors, values)

    @classmethod
    def search(cls, terms):
        order_by = terms.get('orderby')
        if not order_by:
            order_by = 'geositename__value'
        elif order_by in ['name']:
            order_by = 'geosite' + order_by + '__value'

        direction = terms.get('direction')
        if not direction:
            direction = 'ASC'

        dirsym = ''
        if direction == 'DESC':
            dirsym = '-'

        geosite_query = (Geosite.objects.annotate(
            Max(order_by)).order_by(dirsym + order_by + "__max"))

        name = terms.get('name')
        if name:
            geosite_query = geosite_query.filter(
                geositename__value__icontains=name)

        admin1 = terms.get('adminlevel1')
        if admin1:
            geosite_query = geosite_query.filter(
                geositeadminlevel1__value__icontains=admin1)

        admin2 = terms.get('adminlevel2')
        if admin2:
            geosite_query = geosite_query.filter(
                geositeadminlevel2__value__icontains=admin2)

        latitude = terms.get('latitude')
        longitude = terms.get('longitude')
        if latitude and longitude:
            try:
                latitude = float(latitude)
                longitude = float(longitude)
            except ValueError:
                latitude = 0
                longitude = 0

            point = geos.Point(latitude, longitude)
            radius = terms.get('radius')
            if radius:
                try:
                    radius = float(radius)
                except ValueError:
                    radius = 0
                geosite_query = geosite_query.filter(
                    geositecoordinates__value__dwithin=(point, radius))
            else:
                geosite_query = geosite_query.filter(
                    geositecoordinates__value__bbcontains=point)

        geoname = terms.get('geoname')
        if geoname:
            geosite_query = geosite_query.filter(
                geositegeoname__value__icontains=geoname)

        geonameid = terms.get('geonameid')
        if geonameid:
            geosite_query = geosite_query.filter(
                geositegeonameid__value=geonameid)

        return geosite_query
Ejemplo n.º 18
0
class Organization(models.Model, BaseModel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = ComplexFieldContainer(self, OrganizationName)
        self.alias = ComplexFieldContainer(self, OrganizationAlias)
        self.classification = ComplexFieldContainer(
            self, OrganizationClassification)
        self.foundingdate = ComplexFieldContainer(self,
                                                  OrganizationFoundingDate)
        self.dissolutiondate = ComplexFieldContainer(
            self, OrganizationDissolutionDate)
        self.realfounding = ComplexFieldContainer(self,
                                                  OrganizationRealFounding)
        self.realdissolution = ComplexFieldContainer(
            self, OrganizationRealDissolution)

        self.complex_fields = [
            self.name, self.alias, self.classification, self.foundingdate,
            self.dissolutiondate, self.realfounding, self.realdissolution
        ]

        self.required_fields = [
            "Organization_OrganizationName",
        ]

    def get_value(self):
        return self.name.get_value()

    def __str__(self):
        return str(self.name)

    @classmethod
    def search(cls, terms):
        order_by = terms.get('orderby')
        if not order_by:
            order_by = 'organizationname__value'

        direction = terms.get('direction')
        if not direction:
            direction = 'ASC'

        dirsym = ''
        if direction == 'DESC':
            dirsym = '-'

        orgs_query = (Organization.objects.annotate(
            Max(order_by)).order_by(dirsym + order_by + "__max"))

        name = terms.get('name')
        if name:
            orgs_query = orgs_query.filter(
                organizationname__value__icontains=name)

        alias_val = terms.get('alias')
        if alias_val:
            orgs_query = orgs_query.filter(
                organizationalias__value__icontains=alias_val)

        foundingdate_year = terms.get('founding_year')
        if foundingdate_year:
            orgs_query = orgs_query.filter(
                organizationfoundingdate__value__startswith=foundingdate_year)

        foundingdate_month = terms.get('founding_month')
        if foundingdate_month:
            orgs_query = orgs_query.filter(
                organizationfoundingdate__value__contains="-" +
                foundingdate_month + "-")

        foundingdate_day = terms.get('founding_day')
        if foundingdate_day:
            orgs_query = orgs_query.filter(
                organizationfoundingdate__value__endswith=foundingdate_day)

        dissolutiondate_year = terms.get('dissolution_year')
        if dissolutiondate_year:
            orgs_query = orgs_query.filter(
                organizationdissolutiondate__value__startswith=
                dissolutiondate_year)

        dissolutiondate_month = terms.get('dissolution_month')
        if dissolutiondate_month:
            orgs_query = orgs_query.filter(
                organizationdissolutiondate__value__contains="-" +
                dissolutiondate_month + "-")

        dissolutiondate_day = terms.get('dissolution_day')
        if dissolutiondate_day:
            orgs_query = orgs_query.filter(
                organizationdissolutiondate__value__endswith=dissolutiondate_day
            )

        classification = terms.get('classification')
        if classification:
            orgs_query = orgs_query.filter(
                organizationclassification__value_id=classification)

        latitude = terms.get('latitude')
        longitude = terms.get('longitude')
        if latitude and longitude:
            try:
                latitude = float(latitude)
                longitude = float(longitude)
            except ValueError:
                latitude = 0
                longitude = 0

            point = geos.Point(latitude, longitude)
            radius = terms.get('radius')
            if radius:
                try:
                    radius = float(radius)
                except ValueError:
                    radius = 0
                orgs_query = orgs_query.filter(
                    associationorganization__object_ref__associationarea__value__areageometry__value__dwithin
                    =(point, radius))
            else:
                orgs_query = orgs_query.filter(
                    associationorganization__object_ref__associationarea__value__areageometry__value__bbcontains
                    =point)

        return orgs_query
Ejemplo n.º 19
0
class Organization(models.Model, BaseModel, SourcesMixin, VersionsMixin, GetComplexFieldNameMixin):

    uuid = models.UUIDField(default=uuid.uuid4,
                            editable=False,
                            db_index=True)

    published = models.BooleanField(default=False)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.name = ComplexFieldContainer(self, OrganizationName)
        self.aliases = ComplexFieldListContainer(self, OrganizationAlias)
        self.classification = ComplexFieldListContainer(self, OrganizationClassification)
        self.division_id = ComplexFieldContainer(self, OrganizationDivisionId)
        self.headquarters = ComplexFieldContainer(self, OrganizationHeadquarters)
        self.firstciteddate = ComplexFieldContainer(self, OrganizationFirstCitedDate)
        self.lastciteddate = ComplexFieldContainer(self, OrganizationLastCitedDate)
        self.realstart = ComplexFieldContainer(self, OrganizationRealStart)
        self.open_ended = ComplexFieldContainer(self, OrganizationOpenEnded)

        self.complex_fields = [self.name, self.division_id, self.firstciteddate,
                               self.lastciteddate, self.realstart, self.open_ended,
                               self.headquarters]

        self.complex_lists = [self.aliases, self.classification]

        self.required_fields = [
            "Organization_OrganizationName",
        ]

    def get_value(self):
        return self.name.get_value()

    def __str__(self):
        return str(self.name)

    @property
    def associations(self):
        '''
        Return all of this organization's associations, in a custom sorting order.

        Order by first cited date descending, then last cited date descending,
        with nulls last.
        '''
        from association.models import AssociationTenure

        return AssociationTenure.objects\
            .filter(association__associationorganization__value=self)\
            .select_related('association', 'startdate', 'enddate')\
            .order_by(
                F('startdate__value').desc(nulls_last=True),
                F('enddate__value').desc(nulls_last=True),
                'association__associationarea__value',
            )

    @property
    def emplacements(self):
        '''
        Return all of this organization's emplacements, in a custom sorting order.

        Order by first cited date descending, then last cited date descending,
        with nulls last.
        '''
        from emplacement.models import EmplacementTenure

        return EmplacementTenure.objects\
            .filter(emplacement__emplacementorganization__value=self)\
            .select_related('emplacement', 'startdate', 'enddate')\
            .order_by(
                F('startdate__value').desc(nulls_last=True),
                F('enddate__value').desc(nulls_last=True),
                'emplacement__emplacementsite__value',
            )

    @property
    def personnel(self):
        '''
        Returns all personnel ever assigned to a unit

        Objects returned are MembershipPerson objects
        '''
        return [o.object_ref for o in self.membershippersonorganization_set.all()]

    @property
    def alias_list(self):
        return ', '.join(a.get_value().value for a in self.aliases.get_list())

    @property
    def related_entities(self):
        """
        Return a list of dicts with metadata for all of the entities linked to
        this Organization.

        Metadata dicts must have the following keys:
            - name
            - entity_type
            - start_date
            - end_date
            - open_ended
            - url (a link to edit the entity)
        """
        related_entities = []

        # People that are posted to this organization.
        for membershipperson in self.membershippersonorganization_set.all():
            membership = membershipperson.object_ref
            person = membership.member.get_value().value
            related_entities.append({
                'name': person.name.get_value().value,
                'entity_type': _('MembershipPerson'),
                'start_date': membership.firstciteddate.get_value(),
                'end_date': membership.lastciteddate.get_value(),
                'open_ended': membership.realend.get_value(),
                'url': reverse(
                    'edit-organization-personnel',
                    kwargs={
                        'organization_id': self.uuid,
                        'pk': membership.pk
                    }
                ),
            })

        # Organizations that are children of this org.
        for compositionparent in self.child_organization.all():
            composition = compositionparent.object_ref
            child = composition.child.get_value().value
            related_entities.append({
                'name': child.name.get_value().value,
                'entity_type': _('Composition'),
                'start_date': composition.startdate.get_value(),
                'end_date': composition.enddate.get_value(),
                'open_ended': composition.open_ended.get_value(),
                'url': reverse(
                    'edit-organization-composition',
                    kwargs={
                        'organization_id': self.uuid,
                        'pk': composition.pk
                    }
                ),
            })

        # Organizations that are parents of this org.
        for compositionchild in self.parent_organization.all():
            composition = compositionchild.object_ref
            parent = composition.parent.get_value().value
            related_entities.append({
                'name': parent.name.get_value().value,
                'entity_type': _('Composition'),
                'start_date': composition.startdate.get_value(),
                'end_date': composition.enddate.get_value(),
                'open_ended': composition.open_ended.get_value(),
                'url': reverse(
                    'edit-organization-composition',
                    kwargs={
                        'organization_id': self.uuid,
                        'pk': composition.pk
                    }
                ),
            })

        # Organizations that this org is a member of.
        for membershiporganizationmember in self.membershiporganizationmember_set.all():
            membership = membershiporganizationmember.object_ref
            member_org = membership.organization.get_value().value
            related_entities.append({
                'name': member_org.name.get_value().value,
                'entity_type': _('MembershipOrganization'),
                'start_date': membership.firstciteddate.get_value(),
                'end_date': membership.lastciteddate.get_value(),
                'open_ended': membership.open_ended.get_value(),
                'url': reverse(
                    'edit-organization-membership',
                    kwargs={
                        'organization_id': self.uuid,
                        'pk': membership.pk
                    }
                ),
            })

        # Organizations that are members of this org.
        for membershiporganizationorganization in self.membershiporganizationorganization_set.all():
            membership = membershiporganizationorganization.object_ref
            member_org = membership.member.get_value().value
            related_entities.append({
                'name': member_org.name.get_value().value,
                'entity_type': _('MembershipOrganization'),
                'start_date': membership.firstciteddate.get_value(),
                'end_date': membership.lastciteddate.get_value(),
                'open_ended': membership.open_ended.get_value(),
                'url': reverse(
                    'edit-organization-membership',
                    kwargs={
                        'organization_id': self.uuid,
                        'pk': membership.pk
                    }
                ),
            })

        # Areas of operation.
        for associationorganization in self.associationorganization_set.all():
            association = associationorganization.object_ref
            location = association.area.get_value().value
            related_entities.append({
                'name': location.name,
                'entity_type': _('Association'),
                'start_date': association.startdate.get_value(),
                'end_date': association.enddate.get_value(),
                'open_ended': association.open_ended.get_value(),
                'url': reverse(
                    'edit-organization-association',
                    kwargs={
                        'organization_id': self.uuid,
                        'pk': association.pk
                    }
                ),
            })

        # Organization sites.
        for emplacementorganization in self.emplacementorganization_set.all():
            emplacement = emplacementorganization.object_ref
            location = emplacement.site.get_value().value
            related_entities.append({
                'name': location.name,
                'entity_type': _('Emplacement'),
                'start_date': emplacement.startdate.get_value(),
                'end_date': emplacement.enddate.get_value(),
                'open_ended': emplacement.open_ended.get_value(),
                'url': reverse(
                    'edit-organization-emplacement',
                    kwargs={
                        'organization_id': self.uuid,
                        'pk': emplacement.pk
                    }
                ),
            })

        # Violations where this org was a perpetrator.
        for violationperpetrator in self.violationperpetratororganization_set.all():
            violation = violationperpetrator.object_ref
            related_entities.append({
                'name': truncatewords(violation.description.get_value(), 10),
                'entity_type': _('Violation'),
                'start_date': violation.startdate.get_value(),
                'end_date': violation.enddate.get_value(),
                'open_ended': '',
                'url': reverse('edit-violation', kwargs={'slug': violation.uuid}),
            })

        return related_entities
Ejemplo n.º 20
0
class Person(models.Model, BaseModel, SourcesMixin, VersionsMixin, GetComplexFieldNameMixin):

    uuid = models.UUIDField(default=uuid.uuid4,
                            editable=False,
                            db_index=True)

    published = models.BooleanField(default=False)

    def __init__(self, *args, **kwargs):
        self.name = ComplexFieldContainer(self, PersonName)
        self.aliases = ComplexFieldListContainer(self, PersonAlias)
        self.division_id = ComplexFieldContainer(self, PersonDivisionId)
        self.notes = ComplexFieldContainer(self, PersonNotes)

        self.complex_fields = [
            self.name,
            self.division_id,
            self.notes,
        ]
        self.complex_lists = [self.aliases]

        self.required_fields = [
            "Person_PersonName",
        ]

        super().__init__(*args, **kwargs)

    def get_value(self):
        return self.name.get_value()

    def __str__(self):
        try:
            return str(self.personname_set.first().value)
        except AttributeError:
            return str(self.uuid)

    def get_absolute_url(self):
        return reverse('view-person', kwargs={'slug': self.uuid})

    @cached_property
    def memberships(self):
        '''
        Return all of this person's memberships, in a custom sorting order.

        Order by first cited date descending, then last cited date descending,
        with nulls last.
        '''
        mems = self.membershippersonmember_set\
                   .select_related('object_ref')\
                   .annotate(lcd=Coalesce('object_ref__membershippersonfirstciteddate__value',
                                          'object_ref__membershippersonlastciteddate__value',
                                          models.Value('1000-0-0')))\
                   .order_by('-lcd')

        return mems

    @property
    def last_cited(self):
        '''
        Get the global last citation date for this person, leaving out nulls.
        '''
        order = '-object_ref__membershippersonlastciteddate__value'
        memberships = self.membershippersonmember_set.order_by(order)
        for membership in memberships:
            # Filter nulls
            lcd = membership.object_ref.lastciteddate.get_value()
            if lcd:
                return lcd

    @property
    def first_cited(self):
        '''
        Get the global first citation date for this person, leaving out nulls.
        '''
        order = 'object_ref__membershippersonfirstciteddate__value'
        memberships = self.membershippersonmember_set.order_by(order)
        for membership in memberships:
            fcd = membership.object_ref.firstciteddate.get_value()
            if fcd:
                return fcd

    @property
    def related_entities(self):
        """
        Return a list of dicts with metadata for all of the entities linked to
        this Person.

        Metadata dicts must have the following keys:
            - name
            - entity_type
            - start_date
            - end_date
            - open_ended
            - url (a link to edit the entity)
        """
        related_entities = []

        # This person's postings.
        for membershippersonmember in self.membershippersonmember_set.all():
            membership = membershippersonmember.object_ref
            organization = membership.organization.get_value().value
            related_entities.append({
                'name': organization.name.get_value().value,
                'entity_type': _('MembershipPerson'),
                'start_date': membership.firstciteddate.get_value(),
                'end_date': membership.lastciteddate.get_value(),
                'open_ended': membership.realend.get_value(),
                'url': reverse(
                    'edit-person-postings',
                    kwargs={
                        'person_id': self.uuid,
                        'pk': membership.pk
                    }
                ),
            })

        # Incidents where this person was a perpetrator.
        for violationperpetrator in self.violationperpetrator_set.all():
            violation = violationperpetrator.object_ref
            related_entities.append({
                'name': truncatewords(violation.description.get_value(), 10),
                'entity_type': _('Violation'),
                'start_date': violation.startdate.get_value(),
                'end_date': violation.enddate.get_value(),
                'open_ended': '',
                'url': reverse('edit-violation', kwargs={'slug': violation.uuid}),
            })

        return related_entities