-
-
Notifications
You must be signed in to change notification settings - Fork 221
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Location select widget - filter by whether the location "has users" #34912
Changes from 7 commits
2e28575
21c66e5
2135843
258745c
e7bec3b
238bc7a
c63458e
86f906a
ba79133
9bf39ed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,6 +31,8 @@ | |
url(r'^$', default, name='default_locations_view'), | ||
url(r'^list/$', LocationsListView.as_view(), name=LocationsListView.urlname), | ||
url(r'^location_search/$', LocationsSearchView.as_view(), name='location_search'), | ||
url(r'^location_search_has_users_only/$', LocationsSearchView.as_view( | ||
include_locations_with_no_users_allowed=False), name='location_search_has_users_only'), | ||
Comment on lines
+35
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What would you think of naming this after the place it's used rather than what distinguishes it from the other endpoint? We'll also soon be excluding already-assigned locations from the results, so it may see further tweaking. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see from a later commit that the plan is to switch between endpoints depending on the desired functionality. Would it be possible to make this enpdoint specific to the location assignment widget and then move the feature flag check there? Otherwise further changes would need to account for both endpoints. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Why would this be the case? Are you imagining the new needed search criteria with the upcoming location select widget work would be passed as URL parameters? Or maybe your are thinking they could be passed directly to the view, ah. I was thinking they would be passed in the request body. If that's the approach we want to take I am open to that, but maybe changes like you're proposing belong as part of that scope of workk. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, I see that it's still the same view in both cases, but with a different initialization parameter here, I was thinking it was a subclass for some reason. I still think there may be a place for making a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm struggling a bit to articulate what I'm thinking, but let me try. Essentially, there's a generic "locations select widget" that is used in several different places. This is one customization specific to user location assignment, and we've got a couple more customizations likely coming. The way it's done here is to set a flag on the widget, which directs to a different URL, which sets a flag on the view, which sets a flag on the controller, which forks the behavior in the controller's parent class. That's a lot of steps, making it perhaps challenging to see when/how/why the flag is set to what it is. The alternative I'm imagining would be to switch from the generic widget to one that's specific to user location assignment. Then it's clear in what context the behavior differs. Perhaps that'd still use the same system of passing flags around, only the flag would be called "is user location assignment" or some such. Then when we fork behavior, we could do it all under the same flag or subclass, making the scope under which those changes apply clearer. Again I don't think that's necessarily better, certainly not with just this logic change. Mostly leaving my thoughts here now because I suspect it'll start to be more compelling as we further fork the logic (though it's always hard to tell for sure without trying it). From the article Simon shared this morning:
Here the widget for picking locations for a user is coupled to the generic widget for finding locations on a project, and I'm thinking that they are starting to evolve separately, so we may want to decouple them (though perhaps the enriched context actually should benefit all instances). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I gotcha. Yeah, what you're saying makes sense.
You mean extending the class to make one thats for assignment to users, right? I agree doing that instead of the "first" flag that's passed through would help clarify to the dev what it's used for/how they should use it. I could also see forking the search view as you're suggesting, into something like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, something like that, whichever seems cleaner. Like maybe it's just a flag in the widget and a subclass for the options view, not sure. But yeah, some way to tie it to the specific usage rather than the behavior change from the "normal" way. |
||
url(r'^location_types/$', LocationTypesView.as_view(), name=LocationTypesView.urlname), | ||
url(r'^import/$', waf_allow('XSS_BODY')(LocationImportView.as_view()), name=LocationImportView.urlname), | ||
url(r'^import_status/(?P<download_id>(?:dl-)?[0-9a-fA-Z]{25,32})/$', LocationImportStatusView.as_view(), | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -58,6 +58,7 @@ | |
from corehq.const import LOADTEST_HARD_LIMIT, USER_CHANGE_VIA_WEB | ||
from corehq.pillows.utils import MOBILE_USER_TYPE, WEB_USER_TYPE | ||
from corehq.toggles import ( | ||
LOCATION_HAS_USERS, | ||
TWO_STAGE_USER_PROVISIONING, | ||
TWO_STAGE_USER_PROVISIONING_BY_SMS, | ||
) | ||
|
@@ -1141,7 +1142,7 @@ def render(self, name, value, attrs=None, renderer=None): | |
}) | ||
|
||
|
||
class BaseLocationForm(forms.Form): | ||
class SelectUserLocationForm(forms.Form): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
assigned_locations = forms.CharField( | ||
label=gettext_noop("Locations"), | ||
required=False, | ||
|
@@ -1156,10 +1157,11 @@ class BaseLocationForm(forms.Form): | |
def __init__(self, domain: str, *args, **kwargs): | ||
from corehq.apps.locations.forms import LocationSelectWidget | ||
self.request = kwargs.pop('request') | ||
super(BaseLocationForm, self).__init__(*args, **kwargs) | ||
super(SelectUserLocationForm, self).__init__(*args, **kwargs) | ||
self.domain = domain | ||
self.fields['assigned_locations'].widget = LocationSelectWidget( | ||
self.domain, multiselect=True, id='id_assigned_locations' | ||
self.domain, multiselect=True, id='id_assigned_locations', | ||
include_locations_with_no_users_allowed=False | ||
) | ||
self.fields['assigned_locations'].help_text = ExpandedMobileWorkerFilter.location_search_help | ||
self.fields['primary_location'].widget = PrimaryLocationWidget( | ||
|
@@ -1176,7 +1178,9 @@ def clean_assigned_locations(self): | |
locations = get_locations_from_ids(location_ids, self.domain) | ||
except SQLLocation.DoesNotExist: | ||
raise forms.ValidationError(_('One or more of the locations was not found.')) | ||
|
||
if LOCATION_HAS_USERS.enabled(self.domain) and locations.filter(location_type__has_users=False).exists(): | ||
raise forms.ValidationError( | ||
_('One or more of the locations you specified cannot have users assigned.')) | ||
return [location.location_id for location in locations] | ||
|
||
def _user_has_permission_to_access_locations(self, new_location_ids): | ||
|
@@ -1185,7 +1189,7 @@ def _user_has_permission_to_access_locations(self, new_location_ids): | |
self.domain, self.request.couch_user)) | ||
|
||
def clean(self): | ||
self.cleaned_data = super(BaseLocationForm, self).clean() | ||
self.cleaned_data = super(SelectUserLocationForm, self).clean() | ||
|
||
primary_location_id = self.cleaned_data['primary_location'] | ||
assigned_location_ids = self.cleaned_data.get('assigned_locations', []) | ||
|
@@ -1209,7 +1213,7 @@ def clean(self): | |
return self.cleaned_data | ||
|
||
|
||
class CommtrackUserForm(BaseLocationForm): | ||
class CommtrackUserForm(SelectUserLocationForm): | ||
program_id = forms.ChoiceField( | ||
label=gettext_noop("Program"), | ||
choices=(), | ||
|
@@ -1631,6 +1635,7 @@ def __init__(self, *args, **kwargs): | |
id='id_location_id', | ||
placeholder=_("All Locations"), | ||
attrs={'data-bind': 'value: location_id'}, | ||
include_locations_with_no_users_allowed=False | ||
) | ||
self.fields['location_id'].widget.query_url = "{url}?show_all=true".format( | ||
url=self.fields['location_id'].widget.query_url | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW there's also a
@privilege_enabled
decorator you can use for this to target a specific privilege