Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
concrexit
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
70
Issues
70
List
Boards
Labels
Service Desk
Milestones
Merge Requests
10
Merge Requests
10
Operations
Operations
Incidents
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
thalia
concrexit
Commits
4a5d8b29
Verified
Commit
4a5d8b29
authored
Mar 02, 2019
by
Sébastiaan Versteeg
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add manual data minimisation functionality
parent
c0062b4c
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
91 additions
and
37 deletions
+91
-37
website/members/admin.py
website/members/admin.py
+23
-3
website/members/services.py
website/members/services.py
+9
-5
website/members/tests/test_services.py
website/members/tests/test_services.py
+59
-29
No files found.
website/members/admin.py
View file @
4a5d8b29
...
...
@@ -3,7 +3,7 @@ This module registers admin pages for the models
"""
import
csv
import
datetime
from
django.contrib
import
admin
from
django.contrib
import
admin
,
messages
from
django.contrib.auth.admin
import
UserAdmin
as
BaseUserAdmin
from
django.contrib.auth.models
import
User
from
django.db.models
import
Q
...
...
@@ -11,7 +11,8 @@ from django.http import HttpResponse
from
django.utils
import
timezone
from
django.utils.translation
import
ugettext_lazy
as
_
from
members.models
import
EmailChange
from
members
import
services
from
members.models
import
EmailChange
,
Member
from
.
import
forms
,
models
...
...
@@ -93,7 +94,7 @@ class UserAdmin(BaseUserAdmin):
add_form
=
forms
.
UserCreationForm
actions
=
[
'address_csv_export'
,
'student_number_csv_export'
,
'email_csv_export'
]
'email_csv_export'
,
'minimise_data'
]
inlines
=
(
ProfileInline
,
MembershipInline
,)
list_filter
=
(
MembershipTypeListFilter
,
...
...
@@ -161,6 +162,25 @@ class UserAdmin(BaseUserAdmin):
student_number_csv_export
.
short_description
=
_
(
'Download student number '
'label for selected users'
)
def
minimise_data
(
self
,
request
,
queryset
):
processed
=
len
(
services
.
execute_data_minimisation
(
members
=
Member
.
objects
.
filter
(
pk__in
=
queryset
)))
if
processed
==
0
:
self
.
message_user
(
request
,
_
(
'Data minimisation could not be executed '
'for the selected user(s).'
),
messages
.
ERROR
)
else
:
self
.
message_user
(
request
,
_
(
'Data minimisation was executed '
'for {} user(s).'
).
format
(
processed
),
messages
.
SUCCESS
)
minimise_data
.
short_description
=
_
(
'Minimise data for the selected users'
)
@
admin
.
register
(
models
.
Member
)
class
MemberAdmin
(
UserAdmin
):
...
...
website/members/services.py
View file @
4a5d8b29
from
datetime
import
date
from
django.db.models
import
Q
from
django.db.models
import
Q
,
Count
from
django.utils
import
timezone
from
django.utils.translation
import
gettext
...
...
@@ -164,17 +164,21 @@ def process_email_change(change_request):
emails
.
send_email_change_completion_message
(
change_request
)
def
execute_data_minimisation
(
dry_run
=
False
):
def
execute_data_minimisation
(
dry_run
=
False
,
members
=
None
):
"""
Clean the profiles of members/users of whom the last membership ended
at least 31 days ago
:param dry_run: does not really remove data if True
:param members: queryset of members to process, optional
:return: list of processed members
"""
members
=
(
Member
.
objects
.
exclude
(
Q
(
membership__until__isnull
=
True
)
|
Q
(
membership__until__gt
=
timezone
.
now
().
date
()))
if
not
members
:
members
=
Member
.
objects
members
=
(
members
.
annotate
(
membership_count
=
Count
(
'membership'
))
.
exclude
((
Q
(
membership__until__isnull
=
True
)
|
Q
(
membership__until__gt
=
timezone
.
now
().
date
()))
&
Q
(
membership_count__gt
=
0
))
.
distinct
()
.
prefetch_related
(
'membership_set'
,
'profile'
))
deletion_period
=
timezone
.
now
().
date
()
-
timezone
.
timedelta
(
days
=
31
)
...
...
website/members/tests/test_services.py
View file @
4a5d8b29
...
...
@@ -224,77 +224,107 @@ class DataMinimisationTest(TestCase):
@
classmethod
def
setUpTestData
(
cls
):
cls
.
m
ember
=
Member
.
objects
.
create
(
cls
.
m
1
=
Member
.
objects
.
create
(
username
=
'test1'
,
first_name
=
'Test1'
,
last_name
=
'Example'
,
email
=
'test1@example.org'
)
Profile
.
objects
.
create
(
user
=
cls
.
m
ember
,
user
=
cls
.
m
1
,
language
=
'nl'
,
student_number
=
's1234567'
)
cls
.
membership
=
Membership
.
objects
.
create
(
user
=
cls
.
member
,
cls
.
s1
=
Membership
.
objects
.
create
(
user
=
cls
.
m1
,
type
=
Membership
.
MEMBER
,
since
=
timezone
.
now
().
replace
(
year
=
2017
,
month
=
9
,
day
=
1
),
until
=
timezone
.
now
().
replace
(
year
=
2018
,
month
=
9
,
day
=
1
)
)
cls
.
m2
=
Member
.
objects
.
create
(
username
=
'test2'
,
first_name
=
'Test2'
,
last_name
=
'Example'
,
email
=
'test2@example.org'
)
Profile
.
objects
.
create
(
user
=
cls
.
m2
,
language
=
'nl'
,
student_number
=
's7654321'
)
cls
.
s2
=
Membership
.
objects
.
create
(
user
=
cls
.
m2
,
type
=
Membership
.
MEMBER
,
since
=
timezone
.
now
().
replace
(
year
=
2017
,
month
=
9
,
day
=
1
),
until
=
timezone
.
now
().
replace
(
year
=
2018
,
month
=
9
,
day
=
1
)
)
def
test_removes_after_31_days
(
self
):
processed
=
services
.
execute_data_minimisation
(
True
)
self
.
assertEqual
(
len
(
processed
),
1
)
self
.
assertEqual
(
processed
[
0
],
self
.
member
)
def
test_removes_after_31_days_or_no_membership
(
self
):
with
self
.
subTest
(
'Deletes after 31 days'
):
processed
=
services
.
execute_data_minimisation
(
True
)
self
.
assertEqual
(
len
(
processed
),
2
)
self
.
assertEqual
(
processed
[
0
],
self
.
m1
)
self
.
membership
.
until
=
timezone
.
now
().
replace
(
year
=
2018
,
month
=
11
,
day
=
1
)
self
.
membership
.
save
()
processed
=
services
.
execute_data_minimisation
(
True
)
self
.
assertEqual
(
len
(
processed
),
0
)
with
self
.
subTest
(
'Deletes after 31 days'
):
self
.
s1
.
until
=
timezone
.
now
().
replace
(
year
=
2018
,
month
=
11
,
day
=
1
)
self
.
s1
.
save
()
processed
=
services
.
execute_data_minimisation
(
True
)
self
.
assertEqual
(
len
(
processed
),
1
)
with
self
.
subTest
(
'Deletes when no memberships'
):
self
.
s1
.
delete
()
processed
=
services
.
execute_data_minimisation
(
True
)
self
.
assertEqual
(
len
(
processed
),
2
)
def
test_dry_run
(
self
):
with
self
.
subTest
(
'With dry_run=True'
):
services
.
execute_data_minimisation
(
True
)
self
.
m
ember
.
refresh_from_db
()
self
.
assertEqual
(
self
.
m
ember
.
profile
.
student_number
,
's1234567'
)
self
.
m
1
.
refresh_from_db
()
self
.
assertEqual
(
self
.
m
1
.
profile
.
student_number
,
's1234567'
)
with
self
.
subTest
(
'With dry_run=False'
):
services
.
execute_data_minimisation
(
False
)
self
.
member
.
refresh_from_db
()
self
.
assertIsNone
(
self
.
member
.
profile
.
student_number
)
self
.
m1
.
refresh_from_db
()
self
.
assertIsNone
(
self
.
m1
.
profile
.
student_number
)
def
test_provided_queryset
(
self
):
processed
=
services
.
execute_data_minimisation
(
True
,
members
=
Member
.
objects
)
self
.
assertEqual
(
len
(
processed
),
2
)
self
.
assertEqual
(
processed
[
0
],
self
.
m1
)
def
test_does_not_affect_current_members
(
self
):
with
self
.
subTest
(
'Membership ends in future'
):
self
.
membership
.
until
=
timezone
.
now
().
replace
(
self
.
s1
.
until
=
timezone
.
now
().
replace
(
year
=
2019
,
month
=
9
,
day
=
1
)
self
.
membership
.
save
()
self
.
s1
.
save
()
processed
=
services
.
execute_data_minimisation
(
True
)
self
.
assertEqual
(
len
(
processed
),
0
)
self
.
assertEqual
(
len
(
processed
),
1
)
with
self
.
subTest
(
'Never ending membership'
):
self
.
membership
.
until
=
None
self
.
membership
.
save
()
self
.
s1
.
until
=
None
self
.
s1
.
save
()
processed
=
services
.
execute_data_minimisation
(
True
)
self
.
assertEqual
(
len
(
processed
),
0
)
self
.
membership
.
until
=
timezone
.
now
().
replace
(
self
.
assertEqual
(
len
(
processed
),
1
)
self
.
s1
.
until
=
timezone
.
now
().
replace
(
year
=
2018
,
month
=
9
,
day
=
1
)
self
.
membership
.
save
()
self
.
s1
.
save
()
with
self
.
subTest
(
'Newer year membership after expired one'
):
m
=
Membership
.
objects
.
create
(
user
=
self
.
m
ember
,
user
=
self
.
m
1
,
type
=
Membership
.
MEMBER
,
since
=
timezone
.
now
().
replace
(
year
=
2018
,
month
=
9
,
day
=
10
),
until
=
timezone
.
now
().
replace
(
year
=
2019
,
month
=
8
,
day
=
31
),
)
processed
=
services
.
execute_data_minimisation
(
True
)
self
.
assertEqual
(
len
(
processed
),
0
)
self
.
assertEqual
(
len
(
processed
),
1
)
m
.
delete
()
with
self
.
subTest
(
'Newer study membership after expired one'
):
m
=
Membership
.
objects
.
create
(
user
=
self
.
m
ember
,
user
=
self
.
m
1
,
type
=
Membership
.
MEMBER
,
since
=
timezone
.
now
().
replace
(
year
=
2018
,
month
=
9
,
day
=
10
),
until
=
None
)
processed
=
services
.
execute_data_minimisation
(
True
)
self
.
assertEqual
(
len
(
processed
),
0
)
self
.
assertEqual
(
len
(
processed
),
1
)
m
.
delete
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment