Example #1
0
class User(db.Document, UserMixin):
    email = db.EmailField()
    password = db.StringField(max_length=255)
    active = db.BooleanField(default=True)
    confirmed_at = db.DateTimeField()
    current_login_at = db.DateTimeField()
    last_login_at = db.DateTimeField()
    current_login_ip = db.StringField(max_length=45)
    last_login_ip = db.StringField(max_length=45)
    login_count = db.IntField(default=0)
    roles = db.ListField(db.ReferenceField(Role,
                         reverse_delete_rule=db.PULL), default=[])

    deployment = db.ReferenceField(Deployment)

    meta = {
        'indexes': [
            ['deployment'],
            ['deployment', 'email'],
            ['deployment', 'email', 'password']
        ]
    }

    def __unicode__(self):
        return self.email or u''
Example #2
0
class FormField(db.EmbeddedDocument):
    '''A :class:`mongoengine.EmbeddedDocument` used in storing the
    Checklist/Critical Incident form questions in a
    :class:`core.documents.Form` model.

    Each :class:`core.documents.FormField` has attributes for specifying
    various behaviour for the form field.

    :attr:`analysis_type` which specifies the sort of data analysis to be
    performed on the field and is defined by the values stored
    in :attr:`ANALYSIS_TYPES`

    :attr:`represents_boolean` which is either True for a FormField that
    accepts only one value (e.g. Critical Incident form fields)

    :attr:`options` which is a dictionary that has keys representing
    field option values and values representing the option description.
    (e.g. {'1': 'Yes'})

    :attr:`allows_multiple_values` which is a boolean field specifying whether
    the field will accept multiple values as correct responses

    :attr:`min_value` which specifies the minimum accepted value and
    :attr:`max_value` for specifying the maximum valid value.

    :attr:`description` stores the textual description of the field.

    :attr:`name` is the question code used to identify the field (e.g. AA)'''

    ANALYSIS_TYPES = (('N/A', _('Not Applicable')), ('PROCESS',
                                                     _('Process Analysis')),
                      ('RESULT', _('Results Analysis')))

    name = FormFieldNameField(required=True)
    description = db.StringField(required=True)
    max_value = db.IntField(default=9999)
    min_value = db.IntField(default=0)
    allows_multiple_values = db.BooleanField(default=False)
    is_comment_field = db.BooleanField(default=False)
    options = db.DictField()
    represents_boolean = db.BooleanField(default=False)
    analysis_type = db.StringField(choices=ANALYSIS_TYPES, default='N/A')
Example #3
0
class LocationType(db.Document):
    '''Stores the type describing the administrative level of a Location
    :attr ancestors_ref: This stores a list references to ancestor
    loction types as documented in
    http://docs.mongodb.org/manual/tutorial/model-tree-structures/'''

    name = db.StringField()
    ancestors_ref = db.ListField(db.ReferenceField(
        'LocationType', reverse_delete_rule=db.PULL))
    ancestor_count = db.IntField()
    is_administrative = db.BooleanField(default=False)
    is_political = db.BooleanField(default=False)
    has_registered_voters = db.BooleanField(db_field='has_rv', default=False)
    has_political_code = db.BooleanField(db_field='has_pc', default=False)
    has_other_code = db.BooleanField(db_field='has_oc', default=False)
    metafields = db.ListField(db.StringField())
    slug = db.StringField()

    deployment = db.ReferenceField(Deployment)

    meta = {
        'indexes': [
            ['deployment']
        ]
    }

    @classmethod
    def get_root_for_event(cls, event):
        return cls.objects.get(events=event, __raw__={'ancestors_ref': []})

    @classmethod
    def root(cls):
        return cls.objects.get(__raw__={'ancestors_ref': []})

    def clean(self):
        if not self.slug:
            self.slug = slugify_unicode(self.name).lower()
        self.ancestor_count = len(self.ancestors_ref)
        return super(LocationType, self).clean()

    @property
    def children(self):
        """Returns a list of descendants sorted by the length of the
        `attr`ancestors_ref.
        """
        temp = LocationType.objects(ancestors_ref=self)
        return sorted(temp, None, lambda x: len(x.ancestors_ref))

    def __unicode__(self):
        return self.name or u''
