class GroupMembershipForm(forms.Form): selected_ids = forms.Field( label=ugettext_lazy("Group Membership"), required=False, widget=Select2Ajax(multiple=True), ) def __init__(self, group_api_url, *args, **kwargs): submit_label = kwargs.pop('submit_label', "Update") fieldset_title = kwargs.pop( 'fieldset_title', ugettext_lazy("Edit Group Membership")) super(GroupMembershipForm, self).__init__(*args, **kwargs) self.fields['selected_ids'].widget.set_url(group_api_url) self.helper = FormHelper() self.helper.label_class = 'col-sm-3 col-md-2' self.helper.field_class = 'col-sm-9 col-md-8 col-lg-6' self.helper.form_tag = False self.helper.layout = crispy.Layout( crispy.Fieldset( fieldset_title, crispy.Field('selected_ids'), ), hqcrispy.FormActions( crispy.ButtonHolder( Submit('submit', submit_label) ) ) )
class UsersAtLocationForm(forms.Form): selected_ids = forms.Field( label=ugettext_lazy("Workers at Location"), required=False, widget=Select2Ajax(multiple=True), ) def __init__(self, domain_object, location, *args, **kwargs): self.domain_object = domain_object self.location = location super(UsersAtLocationForm, self).__init__( initial={'selected_ids': self.get_users_at_location()}, prefix="users", *args, **kwargs ) from corehq.apps.reports.filters.api import MobileWorkersOptionsView self.fields['selected_ids'].widget.set_url( reverse(MobileWorkersOptionsView.urlname, args=(self.domain_object.name,)) ) self.fields['selected_ids'].widget.set_initial(self.get_users_at_location()) self.helper = FormHelper() self.helper.label_class = 'col-sm-3 col-md-2' self.helper.field_class = 'col-sm-9 col-md-8 col-lg-6' self.helper.form_tag = False self.helper.layout = crispy.Layout( crispy.Fieldset( _("Specify Workers at this Location"), crispy.Field('selected_ids'), ), hqcrispy.FormActions( crispy.ButtonHolder( Submit('submit', ugettext_lazy("Update Location Membership")) ) ) ) # Adding a 5 second timeout because that is the elasticsearch refresh interval. @memoized @quickcache(['self.domain_object.name', 'self.location.location_id'], memoize_timeout=0, timeout=5) def get_users_at_location(self): user_query = UserES().domain( self.domain_object.name ).mobile_users().location( self.location.location_id ).fields(['_id', 'username', 'first_name', 'last_name']) return [ dict(id=u['_id'], text=user_display_string( u['username'], u.get('first_name', ''), u.get('last_name', '') )) for u in user_query.run().hits] def unassign_users(self, users): for doc in iter_docs(CommCareUser.get_db(), users): # This could probably be sped up by bulk saving, but there's a lot # of stuff going on - seems tricky. CommCareUser.wrap(doc).unset_location_by_id(self.location.location_id, fall_back_to_next=True) def assign_users(self, users): for doc in iter_docs(CommCareUser.get_db(), users): CommCareUser.wrap(doc).add_to_assigned_locations(self.location) def clean_selected_ids(self): # Django uses get by default, but selected_ids is actually a list return self.data.getlist('users-selected_ids') def save(self): selected_users = set(self.cleaned_data['selected_ids']) previous_users = set([u['id'] for u in self.get_users_at_location()]) to_remove = previous_users - selected_users to_add = selected_users - previous_users self.unassign_users(to_remove) self.assign_users(to_add) self.cache_users_at_location(selected_users) def cache_users_at_location(self, selected_users): user_cache_list = [] for doc in iter_docs(CommCareUser.get_db(), selected_users): display_username = user_display_string( doc['username'], doc.get('first_name', ''), doc.get('last_name', '')) user_cache_list.append({'text': display_username, 'id': doc['_id']}) self.get_users_at_location.set_cached_value(self).to(user_cache_list)
class DashboardFeedFilterForm(forms.Form): """ A form used to configure the filters on a Dashboard Feed export """ emwf_case_filter = forms.Field( label=ugettext_lazy("Case Owner(s)"), required=False, widget=Select2Ajax(multiple=True), help_text=ExpandedMobileWorkerFilter.location_search_help, ) emwf_form_filter = forms.Field( label=ugettext_lazy("User(s)"), required=False, widget=Select2Ajax(multiple=True), help_text=ExpandedMobileWorkerFilter.location_search_help, ) date_range = forms.ChoiceField( label=ugettext_lazy("Date Range"), required=True, initial="last7", choices=[ ("last7", ugettext_lazy("Last 7 days")), ("last30", ugettext_lazy("Last 30 days")), ("lastmonth", ugettext_lazy("Last month")), ("lastyear", ugettext_lazy("Last year")), ("lastn", ugettext_lazy("Days ago")), ("since", ugettext_lazy("Since a date")), ("range", ugettext_lazy("From a date to a date")), ], help_text=''' <span data-bind='visible: showEmwfFormFilter'>{}</span> <span data-bind='visible: showEmwfCaseFilter'>{}</span> '''.format( ugettext_lazy("Export forms received in this date range."), ugettext_lazy("Export cases modified in this date range."), )) days = forms.IntegerField( label=ugettext_lazy("Number of Days"), required=False, ) start_date = forms.DateField( label=ugettext_lazy("Begin Date"), required=False, widget=forms.DateInput(format="%Y-%m-%d", attrs={"placeholder": "YYYY-MM-DD"}), help_text="<small class='label label-default'>{}</small>".format( ugettext_lazy("YYYY-MM-DD")), ) end_date = forms.DateField( label=ugettext_lazy("End Date"), required=False, widget=forms.DateInput(format="%Y-%m-%d", attrs={"placeholder": "YYYY-MM-DD"}), help_text="<small class='label label-default'>{}</small>".format( ugettext_lazy("YYYY-MM-DD")), ) def __init__(self, domain_object, *args, **kwargs): self.domain_object = domain_object super(DashboardFeedFilterForm, self).__init__(*args, **kwargs) self.fields['emwf_case_filter'].widget.set_url( reverse(CaseListFilter.options_url, args=(self.domain_object.name, ))) self.fields['emwf_form_filter'].widget.set_url( reverse(ExpandedMobileWorkerFilter.options_url, args=(self.domain_object.name, ))) self.helper = HQModalFormHelper() self.helper.form_tag = False self.helper.layout = Layout(*self.layout_fields) def clean(self): cleaned_data = super(DashboardFeedFilterForm, self).clean() errors = [] if cleaned_data['emwf_form_filter'] and cleaned_data[ 'emwf_case_filter']: # This should only happen if a user builds the reqest manually or there is an error rendering the form forms.ValidationError( _("Cannot submit case and form users and groups filter")) date_range = cleaned_data['date_range'] if date_range in ("since", "range") and not cleaned_data.get( 'start_date', None): errors.append( forms.ValidationError(_("A valid start date is required"))) if date_range == "range" and not cleaned_data.get('end_date', None): errors.append( forms.ValidationError(_("A valid end date is required"))) if errors: raise forms.ValidationError(errors) def format_export_data(self, export): return { 'domain': self.domain_object.name, 'sheet_name': export.name, 'export_id': export.get_id, 'export_type': self._export_type, 'name': export.name, 'edit_url': self.get_edit_url(export), } @property def layout_fields(self): return [ crispy.Div( crispy.Field('emwf_case_filter', ), data_bind="visible: showEmwfCaseFilter", ), crispy.Div( crispy.Field('emwf_form_filter', ), data_bind="visible: showEmwfFormFilter", ), crispy.Field( 'date_range', data_bind='value: dateRange', ), crispy.Div( crispy.Field("days", data_bind="value: days"), data_bind="visible: showDays", ), crispy.Div( crispy.Field( "start_date", data_bind="value: startDate", ), data_bind= "visible: showStartDate, css: {'has-error': startDateHasError}", ), crispy.Div( crispy.Field( "end_date", data_bind="value: endDate", ), data_bind= "visible: showEndDate, css: {'has-error': endDateHasError}", ) ] def to_export_instance_filters(self, can_access_all_locations, accessible_location_ids, export_type): """ Serialize the bound form as an ExportInstanceFilters object. """ # Confirm that either form filter data or case filter data but not both has been submitted. assert ((self.cleaned_data['emwf_form_filter'] is not None) != (self.cleaned_data['emwf_case_filter'] is not None)) assert (export_type == 'form' or export_type == 'case') if export_type == 'form': return self._to_form_export_instance_filters( can_access_all_locations, accessible_location_ids) else: return self._to_case_export_instance_filters( can_access_all_locations, accessible_location_ids) def _to_case_export_instance_filters(self, can_access_all_locations, accessible_location_ids): emwf_selections = self.cleaned_data["emwf_case_filter"] return CaseExportInstanceFilters( date_period=DatePeriod( period_type=self.cleaned_data['date_range'], days=self.cleaned_data['days'], begin=self.cleaned_data['start_date'], end=self.cleaned_data['end_date'], ), users=CaseListFilter.selected_user_ids(emwf_selections), reporting_groups=CaseListFilter.selected_reporting_group_ids( emwf_selections), locations=CaseListFilter.selected_location_ids(emwf_selections), user_types=CaseListFilter.selected_user_types(emwf_selections), can_access_all_locations=can_access_all_locations, accessible_location_ids=accessible_location_ids, sharing_groups=CaseListFilter.selected_sharing_group_ids( emwf_selections), show_all_data=CaseListFilter.show_all_data(emwf_selections) or CaseListFilter.no_filters_selected(emwf_selections), show_project_data=CaseListFilter.show_project_data( emwf_selections), ) def _to_form_export_instance_filters(self, can_access_all_locations, accessible_location_ids): emwf_selections = self.cleaned_data["emwf_form_filter"] return FormExportInstanceFilters( date_period=DatePeriod( period_type=self.cleaned_data['date_range'], days=self.cleaned_data['days'], begin=self.cleaned_data['start_date'], end=self.cleaned_data['end_date'], ), users=ExpandedMobileWorkerFilter.selected_user_ids( emwf_selections), reporting_groups=ExpandedMobileWorkerFilter. selected_reporting_group_ids(emwf_selections), locations=ExpandedMobileWorkerFilter.selected_location_ids( emwf_selections), user_types=ExpandedMobileWorkerFilter.selected_user_types( emwf_selections), can_access_all_locations=can_access_all_locations, accessible_location_ids=accessible_location_ids, ) @classmethod def get_form_data_from_export_instance_filters(cls, export_instance_filters, domain, export_type): """ Return a dictionary representing the form data from a given ExportInstanceFilters. This is used to populate a form from an existing export instance :param export_instance_filters: :return: """ if export_instance_filters: date_period = export_instance_filters.date_period selected_items = (export_instance_filters.users + export_instance_filters.reporting_groups + export_instance_filters.locations + export_instance_filters.user_types) if isinstance(export_instance_filters, CaseExportInstanceFilters): selected_items += ( export_instance_filters.sharing_groups + (["all_data"] if export_instance_filters.show_all_data else []) + (["project_data"] if export_instance_filters.show_project_data else [])) emwf_utils_class = CaseListFilterUtils if export_type is CaseExportInstance else \ EmwfUtils emwf_data = [] for item in selected_items: choice_tuple = emwf_utils_class(domain).id_to_choice_tuple( str(item)) if choice_tuple: emwf_data.append({ "id": choice_tuple[0], "text": choice_tuple[1] }) return { "date_range": date_period.period_type if date_period else None, "days": date_period.days if date_period else None, "start_date": date_period.begin if date_period else None, "end_date": date_period.end if date_period else None, "emwf_form_filter": emwf_data, "emwf_case_filter": emwf_data, } else: return None
class UsersAtLocationForm(forms.Form): selected_ids = forms.Field( label=ugettext_lazy("Group Membership"), required=False, widget=Select2Ajax(multiple=True), ) def __init__(self, domain_object, location, *args, **kwargs): self.domain_object = domain_object self.location = location fieldset_title = kwargs.pop('fieldset_title', ugettext_lazy("Edit Group Membership")) submit_label = kwargs.pop('submit_label', ugettext_lazy("Update Membership")) super(UsersAtLocationForm, self).__init__(initial={'selected_ids': self.users_at_location}, *args, **kwargs) from corehq.apps.reports.filters.api import MobileWorkersOptionsView self.fields['selected_ids'].widget.set_url( reverse(MobileWorkersOptionsView.urlname, args=(self.domain_object.name, ))) self.fields['selected_ids'].widget.set_initial(self.users_at_location) self.helper = FormHelper() self.helper.label_class = 'col-sm-3 col-md-2' self.helper.field_class = 'col-sm-9 col-md-8 col-lg-6' self.helper.form_tag = False self.helper.layout = crispy.Layout( crispy.Fieldset( fieldset_title, crispy.Field('selected_ids'), ), hqcrispy.FormActions( crispy.ButtonHolder(Submit('submit', submit_label)))) @property @memoized def users_at_location(self): user_query = UserES().domain( self.domain_object.name).mobile_users().location( self.location.location_id).fields( ['_id', 'username', 'first_name', 'last_name']) return [ dict(id=u['_id'], text=user_display_string(u['username'], u.get('first_name', ''), u.get('last_name', ''))) for u in user_query.run().hits ] def unassign_users(self, users): for doc in iter_docs(CommCareUser.get_db(), users): # This could probably be sped up by bulk saving, but there's a lot # of stuff going on - seems tricky. CommCareUser.wrap(doc).unset_location_by_id( self.location.location_id, fall_back_to_next=True) def assign_users(self, users): for doc in iter_docs(CommCareUser.get_db(), users): CommCareUser.wrap(doc).add_to_assigned_locations(self.location) def save(self): selected_users = set(self.cleaned_data['selected_ids'].split(',')) previous_users = set([u['id'] for u in self.users_at_location]) to_remove = previous_users - selected_users to_add = selected_users - previous_users self.unassign_users(to_remove) self.assign_users(to_add)