Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apollo/frontend/templates/frontend/dashboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
{% block content %}
<div class="row">
<div class="col-md-12">
{{ render_filter_form(filter_form, location)}}
{{ render_filter_form(form, filter_form, location)}}
</div>
</div>
{%- if not daily_stratified_progress %}
Expand Down
31 changes: 30 additions & 1 deletion apollo/frontend/templates/frontend/macros/dashboard_filter.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,35 @@
{% macro render_filter_form(filter_form, location) %}
{% macro render_filter_form(dashboard_form, filter_form, location) %}
<div class="card border-light bg-light mb-3">
<div class="card-header">
<form class="mb-n2 ml-n2 mr-n2">
{% if dashboard_form.untrack_data_conflicts or dashboard_form.form_type != 'CHECKLIST' %}
<div class="form-row">
<div class="col-md-4 mb-2">
<label for="{{ filter_form.sample.id }}" class="sr-only">{{ filter_form.sample.label.text }}</label>
{{ filter_form.sample(class_='form-control custom-select') }}
</div>
<div class="col-md-4 mb-2">
<label for="{{ filter_form.location_group.id }}" class="sr-only">{{ filter_form.location_group.label.text }}</label>
{{ filter_form.location_group(class_='form-control custom-select') }}
</div>
<div class="col-md-4 mb-2">
<label for="{{ filter_form.participant_group.id }}" class="sr-only">{{ filter_form.participant_group.label.text }}</label>
{{ filter_form.participant_group(class_='form-control custom-select') }}
</div>
</div>
<div class="form-row">
<div class="col-md-10 mb-2">
<label for="{{ filter_form.location.id }}" class="sr-only">{{ filter_form.location.label.text }}</label>
{{ filter_form.location(class_='form-control select2 select2-locations') }}
</div>
<div class="col-md-2 mb-2">
<div class="d-flex flex-row">
<button class="btn btn-primary mr-2 flex-fill" type="submit">{{ _('Filter') }}</button>
<a class="btn btn-secondary flex-fill" href="#" id="filter_reset">{{ _('Clear') }}</a>
</div>
</div>
</div>
{% else %}
<div class="form-row">
<div class="col-md-2 mb-2">
<label for="{{ filter_form.sample.id }}" class="sr-only">{{ filter_form.sample.label.text }}</label>
Expand All @@ -22,6 +50,7 @@
</div>
</div>
</div>
{% endif %}
</form>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@
<label for="{{ form.partner.id }}" class="sr-only">{{ form.partner.label.text }}</label>
{{ form.partner(class_='form-control custom-select') }}
</div>
<div class="col-md-2 mb-2">
<label for="{{ form.group.id }}" class="sr-only">{{ form.group.label.text }}</label>
{{ form.group(class_='form-control custom-select') }}
</div>
<div class="col-md-2 mb-2">
<div class="d-flex flex-row">
<button class="btn btn-primary mr-2 flex-fill" type="submit">{{ _('Filter') }}</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@
<label for="{{ filter_form.participant_role.id }}" class="sr-only">{{ filter_form.participant_role.label.text }}</label>
{{ filter_form.participant_role(class_='form-control custom-select') }}
</div>
<div class="col-6 col-lg-2 mb-2">
<label for="{{ filter_form.participant_group.id }}" class="sr-only">{{ filter_form.participant_group.label.text }}</label>
{{ filter_form.participant_group(class_='form-control custom-select') }}
</div>
{%- if form.show_moment %}
<div class="col-md-6 col-lg-2 mb-2">
<div class="input-group date" id="datepicker" data-target-input="nearest">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@
<label for="{{ filter_form.participant_role.id }}" class="sr-only">{{ filter_form.participant_role.label.text }}</label>
{{ filter_form.participant_role(class_='form-control custom-select') }}
</div>
<div class="col-6 col-lg-2 mb-2">
<label for="{{ filter_form.participant_group.id }}" class="sr-only">{{ filter_form.participant_group.label.text }}</label>
{{ filter_form.participant_group(class_='form-control custom-select') }}
</div>
{%- if form.show_moment %}
<div class="col-6 col-lg-2 mb-2">
<div class="input-group date" id="datepicker" data-target-input="nearest">
Expand Down
4 changes: 0 additions & 4 deletions apollo/frontend/templates/frontend/participant_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -356,10 +356,6 @@ <h3>{{ _('Finalize') }}</h3>

LocationOptions.placeholder = { id: '-1', text: '{{ _("Location") }}'};
$('select.select2-locations').select2(LocationOptions);
$('#group.select2').select2({
theme: 'bootstrap4',
placeholder: "{{ _('All Groups') }}"
});
});
</script>

Expand Down
1 change: 1 addition & 0 deletions apollo/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import pkgutil

CSV_MIMETYPES = [
"text/plain",
"text/csv",
"application/csv",
"text/x-csv",
Expand Down
7 changes: 4 additions & 3 deletions apollo/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@
LocationTypePath, LocationGroup, locations_groups)
from apollo.messaging.models import Message # noqa
from apollo.participants.models import ( # noqa
ParticipantSet, ParticipantDataField,
Participant, ParticipantPartner, ParticipantRole, PhoneContact,
ContactHistory, Sample, samples_participants)
ParticipantGroup, ParticipantGroupType, ParticipantSet,
ParticipantDataField, Participant, ParticipantPartner,
ParticipantRole, PhoneContact, ContactHistory, Sample,
groups_participants, samples_participants)
from apollo.submissions.models import ( # noqa
Submission, SubmissionComment, SubmissionImageAttachment,
SubmissionVersion)
Expand Down
47 changes: 45 additions & 2 deletions apollo/participants/filters.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
from cgi import escape
from collections import OrderedDict

