Skip to content

Commit 4b76802

Browse files
committed
Add bulk mail scrubbing without user log entries
1 parent 3c8e0f1 commit 4b76802

File tree

3 files changed

+61
-14
lines changed

3 files changed

+61
-14
lines changed

pycroft/lib/user_deletion.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@
1313
from sqlalchemy import CTE, ScalarResult, func, and_, not_
1414
from sqlalchemy.future import select
1515
from sqlalchemy.orm import joinedload, Session
16-
from sqlalchemy.sql import Select
16+
from sqlalchemy.sql import Select, insert, literal_column, update
1717

1818
from pycroft.helpers.i18n.deferred import deferred_gettext
1919
from pycroft.lib.logging import log_user_event
2020
from pycroft.model.host import Host
2121
from pycroft.model.property import CurrentProperty
2222
from pycroft.model.scrubbing import ScrubLog
23+
from pycroft.model.session import current_timestamp, utcnow
2324
from pycroft.model.user import RoomHistoryEntry, User
2425
from pycroft.lib.membership import select_user_and_last_mem
2526

@@ -165,8 +166,22 @@ def scrub_mail(session: Session, user: User, author: User):
165166

166167

167168
def scrub_all_mails(session: Session, author: User):
168-
stmt, _ = scrubbable_mails(session)
169-
# TODO implement
169+
year = utcnow().year
170+
users_with_mail = scrubbable_mails_stmt(year)
171+
ids_subq = users_with_mail.with_only_columns(User.id).scalar_subquery()
172+
# TODO report affected cols in return value
173+
_ = session.execute(update(User).where(User.id.in_(ids_subq)).values(email=None))
174+
# TODO insert user_log_entry
175+
_ = session.execute(
176+
insert(ScrubLog).from_select(
177+
("executed_at", "scrubber", "info"),
178+
users_with_mail.with_only_columns(
179+
current_timestamp().label("executed_at"),
180+
literal_column("'mail'").label("scrubber"),
181+
func.jsonb_build_object("user_id", User.id).label("info"),
182+
),
183+
)
184+
)
170185

171186

172187
def scrubbable_hosts_stmt(year: int) -> Select[tuple[Host]]:

tests/lib/scrubbing/conftest.py

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,43 @@ def user_archivable(module_session: Session, config: Config) -> User:
3232
)
3333

3434

35+
@pytest.fixture(scope="module")
36+
def users_archivable(module_session: Session, config: Config) -> list[User]:
37+
# TODO: user wtih membership
38+
return f.UserFactory.create_batch(
39+
10,
40+
registered_at=datetime(2020, 7, 1),
41+
with_membership=True,
42+
membership__active_during=closedopen(datetime(2020, 7, 1), datetime(2021, 11, 25)),
43+
membership__group=config.member_group,
44+
without_room=True,
45+
)
46+
47+
3548
@pytest.fixture(scope="module", autouse=True)
36-
def user_do_not_archive(
49+
def users_do_not_archive(
3750
module_session: Session, config: Config, group_do_not_archive: PropertyGroup
38-
) -> User:
51+
) -> list[User]:
3952
"""
4053
Create a user with a membership in a group that has the do-not-archive
4154
property.
4255
"""
43-
# TODO _very old_ membership, but also mem in a group
44-
user = f.UserFactory.create(
45-
registered_at=datetime(2020, 7, 1),
56+
# TODO wayyy better example set! instead of making all `do-not-archive`,
57+
# we would want them to have different membership ranges, also additional memberships;
58+
# also edge-cases like upcoming memberships or on-new-years membership terminations
59+
beginning = datetime(2020, 7, 1)
60+
users = f.UserFactory.create_batch(
61+
10,
62+
registered_at=beginning,
4663
with_membership=True,
47-
membership__active_during=closedopen(datetime(2020, 7, 1), datetime(2021, 11, 25)),
64+
membership__active_during=closedopen(beginning, datetime(2021, 11, 25)),
4865
membership__group=config.member_group,
4966
without_room=True,
5067
)
51-
f.MembershipFactory.create(
52-
user=user, group=group_do_not_archive, active_during=closedopen(datetime(2020, 7, 1), None)
53-
)
54-
return user
68+
for user in users:
69+
f.MembershipFactory.create(
70+
user=user,
71+
group=group_do_not_archive,
72+
active_during=closedopen(beginning, None),
73+
)
74+
return users

tests/lib/scrubbing/test_mail_scrubbing.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from itertools import chain
12
import pytest
23
from sqlalchemy import select
34
from sqlalchemy.orm import Session
@@ -35,6 +36,17 @@ def test_scrubbing_scrubs_mail(user_archivable: User, processor: User, session:
3536

3637

3738
def test_bulk_scrubbing_scrubs_mail(
38-
users_archivable: list[User], users_notarchivable: list[User], processor: User, session: Session
39+
users_archivable: list[User],
40+
users_do_not_archive: list[User],
41+
processor: User,
42+
session: Session,
3943
):
4044
scrub_all_mails(session, author=processor)
45+
for u in chain(users_archivable, users_do_not_archive):
46+
session.refresh(u)
47+
assert all(
48+
u.email is None for u in users_archivable
49+
), "mails should have been None for prepared users"
50+
assert all(
51+
u.email is not None for u in users_do_not_archive
52+
), "mails should not have been scrubbed for do-not-archive users"

0 commit comments

Comments
 (0)