Example #4
0
class Location(db.DynamicDocument):

    '''A store for Locations'''

    name = db.StringField()
    code = db.StringField()
    political_code = db.StringField(db_field='pcode')
    other_code = db.StringField(db_field='ocode')
    location_type = db.StringField()
    coords = db.GeoPointField()
    registered_voters = db.LongField(db_field='rv', default=0)
    ancestor_count = db.IntField(default=0)
    ancestors_ref = db.ListField(db.ReferenceField(
        'Location', reverse_delete_rule=db.PULL))
    samples = db.ListField(db.ReferenceField(
        'Sample', reverse_delete_rule=db.PULL))
    events = db.ListField(db.ReferenceField(
        Event, reverse_delete_rule=db.PULL))
    deployment = db.ReferenceField(Deployment)

    meta = {
        'indexes': [
            ['ancestors_ref'],
            ['ancestors_ref', 'location_type'],
            ['samples'],
            ['events'],
            ['location_type'],
            ['name'],
            ['name', 'location_type'],
            ['code'],
            ['political_code'],
            ['events', 'code'],
            ['deployment'],
            ['deployment', 'events']
        ],
        'queryset_class': LocationQuerySet
    }

    @classmethod
    def get_root_for_event(cls, event):
        return cls.objects.get(events=event, __raw__={'ancestors_ref': []})

    @classmethod
    def root(cls):
        return cls.objects.get(__raw__={'ancestors_ref': []})

    @property
    def children(self):
        return Location.objects(
            ancestors_ref__all=self.ancestors_ref + [self],
            ancestors_ref__size=len(self.ancestors_ref) + 1)

    @property
    def descendants(self):
        return Location.objects(ancestors_ref=self)

    @property
    def ancestors(self):
        return self.ancestors_ref

    def ancestor(self, location_type, include_self=True):
        try:
            return filter(lambda l: l.location_type == location_type,
                          self.ancestors_ref + [self] if include_self else
                          self.ancestors_ref)[0]
        except IndexError:
            pass

    def __unicode__(self):
        return self.name or u''

    def save(self, *args, **kwargs):
        from . import LocationsService
        cache.delete_memoized(LocationsService.registered_voters_map)
        return super(Location, self).save(*args, **kwargs)

    def _update_ancestor_count(self, save=False):
        if self.ancestor_count != len(self.ancestors_ref):
            self.ancestor_count = len(self.ancestors_ref)

        if save:
            self.update(set__ancestor_count=self.ancestor_count)

    def clean(self):
        self._update_ancestor_count()
Example #5
0
class Participant(db.DynamicDocument):
    '''Storage for participant contact information'''

    GENDER = (('F', _('Female')), ('M', _('Male')), ('', _('Unspecified')))

    participant_id = db.StringField()
    name = db.StringField()
    role = db.ReferenceField('ParticipantRole')
    partner = db.ReferenceField('ParticipantPartner')
    location = db.ReferenceField('Location')
    location_name_path = db.DictField()
    supervisor = db.ReferenceField('Participant')
    gender = db.StringField(choices=GENDER, default='')
    groups = db.ListField(
        db.ReferenceField(ParticipantGroup, reverse_delete_rule=db.PULL))

    email = db.EmailField()
    phones = db.ListField(db.EmbeddedDocumentField(PhoneContact))
    message_count = db.IntField(default=0)
    accurate_message_count = db.IntField(default=0)

    event = db.ReferenceField(Event)
    deployment = db.ReferenceField(Deployment)

    completion_rating = db.FloatField(default=1)
    device_id = db.StringField()
    password = db.StringField()

    meta = {
        'indexes': [['participant_id'], ['device_id'], ['location'],
                    ['phones.number'], ['event'], ['name'], ['role'],
                    ['partner'], ['groups'], ['deployment'],
                    ['deployment', 'event']],
        'queryset_class':
        ParticipantQuerySet
    }

    def __unicode__(self):
        return self.name or u''

    def clean(self):
        # unlike for submissions, this always gets called, because
        # participants are 'mobile' - they can be moved from one location
        # to another. we want this to reflect that.
        self.location_name_path = compute_location_path(self.location)
        if self.gender not in map(lambda op: op[0], self.GENDER):
            self.gender = ''

    def get_phone(self):
        if self.phones:
            return self.phones[0].number
        else:
            return None

    def set_phone(self, value):
        # TODO: blind overwrite is silly. find a way to ensure the number
        # doesn't already exist
        if not self.phones:
            self.phones.append(PhoneContact(number=value, verified=True))
        else:
            self.phones[0].number = value
            self.phones[0].verified = True
        self.save()
        self.reload()

    phone = property(get_phone, set_phone)

    @property
    def last_seen_phone(self):
        if self.phones:
            phones = sorted(self.phones,
                            key=lambda p: p.last_seen
                            if p.last_seen else datetime.fromtimestamp(0))
            phones.reverse()
            return phones[0].number
        else:
            return None