From 8d5683d3f18004d44cb4277a67da12c0a88e7698 Mon Sep 17 00:00:00 2001 From: Kevin Morris Date: Mon, 22 Nov 2021 10:28:58 -0800 Subject: [PATCH] change(tuvotereminder): converted to use aurweb.db ORM - Removed tuvotereminder sharness test. - Added [tuvotereminder] section to config.defaults. - Added `range_start` option to config.defaults [tuvotereminder]. - Added `range_end` option to config.defaults [tuvotereminder]. Signed-off-by: Kevin Morris --- aurweb/scripts/tuvotereminder.py | 33 ++++++---- conf/config.defaults | 8 +++ test/t2200-tuvotereminder.t | 53 ---------------- test/test_tuvotereminder.py | 102 +++++++++++++++++++++++++++++++ 4 files changed, 131 insertions(+), 65 deletions(-) delete mode 100755 test/t2200-tuvotereminder.t create mode 100644 test/test_tuvotereminder.py diff --git a/aurweb/scripts/tuvotereminder.py b/aurweb/scripts/tuvotereminder.py index eb3874e1..5e860725 100755 --- a/aurweb/scripts/tuvotereminder.py +++ b/aurweb/scripts/tuvotereminder.py @@ -1,27 +1,36 @@ #!/usr/bin/env python3 -import subprocess -import time +from datetime import datetime + +from sqlalchemy import and_ import aurweb.config -import aurweb.db + +from aurweb import db +from aurweb.models import TUVoteInfo +from aurweb.scripts import notify notify_cmd = aurweb.config.get('notifications', 'notify-cmd') def main(): - conn = aurweb.db.Connection() + db.get_engine() - now = int(time.time()) - filter_from = now + 500 - filter_to = now + 172800 + now = int(datetime.utcnow().timestamp()) - cur = conn.execute("SELECT ID FROM TU_VoteInfo " + - "WHERE End >= ? AND End <= ?", - [filter_from, filter_to]) + start = aurweb.config.getint("tuvotereminder", "range_start") + filter_from = now + start - for vote_id in [row[0] for row in cur.fetchall()]: - subprocess.Popen((notify_cmd, 'tu-vote-reminder', str(vote_id))).wait() + end = aurweb.config.getint("tuvotereminder", "range_end") + filter_to = now + end + + query = db.query(TUVoteInfo.ID).filter( + and_(TUVoteInfo.End >= filter_from, + TUVoteInfo.End <= filter_to) + ) + for voteinfo in query: + notif = notify.TUVoteReminderNotification(voteinfo.ID) + notif.send() if __name__ == '__main__': diff --git a/conf/config.defaults b/conf/config.defaults index a589997b..082d51a5 100644 --- a/conf/config.defaults +++ b/conf/config.defaults @@ -121,3 +121,11 @@ commit_url = https://gitlab.archlinux.org/archlinux/aurweb/-/commits/%s ; Example deployment configuration step: ; sed -r "s/^;?(commit_hash) =.*$/\1 = $(git rev-parse HEAD)/" config ;commit_hash = 1234567 + +[tuvotereminder] +; Offsets used to determine when TUs should be reminded about +; votes that they should make. +; Reminders will be sent out for all votes that a TU has not yet +; voted on based on `now + range_start <= End <= now + range_end`. +range_start = 500 +range_end = 172800 diff --git a/test/t2200-tuvotereminder.t b/test/t2200-tuvotereminder.t deleted file mode 100755 index 2f3836de..00000000 --- a/test/t2200-tuvotereminder.t +++ /dev/null @@ -1,53 +0,0 @@ -#!/bin/sh - -test_description='tuvotereminder tests' - -. "$(dirname "$0")/setup.sh" - -test_expect_success 'Test Trusted User vote reminders.' ' - now=$(date -d now +%s) && - tomorrow=$(date -d tomorrow +%s) && - threedays=$(date -d "3 days" +%s) && - cat <<-EOD | sqlite3 aur.db && - INSERT INTO TU_VoteInfo (ID, Agenda, User, Submitted, End, Quorum, SubmitterID) VALUES (1, "Lorem ipsum.", "user", 0, $now, 0.00, 2); - INSERT INTO TU_VoteInfo (ID, Agenda, User, Submitted, End, Quorum, SubmitterID) VALUES (2, "Lorem ipsum.", "user", 0, $tomorrow, 0.00, 2); - INSERT INTO TU_VoteInfo (ID, Agenda, User, Submitted, End, Quorum, SubmitterID) VALUES (3, "Lorem ipsum.", "user", 0, $tomorrow, 0.00, 2); - INSERT INTO TU_VoteInfo (ID, Agenda, User, Submitted, End, Quorum, SubmitterID) VALUES (4, "Lorem ipsum.", "user", 0, $threedays, 0.00, 2); - EOD - >sendmail.out && - cover "$TUVOTEREMINDER" && - grep -q "Proposal 2" sendmail.out && - grep -q "Proposal 3" sendmail.out && - test_must_fail grep -q "Proposal 1" sendmail.out && - test_must_fail grep -q "Proposal 4" sendmail.out -' - -test_expect_success 'Check that only TUs who did not vote receive reminders.' ' - cat <<-EOD | sqlite3 aur.db && - INSERT INTO TU_Votes (VoteID, UserID) VALUES (1, 2); - INSERT INTO TU_Votes (VoteID, UserID) VALUES (2, 2); - INSERT INTO TU_Votes (VoteID, UserID) VALUES (3, 2); - INSERT INTO TU_Votes (VoteID, UserID) VALUES (4, 2); - INSERT INTO TU_Votes (VoteID, UserID) VALUES (1, 7); - INSERT INTO TU_Votes (VoteID, UserID) VALUES (3, 7); - INSERT INTO TU_Votes (VoteID, UserID) VALUES (2, 8); - INSERT INTO TU_Votes (VoteID, UserID) VALUES (4, 8); - INSERT INTO TU_Votes (VoteID, UserID) VALUES (1, 9); - EOD - >sendmail.out && - cover "$TUVOTEREMINDER" && - cat <<-EOD >expected && - Subject: TU Vote Reminder: Proposal 2 - To: tu2@localhost - Subject: TU Vote Reminder: Proposal 2 - To: tu4@localhost - Subject: TU Vote Reminder: Proposal 3 - To: tu3@localhost - Subject: TU Vote Reminder: Proposal 3 - To: tu4@localhost - EOD - grep "^\(Subject\|To\)" sendmail.out >sendmail.parts && - test_cmp sendmail.parts expected -' - -test_done diff --git a/test/test_tuvotereminder.py b/test/test_tuvotereminder.py new file mode 100644 index 00000000..bb898e3a --- /dev/null +++ b/test/test_tuvotereminder.py @@ -0,0 +1,102 @@ +from datetime import datetime +from typing import Tuple + +import pytest + +from aurweb import config, db +from aurweb.models import TUVote, TUVoteInfo, User +from aurweb.models.account_type import TRUSTED_USER_ID +from aurweb.scripts import tuvotereminder as reminder +from aurweb.testing.email import Email + +aur_location = config.get("options", "aur_location") + + +def create_vote(user: User, voteinfo: TUVoteInfo) -> TUVote: + with db.begin(): + vote = db.create(TUVote, User=user, VoteID=voteinfo.ID) + return vote + + +def create_user(username: str, type_id: int): + with db.begin(): + user = db.create(User, AccountTypeID=type_id, Username=username, + Email=f"{username}@example.org", Passwd=str()) + return user + + +def email_pieces(voteinfo: TUVoteInfo) -> Tuple[str, str]: + """ + Return a (subject, content) tuple based on voteinfo.ID + + :param voteinfo: TUVoteInfo instance + :return: tuple(subject, content) + """ + subject = f"TU Vote Reminder: Proposal {voteinfo.ID}" + content = (f"Please remember to cast your vote on proposal {voteinfo.ID} " + f"[1]. The voting period\nends in less than 48 hours.\n\n" + f"[1] {aur_location}/tu/?id={voteinfo.ID}") + return (subject, content) + + +@pytest.fixture +def user(db_test) -> User: + yield create_user("test", TRUSTED_USER_ID) + + +@pytest.fixture +def user2() -> User: + yield create_user("test2", TRUSTED_USER_ID) + + +@pytest.fixture +def user3() -> User: + yield create_user("test3", TRUSTED_USER_ID) + + +@pytest.fixture +def voteinfo(user: User) -> TUVoteInfo: + now = int(datetime.utcnow().timestamp()) + start = config.getint("tuvotereminder", "range_start") + with db.begin(): + voteinfo = db.create(TUVoteInfo, Agenda="Lorem ipsum.", + User=user.Username, End=(now + start + 1), + Quorum=0.00, Submitter=user, Submitted=0) + yield voteinfo + + +def test_tu_vote_reminders(user: User, user2: User, user3: User, + voteinfo: TUVoteInfo): + reminder.main() + assert Email.count() == 3 + + emails = [Email(i).parse() for i in range(1, 4)] + subject, content = email_pieces(voteinfo) + expectations = [ + # (to, content) + (user.Email, subject, content), + (user2.Email, subject, content), + (user3.Email, subject, content) + ] + for i, element in enumerate(expectations): + email, subject, content = element + assert emails[i].headers.get("To") == email + assert emails[i].headers.get("Subject") == subject + assert emails[i].body == content + + +def test_tu_vote_reminders_only_unvoted(user: User, user2: User, user3: User, + voteinfo: TUVoteInfo): + # Vote with user2 and user3; leaving only user to be notified. + create_vote(user2, voteinfo) + create_vote(user3, voteinfo) + + reminder.main() + assert Email.count() == 1 + + email = Email(1).parse() + assert email.headers.get("To") == user.Email + + subject, content = email_pieces(voteinfo) + assert email.headers.get("Subject") == subject + assert email.body == content