Skip to content
Snippets Groups Projects
Verified Commit 7cfa6129 authored by Eliot Berriot's avatar Eliot Berriot
Browse files

See #248: can now filter on invitation status and delete invitations

parent 7b0148a5
No related branches found
No related tags found
No related merge requests found
......@@ -40,7 +40,13 @@ class ManageUserFilterSet(filters.FilterSet):
class ManageInvitationFilterSet(filters.FilterSet):
q = fields.SearchFilter(search_fields=["owner__username", "code", "owner__email"])
is_open = filters.BooleanFilter(method="filter_is_open")
class Meta:
model = users_models.Invitation
fields = ["q"]
fields = ["q", "is_open"]
def filter_is_open(self, queryset, field_name, value):
if value is None:
return queryset
return queryset.open(value)
......@@ -151,3 +151,12 @@ class ManageInvitationSerializer(serializers.ModelSerializer):
"An invitation with this code already exists"
)
return value
class ManageInvitationActionSerializer(common_serializers.ActionSerializer):
actions = [common_serializers.Action("delete", allow_all=False)]
filterset_class = filters.ManageInvitationFilterSet
@transaction.atomic
def handle_delete(self, objects):
return objects.delete()
......@@ -86,3 +86,13 @@ class ManageInvitationViewSet(
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
@list_route(methods=["post"])
def action(self, request, *args, **kwargs):
queryset = self.get_queryset()
serializer = serializers.ManageInvitationActionSerializer(
request.data, queryset=queryset
)
serializer.is_valid(raise_exception=True)
result = serializer.save()
return response.Response(result, status=200)
......@@ -157,12 +157,13 @@ def generate_code(length=10):
class InvitationQuerySet(models.QuerySet):
def open(self):
def open(self, include=True):
now = timezone.now()
qs = self.annotate(_users=models.Count("users"))
qs = qs.filter(_users=0)
qs = qs.exclude(expiration_date__lte=now)
return qs
query = models.Q(_users=0, expiration_date__gt=now)
if include:
return qs.filter(query)
return qs.exclude(query)
class Invitation(models.Model):
......
......@@ -118,3 +118,12 @@ def test_can_filter_open_invitations(factories):
assert models.Invitation.objects.count() == 3
assert list(models.Invitation.objects.open()) == [okay]
def test_can_filter_closed_invitations(factories):
factories["users.Invitation"]()
expired = factories["users.Invitation"](expired=True)
used = factories["users.User"](invited=True).invitation
assert models.Invitation.objects.count() == 3
assert list(models.Invitation.objects.open(False)) == [expired, used]
......@@ -36,7 +36,7 @@
<div class="count field">
<span v-if="selectAll">{{ $t('{% count %} on {% total %} selected', {count: objectsData.count, total: objectsData.count}) }}</span>
<span v-else>{{ $t('{% count %} on {% total %} selected', {count: checked.length, total: objectsData.count}) }}</span>
<template v-if="!currentAction.isDangerous && checkable.length === checked.length">
<template v-if="!currentAction.isDangerous && checkable.length > 0 && checkable.length === checked.length">
<a @click="selectAll = true" v-if="!selectAll">
{{ $t('Select all {% total %} elements', {total: objectsData.count}) }}
</a>
......@@ -157,6 +157,7 @@ export default {
let self = this
self.actionLoading = true
self.result = null
self.actionErrors = []
let payload = {
action: this.currentActionName,
filters: this.filters
......
<template>
<div>
<form v-if="!over" class="ui form" @submit.prevent="submit">
<form class="ui form" @submit.prevent="submit">
<div v-if="errors.length > 0" class="ui negative message">
<div class="header">{{ $t('Error while creating invitation') }}</div>
<ul class="list">
......
......@@ -7,7 +7,7 @@
<input type="text" v-model="search" placeholder="Search by username, email, code..." />
</div>
<div class="field">
<i18next tag="label" path="Ordering"/>
<label>{{ $t("Ordering") }}</label>
<select class="ui dropdown" v-model="ordering">
<option v-for="option in orderingOptions" :value="option[0]">
{{ option[1] }}
......@@ -15,10 +15,11 @@
</select>
</div>
<div class="field">
<i18next tag="label" path="Ordering direction"/>
<select class="ui dropdown" v-model="orderingDirection">
<option value="+">{{ $t('Ascending') }}</option>
<option value="-">{{ $t('Descending') }}</option>
<label>{{ $t("Status") }}</label>
<select class="ui dropdown" v-model="isOpen">
<option :value="null">{{ $t('All') }}</option>
<option :value="true">{{ $t('Open') }}</option>
<option :value="false">{{ $t('Expired/used') }}</option>
</select>
</div>
</div>
......@@ -47,7 +48,7 @@
</td>
<td>
<span v-if="scope.obj.users.length > 0" class="ui green basic label">{{ $t('Used') }}</span>
<span v-else-if="scope.obj.expiration_date < new Date()" class="ui red basic label">{{ $t('Expired') }}</span>
<span v-else-if="moment().isAfter(scope.obj.expiration_date)" class="ui red basic label">{{ $t('Expired') }}</span>
<span v-else class="ui basic label">{{ $t('Not used') }}</span>
</td>
<td>
......@@ -81,8 +82,8 @@
<script>
import axios from 'axios'
import moment from 'moment'
import _ from 'lodash'
import time from '@/utils/time'
import Pagination from '@/components/Pagination'
import ActionTable from '@/components/common/ActionTable'
import OrderingMixin from '@/components/mixins/Ordering'
......@@ -99,12 +100,13 @@ export default {
data () {
let defaultOrdering = this.getOrderingFromString(this.defaultOrdering || '-creation_date')
return {
time,
moment,
isLoading: false,
result: null,
page: 1,
paginateBy: 50,
search: '',
isOpen: null,
orderingDirection: defaultOrdering.direction || '+',
ordering: defaultOrdering.field,
orderingOptions: [
......@@ -123,6 +125,7 @@ export default {
'page': this.page,
'page_size': this.paginateBy,
'q': this.search,
'is_open': this.isOpen,
'ordering': this.getOrderingAsString()
}, this.filters)
let self = this
......@@ -153,11 +156,13 @@ export default {
},
actions () {
return [
// {
// name: 'delete',
// label: this.$t('Delete'),
// isDangerous: true
// }
{
name: 'delete',
label: this.$t('Delete'),
filterCheckable: (obj) => {
return obj.users.length === 0 && moment().isBefore(obj.expiration_date)
}
}
]
}
},
......@@ -170,9 +175,15 @@ export default {
this.fetchData()
},
ordering () {
this.page = 1
this.fetchData()
},
isOpen () {
this.page = 1
this.fetchData()
},
orderingDirection () {
this.page = 1
this.fetchData()
}
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment