diff --git a/INSTALL b/INSTALL index c6fdf8af..23fb6c3d 100644 --- a/INSTALL +++ b/INSTALL @@ -120,7 +120,7 @@ interval: */2 * * * * bash -c 'poetry run aurweb-pkgmaint' */2 * * * * bash -c 'poetry run aurweb-usermaint' */2 * * * * bash -c 'poetry run aurweb-popupdate' - */12 * * * * bash -c 'poetry run aurweb-tuvotereminder' + */12 * * * * bash -c 'poetry run aurweb-votereminder' 7) Create a new database and a user and import the aurweb SQL schema: diff --git a/aurweb/filters.py b/aurweb/filters.py index 38322cdf..3834b586 100644 --- a/aurweb/filters.py +++ b/aurweb/filters.py @@ -118,9 +118,9 @@ def to_qs(query: dict[str, Any]) -> str: @register_filter("get_vote") def get_vote(voteinfo, request: fastapi.Request): - from aurweb.models import TUVote + from aurweb.models import Vote - return voteinfo.tu_votes.filter(TUVote.User == request.user).first() + return voteinfo.votes.filter(Vote.User == request.user).first() @register_filter("number_format") diff --git a/aurweb/models/__init__.py b/aurweb/models/__init__.py index a06077ad..a7f8fc66 100644 --- a/aurweb/models/__init__.py +++ b/aurweb/models/__init__.py @@ -26,6 +26,6 @@ from .request_type import RequestType # noqa: F401 from .session import Session # noqa: F401 from .ssh_pub_key import SSHPubKey # noqa: F401 from .term import Term # noqa: F401 -from .tu_vote import TUVote # noqa: F401 -from .tu_voteinfo import TUVoteInfo # noqa: F401 from .user import User # noqa: F401 +from .vote import Vote # noqa: F401 +from .voteinfo import VoteInfo # noqa: F401 diff --git a/aurweb/models/tu_vote.py b/aurweb/models/vote.py similarity index 74% rename from aurweb/models/tu_vote.py rename to aurweb/models/vote.py index 412915e2..ec20fe9b 100644 --- a/aurweb/models/tu_vote.py +++ b/aurweb/models/vote.py @@ -3,24 +3,24 @@ from sqlalchemy.orm import backref, relationship from aurweb import schema from aurweb.models.declarative import Base -from aurweb.models.tu_voteinfo import TUVoteInfo as _TUVoteInfo from aurweb.models.user import User as _User +from aurweb.models.voteinfo import VoteInfo as _VoteInfo -class TUVote(Base): - __table__ = schema.TU_Votes +class Vote(Base): + __table__ = schema.Votes __tablename__ = __table__.name __mapper_args__ = {"primary_key": [__table__.c.VoteID, __table__.c.UserID]} VoteInfo = relationship( - _TUVoteInfo, - backref=backref("tu_votes", lazy="dynamic"), + _VoteInfo, + backref=backref("votes", lazy="dynamic"), foreign_keys=[__table__.c.VoteID], ) User = relationship( _User, - backref=backref("tu_votes", lazy="dynamic"), + backref=backref("votes", lazy="dynamic"), foreign_keys=[__table__.c.UserID], ) @@ -30,13 +30,13 @@ class TUVote(Base): if not self.VoteInfo and not self.VoteID: raise IntegrityError( statement="Foreign key VoteID cannot be null.", - orig="TU_Votes.VoteID", + orig="Votes.VoteID", params=("NULL"), ) if not self.User and not self.UserID: raise IntegrityError( statement="Foreign key UserID cannot be null.", - orig="TU_Votes.UserID", + orig="Votes.UserID", params=("NULL"), ) diff --git a/aurweb/models/tu_voteinfo.py b/aurweb/models/voteinfo.py similarity index 86% rename from aurweb/models/tu_voteinfo.py rename to aurweb/models/voteinfo.py index 63fcee23..b7480661 100644 --- a/aurweb/models/tu_voteinfo.py +++ b/aurweb/models/voteinfo.py @@ -8,14 +8,14 @@ from aurweb.models.declarative import Base from aurweb.models.user import User as _User -class TUVoteInfo(Base): - __table__ = schema.TU_VoteInfo +class VoteInfo(Base): + __table__ = schema.VoteInfo __tablename__ = __table__.name __mapper_args__ = {"primary_key": [__table__.c.ID]} Submitter = relationship( _User, - backref=backref("tu_voteinfo_set", lazy="dynamic"), + backref=backref("voteinfo_set", lazy="dynamic"), foreign_keys=[__table__.c.SubmitterID], ) @@ -30,35 +30,35 @@ class TUVoteInfo(Base): if self.Agenda is None: raise IntegrityError( statement="Column Agenda cannot be null.", - orig="TU_VoteInfo.Agenda", + orig="VoteInfo.Agenda", params=("NULL"), ) if self.User is None: raise IntegrityError( statement="Column User cannot be null.", - orig="TU_VoteInfo.User", + orig="VoteInfo.User", params=("NULL"), ) if self.Submitted is None: raise IntegrityError( statement="Column Submitted cannot be null.", - orig="TU_VoteInfo.Submitted", + orig="VoteInfo.Submitted", params=("NULL"), ) if self.End is None: raise IntegrityError( statement="Column End cannot be null.", - orig="TU_VoteInfo.End", + orig="VoteInfo.End", params=("NULL"), ) if not self.Submitter: raise IntegrityError( statement="Foreign key SubmitterID cannot be null.", - orig="TU_VoteInfo.SubmitterID", + orig="VoteInfo.SubmitterID", params=("NULL"), ) diff --git a/aurweb/routers/package_maintainer.py b/aurweb/routers/package_maintainer.py index 4ccb25b0..9ce38d07 100644 --- a/aurweb/routers/package_maintainer.py +++ b/aurweb/routers/package_maintainer.py @@ -87,9 +87,9 @@ async def package_maintainer( context["past_by"] = past_by current_votes = ( - db.query(models.TUVoteInfo) - .filter(models.TUVoteInfo.End > ts) - .order_by(models.TUVoteInfo.Submitted.desc()) + db.query(models.VoteInfo) + .filter(models.VoteInfo.End > ts) + .order_by(models.VoteInfo.Submitted.desc()) ) context["current_votes_count"] = current_votes.count() current_votes = current_votes.limit(pp).offset(current_off) @@ -99,9 +99,9 @@ async def package_maintainer( context["current_off"] = current_off past_votes = ( - db.query(models.TUVoteInfo) - .filter(models.TUVoteInfo.End <= ts) - .order_by(models.TUVoteInfo.Submitted.desc()) + db.query(models.VoteInfo) + .filter(models.VoteInfo.End <= ts) + .order_by(models.VoteInfo.Submitted.desc()) ) context["past_votes_count"] = past_votes.count() past_votes = past_votes.limit(pp).offset(past_off) @@ -110,21 +110,21 @@ async def package_maintainer( ) context["past_off"] = past_off - last_vote = func.max(models.TUVote.VoteID).label("LastVote") + last_vote = func.max(models.Vote.VoteID).label("LastVote") last_votes_by_pm = ( - db.query(models.TUVote) + db.query(models.Vote) .join(models.User) - .join(models.TUVoteInfo, models.TUVoteInfo.ID == models.TUVote.VoteID) + .join(models.VoteInfo, models.VoteInfo.ID == models.Vote.VoteID) .filter( and_( - models.TUVote.VoteID == models.TUVoteInfo.ID, - models.User.ID == models.TUVote.UserID, - models.TUVoteInfo.End < ts, + models.Vote.VoteID == models.VoteInfo.ID, + models.User.ID == models.Vote.UserID, + models.VoteInfo.End < ts, or_(models.User.AccountTypeID == 2, models.User.AccountTypeID == 4), ) ) - .with_entities(models.TUVote.UserID, last_vote, models.User.Username) - .group_by(models.TUVote.UserID) + .with_entities(models.Vote.UserID, last_vote, models.User.Username) + .group_by(models.Vote.UserID) .order_by(last_vote.desc(), models.User.Username.asc()) ) context["last_votes_by_pm"] = last_votes_by_pm.all() @@ -148,9 +148,9 @@ def render_proposal( request: Request, context: dict, proposal: int, - voteinfo: models.TUVoteInfo, + voteinfo: models.VoteInfo, voters: typing.Iterable[models.User], - vote: models.TUVote, + vote: models.Vote, status_code: HTTPStatus = HTTPStatus.OK, ): """Render a single PM proposal.""" @@ -159,15 +159,15 @@ def render_proposal( context["voters"] = voters.all() total = voteinfo.total_votes() - participation = (total / voteinfo.ActiveTUs) if voteinfo.ActiveTUs else 0 + participation = (total / voteinfo.ActiveUsers) if voteinfo.ActiveUsers else 0 context["participation"] = participation - accepted = (voteinfo.Yes > voteinfo.ActiveTUs / 2) or ( + accepted = (voteinfo.Yes > voteinfo.ActiveUsers / 2) or ( participation > voteinfo.Quorum and voteinfo.Yes > voteinfo.No ) context["accepted"] = accepted - can_vote = voters.filter(models.TUVote.User == request.user).first() is None + can_vote = voters.filter(models.Vote.User == request.user).first() is None context["can_vote"] = can_vote if not voteinfo.is_running(): @@ -190,23 +190,21 @@ async def package_maintainer_proposal(request: Request, proposal: int): context = await make_variable_context(request, "Package Maintainer") proposal = int(proposal) - voteinfo = ( - db.query(models.TUVoteInfo).filter(models.TUVoteInfo.ID == proposal).first() - ) + voteinfo = db.query(models.VoteInfo).filter(models.VoteInfo.ID == proposal).first() if not voteinfo: raise HTTPException(status_code=HTTPStatus.NOT_FOUND) voters = ( db.query(models.User) - .join(models.TUVote) - .filter(models.TUVote.VoteID == voteinfo.ID) + .join(models.Vote) + .filter(models.Vote.VoteID == voteinfo.ID) ) vote = ( - db.query(models.TUVote) + db.query(models.Vote) .filter( and_( - models.TUVote.UserID == request.user.ID, - models.TUVote.VoteID == voteinfo.ID, + models.Vote.UserID == request.user.ID, + models.Vote.VoteID == voteinfo.ID, ) ) .first() @@ -235,23 +233,21 @@ async def package_maintainer_proposal_post( context = await make_variable_context(request, "Package Maintainer") proposal = int(proposal) # Make sure it's an int. - voteinfo = ( - db.query(models.TUVoteInfo).filter(models.TUVoteInfo.ID == proposal).first() - ) + voteinfo = db.query(models.VoteInfo).filter(models.VoteInfo.ID == proposal).first() if not voteinfo: raise HTTPException(status_code=HTTPStatus.NOT_FOUND) voters = ( db.query(models.User) - .join(models.TUVote) - .filter(models.TUVote.VoteID == voteinfo.ID) + .join(models.Vote) + .filter(models.Vote.VoteID == voteinfo.ID) ) vote = ( - db.query(models.TUVote) + db.query(models.Vote) .filter( and_( - models.TUVote.UserID == request.user.ID, - models.TUVote.VoteID == voteinfo.ID, + models.Vote.UserID == request.user.ID, + models.Vote.VoteID == voteinfo.ID, ) ) .first() @@ -282,7 +278,7 @@ async def package_maintainer_proposal_post( "Invalid 'decision' value.", status_code=HTTPStatus.BAD_REQUEST ) - vote = db.create(models.TUVote, User=request.user, VoteInfo=voteinfo) + vote = db.create(models.Vote, User=request.user, VoteInfo=voteinfo) context["error"] = "You've already voted for this proposal." return render_proposal(request, context, proposal, voteinfo, voters, vote) @@ -342,10 +338,8 @@ async def package_maintainer_addvote_post( utcnow = time.utcnow() voteinfo = ( - db.query(models.TUVoteInfo) - .filter( - and_(models.TUVoteInfo.User == user, models.TUVoteInfo.End > utcnow) - ) + db.query(models.VoteInfo) + .filter(and_(models.VoteInfo.User == user, models.VoteInfo.End > utcnow)) .count() ) if voteinfo: @@ -371,7 +365,7 @@ async def package_maintainer_addvote_post( # Active PM types we filter for. types = {PACKAGE_MAINTAINER_ID, PACKAGE_MAINTAINER_AND_DEV_ID} - # Create a new TUVoteInfo (proposal)! + # Create a new VoteInfo (proposal)! with db.begin(): active_pms = ( db.query(User) @@ -385,13 +379,13 @@ async def package_maintainer_addvote_post( .count() ) voteinfo = db.create( - models.TUVoteInfo, + models.VoteInfo, User=user, Agenda=html.escape(agenda), Submitted=timestamp, End=(timestamp + duration), Quorum=quorum, - ActiveTUs=active_pms, + ActiveUsers=active_pms, Submitter=request.user, ) diff --git a/aurweb/schema.py b/aurweb/schema.py index d01d07c9..683f427d 100644 --- a/aurweb/schema.py +++ b/aurweb/schema.py @@ -526,8 +526,8 @@ PackageRequests = Table( # Vote information -TU_VoteInfo = Table( - "TU_VoteInfo", +VoteInfo = Table( + "VoteInfo", metadata, Column("ID", INTEGER(unsigned=True), primary_key=True), Column("Agenda", Text, nullable=False), @@ -546,7 +546,10 @@ TU_VoteInfo = Table( "Abstain", INTEGER(unsigned=True), nullable=False, server_default=text("'0'") ), Column( - "ActiveTUs", INTEGER(unsigned=True), nullable=False, server_default=text("'0'") + "ActiveUsers", + INTEGER(unsigned=True), + nullable=False, + server_default=text("'0'"), ), mysql_engine="InnoDB", mysql_charset="utf8mb4", @@ -555,10 +558,10 @@ TU_VoteInfo = Table( # Individual vote records -TU_Votes = Table( - "TU_Votes", +Votes = Table( + "Votes", metadata, - Column("VoteID", ForeignKey("TU_VoteInfo.ID", ondelete="CASCADE"), nullable=False), + Column("VoteID", ForeignKey("VoteInfo.ID", ondelete="CASCADE"), nullable=False), Column("UserID", ForeignKey("Users.ID", ondelete="CASCADE"), nullable=False), mysql_engine="InnoDB", ) diff --git a/aurweb/scripts/notify.py b/aurweb/scripts/notify.py index 232df78c..0e548be4 100755 --- a/aurweb/scripts/notify.py +++ b/aurweb/scripts/notify.py @@ -20,7 +20,7 @@ from aurweb.models.package_comment import PackageComment from aurweb.models.package_notification import PackageNotification from aurweb.models.package_request import PackageRequest from aurweb.models.request_type import RequestType -from aurweb.models.tu_vote import TUVote +from aurweb.models.vote import Vote logger = aur_logging.get_logger(__name__) @@ -744,11 +744,11 @@ class RequestCloseNotification(Notification): return headers -class TUVoteReminderNotification(Notification): +class VoteReminderNotification(Notification): def __init__(self, vote_id): self._vote_id = int(vote_id) - subquery = db.query(TUVote.UserID).filter(TUVote.VoteID == vote_id) + subquery = db.query(Vote.UserID).filter(Vote.VoteID == vote_id) query = ( db.query(User) .filter( @@ -799,7 +799,7 @@ def main(): "delete": DeleteNotification, "request-open": RequestOpenNotification, "request-close": RequestCloseNotification, - "tu-vote-reminder": TUVoteReminderNotification, + "vote-reminder": VoteReminderNotification, } with db.begin(): diff --git a/aurweb/scripts/tuvotereminder.py b/aurweb/scripts/votereminder.py similarity index 54% rename from aurweb/scripts/tuvotereminder.py rename to aurweb/scripts/votereminder.py index aa59d911..7d5c0c3b 100755 --- a/aurweb/scripts/tuvotereminder.py +++ b/aurweb/scripts/votereminder.py @@ -4,7 +4,7 @@ from sqlalchemy import and_ import aurweb.config from aurweb import db, time -from aurweb.models import TUVoteInfo +from aurweb.models import VoteInfo from aurweb.scripts import notify notify_cmd = aurweb.config.get("notifications", "notify-cmd") @@ -15,17 +15,17 @@ def main(): now = time.utcnow() - start = aurweb.config.getint("tuvotereminder", "range_start") + start = aurweb.config.getint("votereminder", "range_start") filter_from = now + start - end = aurweb.config.getint("tuvotereminder", "range_end") + end = aurweb.config.getint("votereminder", "range_end") filter_to = now + end - query = db.query(TUVoteInfo.ID).filter( - and_(TUVoteInfo.End >= filter_from, TUVoteInfo.End <= filter_to) + query = db.query(VoteInfo.ID).filter( + and_(VoteInfo.End >= filter_from, VoteInfo.End <= filter_to) ) for voteinfo in query: - notif = notify.TUVoteReminderNotification(voteinfo.ID) + notif = notify.VoteReminderNotification(voteinfo.ID) notif.send() diff --git a/aurweb/testing/__init__.py b/aurweb/testing/__init__.py index 4451eb3a..b9b1d263 100644 --- a/aurweb/testing/__init__.py +++ b/aurweb/testing/__init__.py @@ -51,8 +51,8 @@ def setup_test_db(*args): models.Session.__tablename__, models.SSHPubKey.__tablename__, models.Term.__tablename__, - models.TUVote.__tablename__, - models.TUVoteInfo.__tablename__, + models.Vote.__tablename__, + models.VoteInfo.__tablename__, models.User.__tablename__, ] diff --git a/conf/config.defaults b/conf/config.defaults index 8b0d8787..db885b65 100644 --- a/conf/config.defaults +++ b/conf/config.defaults @@ -158,7 +158,7 @@ commit_url = https://gitlab.archlinux.org/archlinux/aurweb/-/commits/%s ; sed -r "s/^;?(commit_hash) =.*$/\1 = $(git rev-parse HEAD)/" config ;commit_hash = 1234567 -[tuvotereminder] +[votereminder] ; Offsets used to determine when Package Maintainers should be reminded about ; votes that they should make. ; Reminders will be sent out for all votes that a Package Maintainer has not yet diff --git a/doc/maintenance.txt b/doc/maintenance.txt index a828abd4..68766402 100644 --- a/doc/maintenance.txt +++ b/doc/maintenance.txt @@ -62,7 +62,7 @@ computations and clean up the database: the official repositories. It is also used to prevent users from uploading packages that are in the official repositories already. -* aurweb-tuvotereminder sends out reminders to TUs if the voting period for a +* aurweb-votereminder sends out reminders if the voting period for a Package Maintainer proposal ends soon. * aurweb-popupdate is used to recompute the popularity score of packages. @@ -107,7 +107,7 @@ usually scheduled using Cron. The current setup is: 2 */2 * * * poetry run aurweb-aurblup 3 */2 * * * poetry run aurweb-pkgmaint 4 */2 * * * poetry run aurweb-usermaint -5 */12 * * * poetry run aurweb-tuvotereminder +5 */12 * * * poetry run aurweb-votereminder ---- Advanced Administrative Features diff --git a/docker/config/aurweb-cron b/docker/config/aurweb-cron index 21fd35dc..a8e80ad6 100644 --- a/docker/config/aurweb-cron +++ b/docker/config/aurweb-cron @@ -4,4 +4,4 @@ AUR_CONFIG='/aurweb/conf/config' */2 * * * * bash -c 'aurweb-pkgmaint' */2 * * * * bash -c 'aurweb-usermaint' */2 * * * * bash -c 'aurweb-popupdate' -*/12 * * * * bash -c 'aurweb-tuvotereminder' +*/12 * * * * bash -c 'aurweb-votereminder' diff --git a/migrations/versions/7d65d35fae45_rename_tu_tables_columns.py b/migrations/versions/7d65d35fae45_rename_tu_tables_columns.py new file mode 100644 index 00000000..a2501ca4 --- /dev/null +++ b/migrations/versions/7d65d35fae45_rename_tu_tables_columns.py @@ -0,0 +1,47 @@ +"""Rename TU tables/columns + +Revision ID: 7d65d35fae45 +Revises: 6a64dd126029 +Create Date: 2023-09-10 10:21:33.092342 + +""" +from alembic import op +from sqlalchemy.dialects.mysql import INTEGER + +# revision identifiers, used by Alembic. +revision = "7d65d35fae45" +down_revision = "6a64dd126029" +branch_labels = None +depends_on = None + +# TU_VoteInfo -> VoteInfo +# TU_VoteInfo.ActiveTUs -> VoteInfo.ActiveUsers +# TU_Votes -> Votes + + +def upgrade(): + # Tables + op.rename_table("TU_VoteInfo", "VoteInfo") + op.rename_table("TU_Votes", "Votes") + + # Columns + op.alter_column( + "VoteInfo", + "ActiveTUs", + existing_type=INTEGER(unsigned=True), + new_column_name="ActiveUsers", + ) + + +def downgrade(): + # Tables + op.rename_table("VoteInfo", "TU_VoteInfo") + op.rename_table("Votes", "TU_Votes") + + # Columns + op.alter_column( + "TU_VoteInfo", + "ActiveUsers", + existing_type=INTEGER(unsigned=True), + new_column_name="ActiveTUs", + ) diff --git a/pyproject.toml b/pyproject.toml index 359923a0..7fc6bde5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -116,7 +116,7 @@ aurweb-notify = "aurweb.scripts.notify:main" aurweb-pkgmaint = "aurweb.scripts.pkgmaint:main" aurweb-popupdate = "aurweb.scripts.popupdate:main" aurweb-rendercomment = "aurweb.scripts.rendercomment:main" -aurweb-tuvotereminder = "aurweb.scripts.tuvotereminder:main" +aurweb-votereminder = "aurweb.scripts.votereminder:main" aurweb-usermaint = "aurweb.scripts.usermaint:main" aurweb-config = "aurweb.scripts.config:main" aurweb-adduser = "aurweb.scripts.adduser:main" diff --git a/schema/gendummydata.py b/schema/gendummydata.py index 6049d41e..f83de525 100755 --- a/schema/gendummydata.py +++ b/schema/gendummydata.py @@ -357,7 +357,7 @@ for t in range(0, OPEN_PROPOSALS + CLOSE_PROPOSALS): user = user_keys[random.randrange(0, len(user_keys))] suid = packagemaintainers[random.randrange(0, len(packagemaintainers))] s = ( - "INSERT INTO TU_VoteInfo (Agenda, User, Submitted, End," + "INSERT INTO VoteInfo (Agenda, User, Submitted, End," " Quorum, SubmitterID) VALUES ('%s', '%s', %d, %d, 0.0, %d);\n" ) s = s % (genFortune(), user, start, end, suid) diff --git a/templates/partials/package-maintainer/proposal/details.html b/templates/partials/package-maintainer/proposal/details.html index c74a5c5e..24b21262 100644 --- a/templates/partials/package-maintainer/proposal/details.html +++ b/templates/partials/package-maintainer/proposal/details.html @@ -23,7 +23,7 @@