from flask_babelex import lazy_gettext as _
from sqlalchemy import func, or_, text, true
Expand All @@ -12,9 +13,11 @@
from apollo.core import CharFilter, ChoiceFilter, FilterSet
from apollo.helpers import _make_choices
from apollo.locations.models import Location, LocationPath
from apollo.wtforms_ext import ExtendedSelectField

from .models import Participant, ParticipantRole, ParticipantPartner
from .models import PhoneContact, Sample
from .models import Participant, ParticipantGroup, ParticipantGroupType
from .models import ParticipantPartner, ParticipantRole
from .models import PhoneContact, Sample, groups_participants


class ParticipantIDFilter(CharFilter):
Expand Down Expand Up @@ -94,6 +97,45 @@ def queryset_(self, query, value):
return ParticipantRoleFilter


def make_participant_group_filter(participant_set_id):
class ParticipantGroupFilter(ChoiceFilter):
field_class = ExtendedSelectField

def __init__(self, *args, **kwargs):
choices = OrderedDict()
choices[''] = _('Group')
for group_type in services.participant_group_types.find(
participant_set_id=participant_set_id
).order_by(
ParticipantGroupType.name):
for group in services.participant_groups.find(
group_type=group_type
).order_by(ParticipantGroup.name):
choices.setdefault(group_type.name, []).append(
(group.id, group.name)
)

kwargs['choices'] = [(k, choices[k]) for k in choices]
kwargs['coerce'] = int
super(ParticipantGroupFilter, self).__init__(*args, **kwargs)

def queryset_(self, query, value):
if value:
query2 = query.join(groups_participants).join(
ParticipantGroup)
return query2.filter(
Participant.id ==
models.groups_participants.c.participant_id, # noqa
ParticipantGroup.id ==
groups_participants.c.group_id,
ParticipantGroup.id == value,
)

return query

return ParticipantGroupFilter


def make_participant_partner_filter(participant_set_id):
class ParticipantPartnerFilter(ChoiceFilter):
def __init__(self, *args, **kwargs):
Expand Down Expand Up @@ -201,6 +243,7 @@ def participant_filterset(participant_set_id, location_set_id=None):
'name': ParticipantNameFilter(),
'phone': ParticipantPhoneFilter(),
'role': make_participant_role_filter(participant_set_id)(),
'group': make_participant_group_filter(participant_set_id)(),
'partner': make_participant_partner_filter(participant_set_id)()
}

Expand Down
73 changes: 71 additions & 2 deletions apollo/participants/models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
from itertools import chain
import re

from sqlalchemy import func
Expand Down Expand Up @@ -51,6 +50,7 @@ def get_import_fields(self):
'phone': _('Phone'),
'partner': _('Partner'),
'location': _('Location code'),
'group': _('Group'),
'gender': _('Gender'),
'email': _('Email'),
'password': _('Password')
Expand Down Expand Up @@ -94,6 +94,20 @@ def get_import_fields(self):
),
)

groups_participants = db.Table(
'participant_group_participants',
db.Column(
'group_id', db.Integer,
db.ForeignKey('participant_group.id', ondelete='CASCADE'),
nullable=False
),
db.Column(
'participant_id', db.Integer,
db.ForeignKey('participant.id', ondelete='CASCADE'),
nullable=False
),
)


class Sample(BaseModel):
__tablename__ = "sample"
Expand Down Expand Up @@ -152,6 +166,56 @@ def __str__(self):
return self.name or ''


class ParticipantGroupType(BaseModel):
__tablename__ = 'participant_group_type'

id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String)
participant_set_id = db.Column(
db.Integer,
db.ForeignKey('participant_set.id', ondelete='CASCADE'),
nullable=False
)

participant_set = db.relationship(
'ParticipantSet', backref=db.backref(
'participant_group_types', cascade='all, delete',
)
)

def __str__(self):
return self.name or ''


class ParticipantGroup(BaseModel):
__tablename__ = 'participant_group'

id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
group_type_id = db.Column(
db.Integer,
db.ForeignKey('participant_group_type.id', ondelete='CASCADE'),
nullable=False
)
participant_set_id = db.Column(
db.Integer,
db.ForeignKey('participant_set.id', ondelete='CASCADE'),
nullable=False
)

group_type = db.relationship(
'ParticipantGroupType',
backref=db.backref('participant_groups', cascade='all, delete'),
)
participant_set = db.relationship(
'ParticipantSet',
backref=db.backref('participant_groups', cascade='all, delete'),
)

def __str__(self):
return self.name or ''


class ParticipantPartner(BaseModel):
__tablename__ = 'participant_partner'

Expand Down Expand Up @@ -227,6 +291,10 @@ class Participant(BaseModel):
backref="participants",
secondary=samples_participants,
)
groups = db.relationship(
'ParticipantGroup', secondary=groups_participants,
backref='participants',
)

def __str__(self):
return self.name or ''
Expand Down Expand Up @@ -334,7 +402,8 @@ class PhoneContact(BaseModel):
onupdate=utils.current_timestamp)
verified = db.Column(db.Boolean, default=False)

participant = db.relationship('Participant', back_populates='phone_contacts')
participant = db.relationship(
'Participant', back_populates='phone_contacts')

def touch(self):
self.updated = utils.current_timestamp()
Expand Down
Loading