Skip to content

Commit

Permalink
Merge branch 'dev' into formulaires-intro-et-conclusion
Browse files Browse the repository at this point in the history
  • Loading branch information
philippemilink authored Sep 24, 2024
2 parents e3b83bd + b14be34 commit f555774
Show file tree
Hide file tree
Showing 3 changed files with 51 additions and 33 deletions.
36 changes: 6 additions & 30 deletions assets/js/autocompletion.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,12 @@
jsonData
.done(function(data) {
self.updateCache(data.results)
self.updateDropdown(self.sortList(data.results, search))
self.updateDropdown(data.results)
})
.fail(function() {
console.error('[Autocompletition] Something went wrong...')
console.error('[Autocompletion] Something went wrong...')
})
this.updateDropdown(this.sortList(this.searchCache(search), search))
this.updateDropdown(this.searchCache(search))
this.showDropdown()
}
}
Expand Down Expand Up @@ -236,7 +236,9 @@
self.handleInput()
}

list = self.filterData(list, self.extractWords(this.$input.val()))
// Exclude already selected items but the last one (it's the prefix of the item we are looking for)
const alreadyChosenWords = self.extractWords(this.$input.val()).slice(0, -1)
list = self.filterData(list, alreadyChosenWords)

if (list.length > this.options.limit) list = list.slice(0, this.options.limit)

Expand All @@ -263,32 +265,6 @@
if (!selected) { this.select($list.find('li').first().attr('data-autocomplete-id')) }
},

sortList: function(list, search) {
const bestMatches = []
const otherMatches = []

for (let i = 0; i < list.length; i++) {
if (list[i][this.options.fieldname].indexOf(search) === 0) {
bestMatches.push(list[i])
} else {
otherMatches.push(list[i])
}
}

const sortFn = function(a, b) {
const valueA = a[this.options.fieldname].toLowerCase()
const valueB = b[this.options.fieldname].toLowerCase()
if (valueA < valueB) { return -1 }
if (valueA > valueB) { return 1 }
return 0
}

bestMatches.sort(sortFn.bind(this))
otherMatches.sort(sortFn.bind(this))

return bestMatches.concat(otherMatches)
},

fetchData: function(input, excludeTerms) {
let data = this.options.url.replace('%s', input)
data = data.replace('%e', excludeTerms)
Expand Down
23 changes: 23 additions & 0 deletions zds/member/api/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,29 @@ def test_search_without_results_in_list_of_users(self):
self.assertIsNone(response.data.get("next"))
self.assertIsNone(response.data.get("previous"))

def test_search_with_results_in_right_order(self):
"""
Gets list of users corresponding to part of a username and
verifies that this list is in the right order, which is:
1. "is equal" case sensitive
2. "is equal" ignoring the case
3. "starts with" case sensitive
4. "starts with" ignoring the case
5. "contains" case sensitive
6. "contains" ignoring the case
The test also checks that:
- usernames containing letters of the searched word (here: 'a', 'n', 'd' and 'r')
but NOT the searched word ("andr") are not returned
- usernames containing non-ascii letters (eg with accents) can be returned as well
"""
for username in ("pierre", "andr", "Radon", "alexandre", "MisterAndrew", "andré", "dragon", "Andromède"):
ProfileFactory(user__username=username)

response = self.client.get(reverse("api:member:list") + "?search=Andr")
list_of_usernames = [item.get("username") for item in response.data.get("results")]
self.assertEqual(list_of_usernames, ["andr", "Andromède", "andré", "MisterAndrew", "alexandre"])

def test_register_new_user(self):
"""
Registers a new user in the database.
Expand Down
25 changes: 22 additions & 3 deletions zds/member/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from django.utils.translation import gettext_lazy as _
from django.core.cache import cache
from django.db.models import Case, When, IntegerField, Value
from django.db.models.signals import post_save, post_delete
from dry_rest_permissions.generics import DRYPermissions
from rest_framework import filters
Expand Down Expand Up @@ -81,12 +82,30 @@ class MemberListAPI(ListCreateAPIView, ProfileCreate, TokenGenerator):
Profile resource to list and register.
"""

filter_backends = (filters.SearchFilter,)
search_fields = ("user__username",)
list_key_func = PagingSearchListKeyConstructor()

def get_queryset(self):
return Profile.objects.contactable_members()
queryset = Profile.objects.contactable_members()
search_param = self.request.query_params.get("search", None)

if search_param:
queryset = (
queryset.filter(user__username__icontains=search_param)
.annotate(
priority=Case(
When(user__username=search_param, then=Value(1)),
When(user__username__iexact=search_param, then=Value(2)),
When(user__username__startswith=search_param, then=Value(3)),
When(user__username__istartswith=search_param, then=Value(4)),
When(user__username__contains=search_param, then=Value(5)),
default=Value(6),
output_field=IntegerField(),
)
)
.order_by("priority", "user__username")
)

return queryset

@etag(list_key_func)
@cache_response(key_func=list_key_func)
Expand Down

0 comments on commit f555774

Please sign in to comment.