-
Notifications
You must be signed in to change notification settings - Fork 6
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
[#8661] Add 'Save Profile' button #5940
Changes from all commits
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 |
---|---|---|
@@ -0,0 +1,3 @@ | ||
### Added | ||
|
||
- Added `SaveSearchProfile` component for saving search profiles |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,10 +18,13 @@ | |
from adhocracy4.filters import widgets as filter_widgets | ||
from adhocracy4.filters.filters import DefaultsFilterSet | ||
from adhocracy4.filters.filters import FreeTextFilter | ||
from adhocracy4.projects.models import Topic | ||
from adhocracy4.rules import mixins as rules_mixins | ||
from meinberlin.apps.contrib.enums import TopicEnum | ||
from meinberlin.apps.contrib.views import CanonicalURLDetailView | ||
from meinberlin.apps.dashboard.mixins import DashboardProjectListGroupMixin | ||
from meinberlin.apps.kiezradar.models import ProjectStatus | ||
from meinberlin.apps.kiezradar.models import ProjectType | ||
from meinberlin.apps.kiezradar.models import SearchProfile | ||
from meinberlin.apps.kiezradar.serializers import SearchProfileSerializer | ||
from meinberlin.apps.maps.models import MapPreset | ||
|
@@ -77,28 +80,43 @@ def districts(self): | |
return [] | ||
|
||
def get_organisations(self): | ||
organisations = Organisation.objects.values_list("name", flat=True).order_by( | ||
"name" | ||
) | ||
organisations = Organisation.objects.values("id", "name").order_by("name") | ||
return json.dumps(list(organisations)) | ||
|
||
def get_district_polygons(self): | ||
districts = self.districts | ||
return json.dumps([district.polygon for district in districts]) | ||
|
||
def get_district_names(self): | ||
city_wide = _("City wide") | ||
districts = AdministrativeDistrict.objects.all() | ||
district_names_list = [district.name for district in districts] | ||
district_names_list.append(str(city_wide)) | ||
return json.dumps(district_names_list) | ||
def get_districts(self): | ||
districts = AdministrativeDistrict.objects.values("id", "name") | ||
districts_list = [district for district in districts] | ||
districts_list.append({"id": -1, "name": "City Wide"}) | ||
return json.dumps(districts_list) | ||
|
||
def get_topics(self): | ||
return json.dumps({topic: str(topic.label) for topic in TopicEnum}) | ||
topics = [ | ||
{ | ||
"id": topic.id, | ||
"code": topic.code, | ||
"name": str(TopicEnum(topic.code).label), | ||
} | ||
for topic in Topic.objects.all() | ||
] | ||
return json.dumps(topics) | ||
|
||
def get_participation_choices(self): | ||
choices = [str(choice[1]) for choice in Plan.participation.field.choices] | ||
return json.dumps(choices) | ||
project_types = [ | ||
{"id": project_type.id, "name": project_type.get_participation_display()} | ||
for project_type in ProjectType.objects.all() | ||
] | ||
return json.dumps(project_types) | ||
|
||
def get_project_status(self): | ||
statuses = [ | ||
{ | ||
"id": project_status.id, | ||
"status": project_status.status, | ||
"name": project_status.get_status_display(), | ||
} | ||
for project_status in ProjectStatus.objects.all() | ||
] | ||
return json.dumps(statuses) | ||
|
||
def get_search_profile(self): | ||
if ( | ||
|
@@ -119,6 +137,12 @@ def get_search_profile(self): | |
pass | ||
return None | ||
|
||
def get_search_profiles_count(self): | ||
if self.request.user.is_authenticated: | ||
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. AFAIK no need for this check, as the view is allowed only to registered users. Same in the 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. Yup, verified, we don't need these authentication checks, they are inherited from adhocracy4 as a mixin in this view class declaration above. 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. Great, thanks. But this view is for the plans map ( 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. agreed, as this page can be viewed by anyone I think we need the check |
||
return SearchProfile.objects.filter(user=self.request.user).count() | ||
else: | ||
return 0 | ||
|
||
def get_context_data(self, **kwargs): | ||
context = super().get_context_data(**kwargs) | ||
|
||
|
@@ -136,9 +160,8 @@ def get_context_data(self, **kwargs): | |
omt_token = settings.A4_OPENMAPTILES_TOKEN | ||
|
||
context["search_profile"] = self.get_search_profile() | ||
context["districts"] = self.get_district_polygons() | ||
context["districts"] = self.get_districts() | ||
context["organisations"] = self.get_organisations() | ||
context["district_names"] = self.get_district_names() | ||
context["topic_choices"] = self.get_topics() | ||
context["extprojects_api_url"] = reverse("extprojects-list") | ||
context["privateprojects_api_url"] = reverse("privateprojects-list") | ||
|
@@ -156,6 +179,10 @@ def get_context_data(self, **kwargs): | |
context["district"] = self.request.GET.get("district", -1) | ||
context["topic"] = self.request.GET.get("topic", -1) | ||
context["participation_choices"] = self.get_participation_choices() | ||
context["search_profiles_url"] = reverse("searchprofiles-list") | ||
context["search_profiles_count"] = self.get_search_profiles_count() | ||
context["is_authenticated"] = json.dumps(self.request.user.is_authenticated) | ||
context["project_status"] = self.get_project_status() | ||
|
||
return context | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
import { renderHook, act } from '@testing-library/react' | ||
import { updateItem } from '../contrib/helpers' | ||
import { useCreateSearchProfile } from './use-create-search-profile' | ||
|
||
jest.mock('../contrib/helpers', () => ({ | ||
updateItem: jest.fn() | ||
})) | ||
|
||
describe('useCreateSearchProfile', () => { | ||
const searchProfilesApiUrl = '/api/search-profiles' | ||
const districts = [ | ||
{ id: 1, name: 'Charlottenburg-Wilmersdorf' }, | ||
{ id: 2, name: 'Friedrichshain-Kreuzberg' } | ||
] | ||
const organisations = [{ id: 1, name: 'liqd' }] | ||
const topicChoices = [ | ||
{ id: 1, name: 'Anti-discrimination, Work & economy', code: 'ANT' } | ||
] | ||
const participationChoices = [ | ||
{ id: 1, name: 'information (no participation)' }, | ||
{ id: 2, name: 'consultation' }, | ||
{ id: 3, name: 'cooperation' }, | ||
{ id: 4, name: 'decision-making' } | ||
] | ||
const projectStatus = [ | ||
{ | ||
id: 1, | ||
status: 0, | ||
name: 'running' | ||
}, | ||
{ | ||
id: 2, | ||
status: 1, | ||
name: 'done' | ||
}, | ||
{ | ||
id: 3, | ||
status: 2, | ||
name: 'future' | ||
} | ||
] | ||
|
||
beforeEach(() => { | ||
jest.clearAllMocks() | ||
}) | ||
|
||
it('handles submission of a search profile', async () => { | ||
const appliedFilters = { | ||
districts: ['Charlottenburg-Wilmersdorf', 'Friedrichshain-Kreuzberg'], | ||
organisation: ['liqd'], | ||
topics: ['ANT'], | ||
participations: [0, 1, 2], | ||
projectState: ['active', 'past', 'future'], | ||
search: '' | ||
} | ||
|
||
const mockedData = { | ||
districts: [1, 2], | ||
organisations: [1], | ||
topics: [1], | ||
project_types: [1, 2, 3], | ||
status: [1, 2, 3] | ||
} | ||
|
||
const { result } = renderHook(() => | ||
useCreateSearchProfile({ | ||
searchProfilesApiUrl, | ||
appliedFilters, | ||
districts, | ||
organisations, | ||
topicChoices, | ||
participationChoices, | ||
projectStatus, | ||
onSearchProfileCreate: () => {} | ||
}) | ||
) | ||
|
||
await act(async () => { | ||
await result.current.createSearchProfile() | ||
}) | ||
|
||
expect(updateItem).toHaveBeenCalledWith( | ||
expect.objectContaining(mockedData), | ||
searchProfilesApiUrl, | ||
'POST' | ||
) | ||
}) | ||
|
||
it('calls onSearchProfileCreate with searchProfile from updateItem', async () => { | ||
const appliedFilters = { | ||
districts: [], | ||
organisation: [], | ||
topics: [], | ||
participations: [], | ||
projectState: [], | ||
search: '' | ||
} | ||
|
||
const mockedSearchProfile = { | ||
id: 1, | ||
user: 1, | ||
name: 'Searchprofile 1', | ||
description: null, | ||
disabled: false, | ||
notification: false, | ||
status: [], | ||
query: 15, | ||
organisations: [], | ||
districts: [], | ||
project_types: [], | ||
topics: [], | ||
query_text: '' | ||
} | ||
|
||
jest.mocked(updateItem).mockResolvedValueOnce({ | ||
ok: true, | ||
json: jest.fn().mockResolvedValueOnce(mockedSearchProfile) | ||
}) | ||
|
||
const mockOnSearchProfileCreate = jest.fn() | ||
|
||
const { result } = renderHook(() => | ||
useCreateSearchProfile({ | ||
searchProfilesApiUrl, | ||
appliedFilters, | ||
districts, | ||
organisations, | ||
topicChoices, | ||
participationChoices, | ||
projectStatus, | ||
onSearchProfileCreate: mockOnSearchProfileCreate | ||
}) | ||
) | ||
|
||
await act(async () => { | ||
await result.current.createSearchProfile() | ||
}) | ||
|
||
expect(mockOnSearchProfileCreate).toHaveBeenCalledWith(mockedSearchProfile) | ||
}) | ||
}) |
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.
@goapunk any idea if the district polygons are used anywhere outside the plans, anything to do with bplans? Since @sevfurneaux is removing these methods, would be good to double check with you as well.
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.
@sevfurneaux the map on the project list will not be highlighting the district borders any more? If that's the case I think removing this is fine