Poll rework (#312)

* poll rework

* forgot to do joinedload on comments

* Fix logic errors with voting, SQL syntax.

Kitchen sink commit from review of poll-rework changes:
  1. Fix seed-db.sql syntax error.
  2. Fix SQL patch file duplication of *submissions* tables rather
     than one set of submissions and one for comments.
  3. Start makeshift SQL patch folder, since this is a large change
     that contributors may wish to apply to their local instances.
  4. Fix checkbox (non-`exclusive`) polls being unable to be
     unchecked. For consistency with `exclusive` polls, they should.
  5. Fix changing the option of an `exclusive` poll when both
     exclusive and non-exclusive options are present in one comment/
     post causing the non-exclusive options to become unchecked.
     (which, by my reading of SQLAlchemy `Query.one_or_none()`
     really could break quite badly in some cases).

* link relationships with their counterparts

* small modification to poll unchecking

Co-authored-by: TLSM <duolsm@outlook.com>
remotes/1693045480750635534/spooky-22
Aevann1 2022-07-02 08:48:04 +02:00 committed by GitHub
parent 671588d41c
commit d0d15c319a
38 changed files with 575 additions and 402 deletions

View File

@ -16,10 +16,12 @@ class AwardRelationship(Base):
kind = Column(String)
awarded_utc = Column(Integer)
user = relationship("User", primaryjoin="AwardRelationship.user_id==User.id", viewonly=True)
post = relationship("Submission", primaryjoin="AwardRelationship.submission_id==Submission.id", viewonly=True)
comment = relationship("Comment", primaryjoin="AwardRelationship.comment_id==Comment.id", viewonly=True)
user = relationship("User", primaryjoin="AwardRelationship.user_id==User.id", back_populates="awards")
post = relationship("Submission", primaryjoin="AwardRelationship.submission_id==Submission.id", back_populates="awards")
comment = relationship("Comment", primaryjoin="AwardRelationship.comment_id==Comment.id", back_populates="awards")
def __repr__(self):
return f"<AwardRelationship(id={self.id})>"
@property
@lazy

View File

@ -29,8 +29,8 @@ class Badge(Base):
url = Column(String)
created_utc = Column(Integer)
user = relationship("User", viewonly=True)
badge = relationship("BadgeDef", primaryjoin="foreign(Badge.badge_id) == remote(BadgeDef.id)", viewonly=True)
user = relationship("User", back_populates="badges")
badge = relationship("BadgeDef", primaryjoin="foreign(Badge.badge_id) == remote(BadgeDef.id)")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs:

View File

@ -19,9 +19,10 @@ class OauthApp(Base):
description = Column(String)
author_id = Column(Integer, ForeignKey("users.id"))
author = relationship("User", viewonly=True)
author = relationship("User")
def __repr__(self): return f"<OauthApp(id={self.id})>"
def __repr__(self):
return f"<OauthApp(id={self.id})>"
@property
@ -36,7 +37,8 @@ class OauthApp(Base):
@property
@lazy
def permalink(self): return f"/admin/app/{self.id}"
def permalink(self):
return f"/admin/app/{self.id}"
@lazy
def idlist(self, page=1):
@ -70,8 +72,11 @@ class ClientAuth(Base):
oauth_client = Column(Integer, ForeignKey("oauth_apps.id"), primary_key=True)
access_token = Column(String)
user = relationship("User", viewonly=True)
application = relationship("OauthApp", viewonly=True)
user = relationship("User")
application = relationship("OauthApp")
def __repr__(self):
return f"<ClientAuth(user_id={self.user_id}, oauth_client={self.oauth_client})>"
@property
@lazy

View File

@ -76,14 +76,15 @@ class Comment(Base):
wordle_result = Column(String)
treasure_amount = Column(String)
oauth_app = relationship("OauthApp", viewonly=True)
post = relationship("Submission", viewonly=True)
oauth_app = relationship("OauthApp")
post = relationship("Submission", back_populates="comments")
author = relationship("User", primaryjoin="User.id==Comment.author_id")
senttouser = relationship("User", primaryjoin="User.id==Comment.sentto", viewonly=True)
parent_comment = relationship("Comment", remote_side=[id], viewonly=True)
child_comments = relationship("Comment", lazy="dynamic", remote_side=[parent_comment_id], viewonly=True)
awards = relationship("AwardRelationship", order_by="AwardRelationship.awarded_utc.desc()", viewonly=True)
flags = relationship("CommentFlag", order_by="CommentFlag.created_utc", viewonly=True)
senttouser = relationship("User", primaryjoin="User.id==Comment.sentto")
parent_comment = relationship("Comment", remote_side=[id], back_populates="child_comments")
child_comments = relationship("Comment", remote_side=[parent_comment_id], back_populates="parent_comment")
awards = relationship("AwardRelationship", order_by="AwardRelationship.awarded_utc.desc()", back_populates="comment")
flags = relationship("CommentFlag", order_by="CommentFlag.created_utc")
options = relationship("CommentOption", order_by="CommentOption.id")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs:
@ -99,35 +100,6 @@ class Comment(Base):
def top_comment(self):
return g.db.get(Comment, self.top_comment_id)
@lazy
def poll_voted(self, v):
if v:
vote = g.db.query(CommentVote.vote_type).filter_by(user_id=v.id, comment_id=self.id).one_or_none()
if vote: return vote[0]
return None
@property
@lazy
def options(self):
return self.child_comments.filter_by(author_id=AUTOPOLLER_ID).order_by(Comment.id).all()
@property
@lazy
def choices(self):
return self.child_comments.filter_by(author_id=AUTOCHOICE_ID).order_by(Comment.id).all()
@lazy
def total_poll_voted(self, v):
if v:
for option in self.options:
if option.poll_voted(v): return True
return False
@lazy
def total_choice_voted(self, v):
if v:
return g.db.query(CommentVote).filter(CommentVote.user_id == v.id, CommentVote.comment_id.in_([x.id for x in self.choices])).first()
return None
@property
@lazy
@ -239,8 +211,7 @@ class Comment(Base):
if not self.parent_submission:
return [x for x in self.child_comments.order_by(Comment.id) if not x.author.shadowbanned]
comments = self.child_comments.filter(Comment.author_id.notin_(poll_bots))
comments = sort_comments(sort, comments)
comments = self.child_comments
return [x for x in comments if not x.author.shadowbanned]
@ -250,8 +221,7 @@ class Comment(Base):
if not self.parent_submission:
return self.child_comments.order_by(Comment.id).all()
comments = self.child_comments.filter(Comment.author_id.notin_(poll_bots))
return sort_comments(sort, comments).all()
return self.child_comments
@property
@ -405,27 +375,25 @@ class Comment(Base):
self.upvotes += amount
g.db.add(self)
for c in self.options:
body += f'<div class="custom-control"><input type="checkbox" class="custom-control-input" id="{c.id}" name="option"'
if c.poll_voted(v): body += " checked"
if v: body += f''' onchange="poll_vote('{c.id}', '{self.id}')"'''
else: body += f''' onchange="poll_vote_no_v('{c.id}', '{self.id}')"'''
body += f'''><label class="custom-control-label" for="{c.id}">{c.body_html}<span class="presult-{self.id}'''
if not self.total_poll_voted(v): body += ' d-none'
body += f'"> - <a href="/votes?link=t3_{c.id}"><span id="poll-{c.id}">{c.upvotes}</span> votes</a></span></label></div>'
if self.choices:
curr = self.total_choice_voted(v)
if curr: curr = " value=" + str(curr.comment_id)
if self.options:
curr = [x for x in self.options if x.exclusive and x.voted(v)]
if curr: curr = " value=" + str(curr[0].id)
else: curr = ''
body += f'<input class="d-none" id="current-{self.id}"{curr}>'
for c in self.choices:
body += f'''<div class="custom-control"><input name="choice-{self.id}" autocomplete="off" class="custom-control-input" type="radio" id="{c.id}" onchange="choice_vote('{c.id}','{self.id}')"'''
if c.poll_voted(v): body += " checked "
body += f'''><label class="custom-control-label" for="{c.id}">{c.body_html}<span class="presult-{self.id}'''
if not self.total_choice_voted(v): body += ' d-none'
body += f'"> - <a href="/votes?link=t3_{c.id}"><span id="choice-{c.id}">{c.upvotes}</span> votes</a></span></label></div>'
for c in self.options:
if c.exclusive:
body += f'''<div class="custom-control"><input name="choice-{self.id}" autocomplete="off" class="custom-control-input" type="radio" id="{c.id}" onchange="choice_vote('{c.id}','{self.id}','comment')"'''
if c.voted(v): body += " checked "
body += f'''><label class="custom-control-label" for="{c.id}">{c.body_html}<span class="presult-{self.id}'''
body += f'"> - <a href="/votes/comment/option/{c.id}"><span id="choice-{c.id}">{c.upvotes}</span> votes</a></span></label></div>'
else:
body += f'<div class="custom-control"><input type="checkbox" class="custom-control-input" id="{c.id}" name="option"'
if c.voted(v): body += " checked"
if v: body += f''' onchange="poll_vote('{c.id}', 'comment')"'''
else: body += f''' onchange="poll_vote_no_v('{c.id}', '{self.id}')"'''
body += f'''><label class="custom-control-label" for="{c.id}">{c.body_html}<span class="presult-{self.id}'''
body += f'"> - <a href="/votes/comment/option/{c.id}"><span id="poll-{c.id}">{c.upvotes}</span> votes</a></span></label></div>'
if self.author.sig_html and (self.author_id == MOOSE_ID or (not self.ghost and not (v and (v.sigs_disabled or v.poor)))):
body += f"<hr>{self.author.sig_html}"

View File

@ -5,4 +5,7 @@ class BannedDomain(Base):
__tablename__ = "banneddomains"
domain = Column(String, primary_key=True)
reason = Column(String)
reason = Column(String)
def __repr__(self):
return f"<BannedDomain(domain={self.domain})>"

View File

@ -9,7 +9,7 @@ class Exile(Base):
sub = Column(String, ForeignKey("subs.name"), primary_key=True)
exiler_id = Column(Integer, ForeignKey("users.id"))
exiler = relationship("User", primaryjoin="User.id==Exile.exiler_id", viewonly=True)
exiler = relationship("User", primaryjoin="User.id==Exile.exiler_id")
def __repr__(self):
return f"<Exile(user_id={self.user_id}, sub={self.sub})>"

View File

@ -15,7 +15,7 @@ class Flag(Base):
reason = Column(String)
created_utc = Column(Integer)
user = relationship("User", primaryjoin = "Flag.user_id == User.id", uselist = False, viewonly=True)
user = relationship("User", primaryjoin = "Flag.user_id == User.id", uselist = False)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -48,7 +48,7 @@ class CommentFlag(Base):
reason = Column(String)
created_utc = Column(Integer)
user = relationship("User", primaryjoin = "CommentFlag.user_id == User.id", uselist = False, viewonly=True)
user = relationship("User", primaryjoin = "CommentFlag.user_id == User.id", uselist = False)
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -9,8 +9,8 @@ class Follow(Base):
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
created_utc = Column(Integer)
user = relationship("User", uselist=False, primaryjoin="User.id==Follow.user_id", viewonly=True)
target = relationship("User", primaryjoin="User.id==Follow.target_id", viewonly=True)
user = relationship("User", uselist=False, primaryjoin="User.id==Follow.user_id", back_populates="following")
target = relationship("User", uselist=False, primaryjoin="User.id==Follow.target_id", back_populates="followers")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -15,6 +15,9 @@ class Lottery(Base):
tickets_sold = Column(Integer, default=0)
winner_id = Column(Integer, ForeignKey("users.id"))
def __repr__(self):
return f"<Lottery(id={self.id})>"
@property
@lazy
def timeleft(self):

View File

@ -18,9 +18,9 @@ class ModAction(Base):
_note=Column(String)
created_utc = Column(Integer)
user = relationship("User", primaryjoin="User.id==ModAction.user_id", viewonly=True)
target_user = relationship("User", primaryjoin="User.id==ModAction.target_user_id", viewonly=True)
target_post = relationship("Submission", viewonly=True)
user = relationship("User", primaryjoin="User.id==ModAction.user_id")
target_user = relationship("User", primaryjoin="User.id==ModAction.target_user_id")
target_post = relationship("Submission")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -12,8 +12,8 @@ class Notification(Base):
read = Column(Boolean, default=False)
created_utc = Column(Integer)
comment = relationship("Comment", viewonly=True)
user = relationship("User", viewonly=True)
comment = relationship("Comment")
user = relationship("User")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -0,0 +1,103 @@
from sqlalchemy import *
from sqlalchemy.orm import relationship
from files.__main__ import Base
from files.helpers.lazy import lazy
import time
class SubmissionOption(Base):
__tablename__ = "submission_options"
id = Column(Integer, primary_key=True)
submission_id = Column(Integer, ForeignKey("submissions.id"))
body_html = Column(Text)
exclusive = Column(Boolean)
votes = relationship("SubmissionOptionVote")
post = relationship("Submission", back_populates="options")
def __repr__(self):
return f"<SubmissionOption(id={self.id})>"
@property
@lazy
def upvotes(self):
return len(self.votes)
@lazy
def voted(self, v):
return v.id in [x.user_id for x in self.votes]
class SubmissionOptionVote(Base):
__tablename__ = "submission_option_votes"
option_id = Column(Integer, ForeignKey("submission_options.id"), primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
created_utc = Column(Integer)
submission_id = Column(Integer, ForeignKey("submissions.id"))
user = relationship("User")
option = relationship("SubmissionOption")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<SubmissionOptionVote(option_id={self.option_id}, user_id={self.user_id})>"
@property
def created_datetime(self):
return str(time.strftime("%d/%B/%Y %H:%M:%S UTC", time.gmtime(self.created_utc)))
class CommentOption(Base):
__tablename__ = "comment_options"
id = Column(Integer, primary_key=True)
comment_id = Column(Integer, ForeignKey("comments.id"))
body_html = Column(Text)
exclusive = Column(Boolean)
votes = relationship("CommentOptionVote")
post = relationship("Comment", back_populates="options")
def __repr__(self):
return f"<CommentOption(id={self.id})>"
@property
@lazy
def upvotes(self):
return len(self.votes)
@lazy
def voted(self, v):
return v.id in [x.user_id for x in self.votes]
class CommentOptionVote(Base):
__tablename__ = "comment_option_votes"
option_id = Column(Integer, ForeignKey("comment_options.id"), primary_key=True)
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
created_utc = Column(Integer)
comment_id = Column(Integer, ForeignKey("comments.id"))
user = relationship("User")
option = relationship("CommentOption")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
super().__init__(*args, **kwargs)
def __repr__(self):
return f"<CommentOptionVote(option_id={self.option_id}, user_id={self.user_id})>"
@property
def created_datetime(self):
return str(time.strftime("%d/%B/%Y %H:%M:%S UTC", time.gmtime(self.created_utc)))

View File

@ -10,6 +10,8 @@ class SaveRelationship(Base):
user_id=Column(Integer, ForeignKey("users.id"), primary_key=True)
submission_id=Column(Integer, ForeignKey("submissions.id"), primary_key=True)
def __repr__(self):
return f"<SaveRelationship(user_id={self.user_id}, submission_id={self.submission_id})>"
class CommentSaveRelationship(Base):
@ -17,4 +19,7 @@ class CommentSaveRelationship(Base):
__tablename__="comment_save_relationship"
user_id=Column(Integer, ForeignKey("users.id"), primary_key=True)
comment_id=Column(Integer, ForeignKey("comments.id"), primary_key=True)
comment_id=Column(Integer, ForeignKey("comments.id"), primary_key=True)
def __repr__(self):
return f"<CommentSaveRelationship(user_id={self.user_id}, comment_id={self.comment_id})>"

View File

@ -19,10 +19,8 @@ class Sub(Base):
bannerurl = Column(String)
css = Column(String)
blocks = relationship("SubBlock",
primaryjoin="SubBlock.sub==Sub.name", viewonly=True)
followers = relationship("SubSubscription",
primaryjoin="SubSubscription.sub==Sub.name", viewonly=True)
blocks = relationship("SubBlock", primaryjoin="SubBlock.sub==Sub.name")
followers = relationship("SubSubscription", primaryjoin="SubSubscription.sub==Sub.name")
def __repr__(self):
return f"<Sub(name={self.name})>"

View File

@ -16,6 +16,7 @@ from .saves import SaveRelationship
from .sub import *
from .subscriptions import *
from .votes import CommentVote
from .polls import *
from flask import g
def sort_posts(sort, posts):
@ -72,12 +73,13 @@ class Submission(Base):
new = Column(Boolean)
author = relationship("User", primaryjoin="Submission.author_id==User.id")
oauth_app = relationship("OauthApp", viewonly=True)
approved_by = relationship("User", uselist=False, primaryjoin="Submission.is_approved==User.id", viewonly=True)
awards = relationship("AwardRelationship", order_by="AwardRelationship.awarded_utc.desc()", viewonly=True)
flags = relationship("Flag", order_by="Flag.created_utc", viewonly=True)
comments = relationship("Comment", primaryjoin="Comment.parent_submission==Submission.id")
subr = relationship("Sub", primaryjoin="foreign(Submission.sub)==remote(Sub.name)", viewonly=True)
oauth_app = relationship("OauthApp")
approved_by = relationship("User", uselist=False, primaryjoin="Submission.is_approved==User.id")
awards = relationship("AwardRelationship", order_by="AwardRelationship.awarded_utc.desc()", back_populates="post")
flags = relationship("Flag", order_by="Flag.created_utc")
comments = relationship("Comment", primaryjoin="Comment.parent_submission==Submission.id", back_populates="post")
subr = relationship("Sub", primaryjoin="foreign(Submission.sub)==remote(Sub.name)")
options = relationship("SubmissionOption", order_by="SubmissionOption.id")
bump_utc = deferred(Column(Integer, server_default=FetchedValue()))
@ -94,43 +96,6 @@ class Submission(Base):
if self.downvotes > 5 and 0.25 < self.upvotes / self.downvotes < 4: return True
return False
@property
@lazy
def options(self):
return g.db.query(Comment).filter_by(parent_submission = self.id, author_id = AUTOPOLLER_ID, level=1).order_by(Comment.id).all()
@property
@lazy
def choices(self):
return g.db.query(Comment).filter_by(parent_submission = self.id, author_id = AUTOCHOICE_ID, level=1).order_by(Comment.id).all()
@property
@lazy
def bet_options(self):
return g.db.query(Comment).filter_by(parent_submission = self.id, author_id = AUTOBETTER_ID, level=1).all()
@lazy
def total_poll_voted(self, v):
if v:
for option in self.options:
if option.poll_voted(v): return True
return False
@lazy
def total_choice_voted(self, v):
if v and self.choices:
return g.db.query(CommentVote).filter(CommentVote.user_id == v.id, CommentVote.comment_id.in_([x.id for x in self.choices])).first()
return None
@lazy
def total_bet_voted(self, v):
if "closed" in self.body.lower(): return True
if v:
for option in self.bet_options:
if option.poll_voted(v): return True
return False
@property
@lazy
@ -401,44 +366,31 @@ class Submission(Base):
self.upvotes += amount
g.db.add(self)
if not listing:
for c in self.options:
if self.options:
curr = [x for x in self.options if x.exclusive and x.voted(v)]
if curr: curr = " value=" + str(curr[0].id)
else: curr = ''
body += f'<input class="d-none" id="current-{self.id}"{curr}>'
for c in self.options:
if c.exclusive:
body += f'''<div class="custom-control"><input name="choice-{self.id}" autocomplete="off" class="custom-control-input" type="radio" id="{c.id}" onchange="choice_vote('{c.id}','{self.id}','post')"'''
if c.voted(v): body += " checked "
body += f'''><label class="custom-control-label" for="{c.id}">{c.body_html}<span class="presult-{self.id}'''
body += f'"> - <a href="/votes/post/option/{c.id}"><span id="choice-{c.id}">{c.upvotes}</span> votes</a></span></label></div>'
else:
body += f'<div class="custom-control"><input type="checkbox" class="custom-control-input" id="{c.id}" name="option"'
if c.poll_voted(v): body += " checked"
if v: body += f''' onchange="poll_vote('{c.id}', '{self.id}')"'''
if c.voted(v): body += " checked"
if v: body += f''' onchange="poll_vote('{c.id}', 'post')"'''
else: body += f''' onchange="poll_vote_no_v('{c.id}', '{self.id}')"'''
body += f'''><label class="custom-control-label" for="{c.id}">{c.body_html}<span class="presult-{self.id}'''
if not self.total_poll_voted(v): body += ' d-none'
body += f'"> - <a href="/votes?link=t3_{c.id}"><span id="poll-{c.id}">{c.upvotes}</span> votes</a></span></label></div>'
if self.choices:
curr = self.total_choice_voted(v)
if curr: curr = " value=" + str(curr.comment_id)
else: curr = ''
body += f'<input class="d-none" id="current-{self.id}"{curr}>'
for c in self.choices:
body += f'''<div class="custom-control"><input name="choice-{self.id}" autocomplete="off" class="custom-control-input" type="radio" id="{c.id}" onchange="choice_vote('{c.id}','{self.id}')"'''
if c.poll_voted(v): body += " checked "
body += f'''><label class="custom-control-label" for="{c.id}">{c.body_html}<span class="presult-{self.id}'''
if not self.total_choice_voted(v): body += ' d-none'
body += f'"> - <a href="/votes?link=t3_{c.id}"><span id="choice-{c.id}">{c.upvotes}</span> votes</a></span></label></div>'
for c in self.bet_options:
body += f'''<div class="custom-control mt-3"><input autocomplete="off" class="custom-control-input bet" type="radio" id="{c.id}" onchange="bet_vote('{c.id}')"'''
if c.poll_voted(v): body += " checked "
if not (v and v.coins >= 200) or self.total_bet_voted(v) or not v.can_gamble: body += " disabled "
body += f'''><label class="custom-control-label" for="{c.id}">{c.body_html} - <a href="/votes?link=t3_{c.id}"><span id="bet-{c.id}">{c.upvotes}</span> bets</a>'''
if not self.total_bet_voted(v):
body += '''<span class="cost"> (cost of entry: 200 coins)</span>'''
body += "</label>"
if v and v.admin_level > 2:
body += f'''<button class="btn btn-primary px-2 mx-2" style="font-size:10px;padding:2px" onclick="post_toast(this,'/distribute/{c.id}')">Declare winner</button>'''
body += "</div>"
body += f'"> - <a href="/votes/post/option/{c.id}"><span id="poll-{c.id}">{c.upvotes}</span> votes</a></span></label></div>'
if self.author.sig_html and (self.author_id == MOOSE_ID or (not self.ghost and not (v and (v.sigs_disabled or v.poor)))):
body += f"<hr>{self.author.sig_html}"
if not listing and self.author.sig_html and (self.author_id == MOOSE_ID or (not self.ghost and not (v and (v.sigs_disabled or v.poor)))):
body += f"<hr>{self.author.sig_html}"
return body

View File

@ -7,7 +7,7 @@ class Subscription(Base):
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
submission_id = Column(Integer, ForeignKey("submissions.id"), primary_key=True)
user = relationship("User", uselist=False, viewonly=True)
user = relationship("User", uselist=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

View File

@ -135,16 +135,16 @@ class User(Base):
total_held_lottery_tickets = Column(Integer, default=0)
total_lottery_winnings = Column(Integer, default=0)
badges = relationship("Badge", order_by="Badge.created_utc", viewonly=True)
subscriptions = relationship("Subscription", viewonly=True)
following = relationship("Follow", primaryjoin="Follow.user_id==User.id", viewonly=True)
followers = relationship("Follow", primaryjoin="Follow.target_id==User.id", viewonly=True)
viewers = relationship("ViewerRelationship", primaryjoin="User.id == ViewerRelationship.user_id", viewonly=True)
blocking = relationship("UserBlock", lazy="dynamic", primaryjoin="User.id==UserBlock.user_id", viewonly=True)
blocked = relationship("UserBlock", lazy="dynamic", primaryjoin="User.id==UserBlock.target_id", viewonly=True)
authorizations = relationship("ClientAuth", viewonly=True)
awards = relationship("AwardRelationship", primaryjoin="User.id==AwardRelationship.user_id", viewonly=True)
referrals = relationship("User", viewonly=True)
badges = relationship("Badge", order_by="Badge.created_utc", back_populates="user")
subscriptions = relationship("Subscription", back_populates="user")
following = relationship("Follow", primaryjoin="Follow.user_id==User.id", back_populates="user")
followers = relationship("Follow", primaryjoin="Follow.target_id==User.id", back_populates="target")
viewers = relationship("ViewerRelationship", primaryjoin="User.id == ViewerRelationship.user_id")
blocking = relationship("UserBlock", lazy="dynamic", primaryjoin="User.id==UserBlock.user_id", back_populates="user")
blocked = relationship("UserBlock", lazy="dynamic", primaryjoin="User.id==UserBlock.target_id", back_populates="target")
authorizations = relationship("ClientAuth", back_populates="user")
awards = relationship("AwardRelationship", primaryjoin="User.id==AwardRelationship.user_id", back_populates="user")
referrals = relationship("User")
def __init__(self, **kwargs):
@ -157,6 +157,10 @@ class User(Base):
super().__init__(**kwargs)
def __repr__(self):
return f"<User(id={self.id})>"
@lazy
def mods(self, sub):
return self.admin_level == 3 or bool(g.db.query(Mod.user_id).filter_by(user_id=self.id, sub=sub).one_or_none())

View File

@ -8,8 +8,8 @@ class UserBlock(Base):
user_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
target_id = Column(Integer, ForeignKey("users.id"), primary_key=True)
user = relationship("User", primaryjoin="User.id==UserBlock.user_id", viewonly=True)
target = relationship("User", primaryjoin="User.id==UserBlock.target_id", viewonly=True)
user = relationship("User", primaryjoin="User.id==UserBlock.user_id", back_populates="blocking")
target = relationship("User", primaryjoin="User.id==UserBlock.target_id", back_populates="blocked")
def __repr__(self):
return f"<UserBlock(user={self.user_id}, target={self.target_id})>"

View File

@ -12,7 +12,7 @@ class ViewerRelationship(Base):
viewer_id = Column(Integer, ForeignKey('users.id'), primary_key=True)
last_view_utc = Column(Integer)
viewer = relationship("User", primaryjoin="ViewerRelationship.viewer_id == User.id", viewonly=True)
viewer = relationship("User", primaryjoin="ViewerRelationship.viewer_id == User.id")
def __init__(self, **kwargs):
@ -21,6 +21,9 @@ class ViewerRelationship(Base):
super().__init__(**kwargs)
def __repr__(self):
return f"<ViewerRelationship(user_id={self.user_id}, viewer_id={self.viewer_id})>"
@property
@lazy
def last_view_since(self):

View File

@ -16,8 +16,8 @@ class Vote(Base):
real = Column(Boolean, default=True)
created_utc = Column(Integer)
user = relationship("User", lazy="subquery", viewonly=True)
post = relationship("Submission", lazy="subquery", viewonly=True)
user = relationship("User", lazy="subquery")
post = relationship("Submission", lazy="subquery")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())
@ -62,7 +62,7 @@ class CommentVote(Base):
created_utc = Column(Integer)
user = relationship("User", lazy="subquery")
comment = relationship("Comment", lazy="subquery", viewonly=True)
comment = relationship("Comment", lazy="subquery")
def __init__(self, *args, **kwargs):
if "created_utc" not in kwargs: kwargs["created_utc"] = int(time.time())

View File

@ -155,9 +155,6 @@ AUTOJANNY_ID = 2
SNAPPY_ID = 3
LONGPOSTBOT_ID = 4
ZOZBOT_ID = 5
AUTOPOLLER_ID = 6
AUTOBETTER_ID = 7
AUTOCHOICE_ID = 8
BASEDBOT_ID = 0
SCHIZO_ID = 0
@ -203,9 +200,6 @@ if SITE in {'rdrama.net', 'devrama.xyz'}:
SNAPPY_ID = 261
LONGPOSTBOT_ID = 1832
ZOZBOT_ID = 1833
AUTOPOLLER_ID = 6176
AUTOBETTER_ID = 7668
AUTOCHOICE_ID = 9167
SCHIZO_ID = 8494
A_ID = 1230
@ -254,9 +248,6 @@ elif SITE == 'pcmemes.net':
SNAPPY_ID = 261
LONGPOSTBOT_ID = 1832
ZOZBOT_ID = 1833
AUTOPOLLER_ID = 2129
AUTOBETTER_ID = 1867
AUTOCHOICE_ID = 2072
BASEDBOT_ID = 800
KIPPY_ID = 1592
@ -313,9 +304,7 @@ elif SITE == 'lgbdropthet.com':
else: # localhost or testing environment implied
pass
poll_bots = AUTOPOLLER_ID, AUTOBETTER_ID, AUTOCHOICE_ID
bots = {NOTIFICATIONS_ID, AUTOJANNY_ID, SNAPPY_ID, LONGPOSTBOT_ID, ZOZBOT_ID, BASEDBOT_ID}.union(poll_bots)
bots = {NOTIFICATIONS_ID, AUTOJANNY_ID, SNAPPY_ID, LONGPOSTBOT_ID, ZOZBOT_ID, BASEDBOT_ID}
IMGUR_KEY = environ.get("IMGUR_KEY").strip()
PUSHER_ID = environ.get("PUSHER_ID", "").strip()

View File

@ -144,7 +144,13 @@ def get_post(i, v=None, graceful=False, rendered=False):
)
if rendered:
posts = post.options(joinedload(Submission.flags), joinedload(Submission.awards), joinedload(Submission.author))
posts = post.options(
joinedload(Submission.flags),
joinedload(Submission.awards),
joinedload(Submission.author),
joinedload(Submission.options),
joinedload(Submission.options, SubmissionOption.votes)
)
post=post.one_or_none()
@ -196,7 +202,12 @@ def get_posts(pids, v=None):
blocked,
blocked.c.user_id == Submission.author_id,
isouter=True
).options(joinedload(Submission.flags), joinedload(Submission.awards), joinedload(Submission.author)).all()
).options(joinedload(Submission.flags),
joinedload(Submission.awards),
joinedload(Submission.author),
joinedload(Submission.options),
joinedload(Submission.options, SubmissionOption.votes)
).all()
output = [p[0] for p in query]
for i in range(len(output)):

View File

@ -16,4 +16,5 @@ from .feeds import *
from .awards import *
from .giphy import *
from .subs import *
from .lottery import *
from .lottery import *
from .polls import *

View File

@ -101,8 +101,7 @@ def post_pid_comment_cid(cid, pid=None, anything=None, v=None, sub=None):
comments = comments.join(User, User.id == Comment.author_id).filter(User.shadowbanned == None)
comments=comments.filter(
Comment.top_comment_id == c.top_comment_id,
Comment.author_id.notin_(poll_bots)
Comment.top_comment_id == c.top_comment_id
).join(
votes,
votes.c.comment_id == Comment.id,
@ -365,30 +364,20 @@ def api_comment(v):
else: c.top_comment_id = parent.top_comment_id
for option in options:
c_option = Comment(author_id=AUTOPOLLER_ID,
parent_submission=parent_submission,
parent_comment_id=c.id,
level=level+1,
option = CommentOption(
comment_id=c.id,
body_html=filter_emojis_only(option),
upvotes=0,
is_bot=True,
ghost=c.ghost
)
g.db.add(c_option)
exclusive=False
)
g.db.add(option)
for choice in choices:
c_choice = Comment(author_id=AUTOCHOICE_ID,
parent_submission=parent_submission,
parent_comment_id=c.id,
level=level+1,
choice = CommentOption(
comment_id=c.id,
body_html=filter_emojis_only(choice),
upvotes=0,
is_bot=True,
ghost=c.ghost
)
g.db.add(c_choice)
exclusive=True
)
g.db.add(choice)
if request.host == 'pcmemes.net' and c.body.lower().startswith("based"):
pill = based_regex.match(body)
@ -664,29 +653,21 @@ def edit_comment(cid, v):
for i in poll_regex.finditer(body):
body = body.replace(i.group(0), "")
c_option = Comment(author_id=AUTOPOLLER_ID,
parent_submission=c.parent_submission,
parent_comment_id=c.id,
level=c.level+1,
option = CommentOption(
comment_id=c.id,
body_html=filter_emojis_only(i.group(1)),
upvotes=0,
is_bot=True,
ghost=c.ghost
)
g.db.add(c_option)
exclusive = False
)
g.db.add(option)
for i in choice_regex.finditer(body):
body = body.replace(i.group(0), "")
c_choice = Comment(author_id=AUTOCHOICE_ID,
parent_submission=c.parent_submission,
parent_comment_id=c.id,
level=c.level+1,
option = CommentOption(
comment_id=c.id,
body_html=filter_emojis_only(i.group(1)),
upvotes=0,
is_bot=True,
ghost=c.ghost
)
g.db.add(c_choice)
exclusive = True
)
g.db.add(option)
if '!slots' not in body.lower() and '!blackjack' not in body.lower() and '!wordle' not in body.lower() and AGENDAPOSTER_PHRASE not in body.lower():
now = int(time.time())

View File

@ -463,9 +463,7 @@ def all_comments(v):
@cache.memoize(timeout=86400)
def comment_idlist(page=1, v=None, nsfw=False, sort="new", t="all", gt=0, lt=0, site=None):
excluded = v.userblocks + [AUTOPOLLER_ID, AUTOBETTER_ID, AUTOCHOICE_ID]
comments = g.db.query(Comment.id).filter(Comment.parent_submission != None, Comment.author_id.notin_(excluded))
comments = g.db.query(Comment.id).filter(Comment.parent_submission != None, Comment.author_id.notin_(v.userblocks))
if v.admin_level < 2:
private = [x[0] for x in g.db.query(Submission.id).filter(Submission.private == True).all()]

View File

@ -313,7 +313,7 @@ def sign_up_post(v):
ref_id = int(request.values.get("referred_by", 0))
users_count = g.db.query(User).count()
if users_count == 8:
if users_count == 5:
admin_level=3
session["history"] = []
else: admin_level=0

View File

@ -0,0 +1,109 @@
from files.helpers.wrappers import *
from files.helpers.get import *
from files.helpers.const import *
from files.classes import *
from flask import *
from files.__main__ import app
@app.post("/vote/post/option/<option_id>")
@is_not_permabanned
def api_vote_option(option_id, v):
option_id = int(option_id)
option = g.db.get(SubmissionOption, option_id)
if not option: abort(404)
if option.exclusive:
vote = g.db.query(SubmissionOptionVote).join(SubmissionOption).filter(
SubmissionOptionVote.user_id==v.id,
SubmissionOptionVote.submission_id==option.submission_id,
SubmissionOption.exclusive==True).one_or_none()
if vote:
g.db.delete(vote)
existing = g.db.query(SubmissionOptionVote).filter_by(option_id=option_id, user_id=v.id).one_or_none()
if not existing:
vote = SubmissionOptionVote(
option_id=option_id,
user_id=v.id,
submission_id=option.submission_id,
)
g.db.add(vote)
elif existing:
g.db.delete(existing)
return "", 204
@app.get("/votes/post/option/<option_id>")
@auth_required
def option_votes(option_id, v):
option_id = int(option_id)
option = g.db.get(SubmissionOption, option_id)
if not option: abort(404)
ups = g.db.query(SubmissionOptionVote).filter_by(option_id=option_id).options(
joinedload(SubmissionOptionVote.user)
).all()
return render_template("poll_votes.html",
v=v,
thing=option,
ups=ups)
@app.post("/vote/comment/option/<option_id>")
@is_not_permabanned
def api_vote_option_comment(option_id, v):
option_id = int(option_id)
option = g.db.get(CommentOption, option_id)
if not option: abort(404)
if option.exclusive:
vote = g.db.query(CommentOptionVote).join(CommentOption).filter(
CommentOptionVote.user_id==v.id,
CommentOptionVote.comment_id==option.comment_id,
CommentOption.exclusive==True).one_or_none()
if vote:
g.db.delete(vote)
existing = g.db.query(CommentOptionVote).filter_by(option_id=option_id, user_id=v.id).one_or_none()
if not existing:
vote = CommentOptionVote(
option_id=option_id,
user_id=v.id,
comment_id=option.comment_id,
)
g.db.add(vote)
elif existing:
g.db.delete(existing)
return "", 204
@app.get("/votes/comment/option/<option_id>")
@auth_required
def option_votes_comment(option_id, v):
option_id = int(option_id)
option = g.db.get(CommentOption, option_id)
if not option: abort(404)
ups = g.db.query(CommentOptionVote).filter_by(option_id=option_id).options(
joinedload(CommentOptionVote.user)
).all()
return render_template("poll_votes.html",
v=v,
thing=option,
ups=ups)

View File

@ -158,7 +158,7 @@ def post_id(pid, anything=None, v=None, sub=None):
if not (v and v.shadowbanned) and not (v and v.admin_level >= 2):
comments = comments.join(User, User.id == Comment.author_id).filter(User.shadowbanned == None)
comments=comments.filter(Comment.parent_submission == post.id, Comment.author_id.notin_(poll_bots)).join(
comments=comments.filter(Comment.parent_submission == post.id).join(
votes,
votes.c.comment_id == Comment.id,
isouter=True
@ -170,7 +170,13 @@ def post_id(pid, anything=None, v=None, sub=None):
blocked,
blocked.c.user_id == Comment.author_id,
isouter=True
).options(joinedload(Comment.flags), joinedload(Comment.awards), joinedload(Comment.author))
).options(
joinedload(Comment.flags),
joinedload(Comment.awards),
joinedload(Comment.author),
joinedload(Comment.options),
joinedload(Comment.options, CommentOption.votes)
)
output = []
for c in comments.all():
@ -192,7 +198,7 @@ def post_id(pid, anything=None, v=None, sub=None):
else:
pinned = g.db.query(Comment).filter(Comment.parent_submission == post.id, Comment.stickied != None).all()
comments = g.db.query(Comment).join(User, User.id == Comment.author_id).filter(User.shadowbanned == None, Comment.parent_submission == post.id, Comment.author_id.notin_(poll_bots), Comment.level == 1, Comment.stickied == None)
comments = g.db.query(Comment).join(User, User.id == Comment.author_id).filter(User.shadowbanned == None, Comment.parent_submission == post.id, Comment.level == 1, Comment.stickied == None)
comments = sort_comments(sort, comments)
@ -268,7 +274,7 @@ def viewmore(v, pid, sort, offset):
votes.c.vote_type,
blocking.c.target_id,
blocked.c.target_id,
).filter(Comment.parent_submission == pid, Comment.author_id.notin_(poll_bots), Comment.stickied == None, Comment.id.notin_(ids))
).filter(Comment.parent_submission == pid, Comment.stickied == None, Comment.id.notin_(ids))
if not (v and v.shadowbanned) and not (v and v.admin_level >= 2):
comments = comments.join(User, User.id == Comment.author_id).filter(User.shadowbanned == None)
@ -303,7 +309,7 @@ def viewmore(v, pid, sort, offset):
second = [c[0] for c in comments.filter(or_(Comment.slots_result != None, Comment.blackjack_result != None, Comment.wordle_result != None), func.length(Comment.body_html) <= 100).all()]
comments = first + second
else:
comments = g.db.query(Comment).join(User, User.id == Comment.author_id).filter(User.shadowbanned == None, Comment.parent_submission == pid, Comment.author_id.notin_(poll_bots), Comment.level == 1, Comment.stickied == None, Comment.id.notin_(ids))
comments = g.db.query(Comment).join(User, User.id == Comment.author_id).filter(User.shadowbanned == None, Comment.parent_submission == pid, Comment.level == 1, Comment.stickied == None, Comment.id.notin_(ids))
comments = sort_comments(sort, comments)
@ -427,27 +433,22 @@ def edit_post(pid, v):
for i in poll_regex.finditer(body):
body = body.replace(i.group(0), "")
c = Comment(author_id=AUTOPOLLER_ID,
parent_submission=p.id,
level=1,
option = SubmissionOption(
submission_id=p.id,
body_html=filter_emojis_only(i.group(1)),
upvotes=0,
is_bot=True,
ghost=p.ghost
)
g.db.add(c)
exclusive = False
)
g.db.add(option)
for i in choice_regex.finditer(body):
body = body.replace(i.group(0), "")
c = Comment(author_id=AUTOCHOICE_ID,
parent_submission=p.id,
level=1,
option = SubmissionOption(
submission_id=p.id,
body_html=filter_emojis_only(i.group(1)),
upvotes=0,
is_bot=True,
ghost=p.ghost
)
g.db.add(c)
exclusive = True
)
g.db.add(option)
body_html = sanitize(body, edit=True)
@ -974,26 +975,20 @@ def submit_post(v, sub=None):
g.db.add(bet_option)
for option in options:
c = Comment(author_id=AUTOPOLLER_ID,
parent_submission=post.id,
level=1,
option = SubmissionOption(
submission_id=post.id,
body_html=filter_emojis_only(option),
upvotes=0,
is_bot=True,
ghost=post.ghost
)
g.db.add(c)
exclusive=False
)
g.db.add(option)
for choice in choices:
c = Comment(author_id=AUTOCHOICE_ID,
parent_submission=post.id,
level=1,
choice = SubmissionOption(
submission_id=post.id,
body_html=filter_emojis_only(choice),
upvotes=0,
is_bot=True,
ghost=post.ghost
)
g.db.add(c)
exclusive=True
)
g.db.add(choice)
vote = Vote(user_id=v.id,
vote_type=1,

View File

@ -317,7 +317,7 @@ def upvoting(v, username):
votes = g.db.query(Submission.author_id, func.count(Submission.author_id)).join(Vote, Vote.submission_id==Submission.id).filter(Submission.ghost == False, Submission.is_banned == False, Submission.deleted_utc == 0, Vote.vote_type==1, Vote.user_id==id).group_by(Submission.author_id).order_by(func.count(Submission.author_id).desc()).all()
votes2 = g.db.query(Comment.author_id, func.count(Comment.author_id)).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.is_banned == False, Comment.deleted_utc == 0, CommentVote.vote_type==1, CommentVote.user_id==id, Comment.author_id.notin_(poll_bots)).group_by(Comment.author_id).order_by(func.count(Comment.author_id).desc()).all()
votes2 = g.db.query(Comment.author_id, func.count(Comment.author_id)).join(CommentVote, CommentVote.comment_id==Comment.id).filter(Comment.ghost == False, Comment.is_banned == False, Comment.deleted_utc == 0, CommentVote.vote_type==1, CommentVote.user_id==id).group_by(Comment.author_id).order_by(func.count(Comment.author_id).desc()).all()
votes = Counter(dict(votes)) + Counter(dict(votes2))

View File

@ -6,12 +6,9 @@ from flask import *
from files.__main__ import app, limiter, cache
from os import environ
@app.get("/votes")
@app.get("/votes/<linK>")
@auth_required
def admin_vote_info_get(v):
link = request.values.get("link")
if not link: return render_template("votes.html", v=v)
def vote_info_get(v, link):
try:
if "t2_" in link: thing = get_post(int(link.split("t2_")[1]), v=v)
elif "t3_" in link: thing = get_comment(int(link.split("t3_")[1]), v=v)
@ -20,8 +17,6 @@ def admin_vote_info_get(v):
if thing.ghost and v.id != AEVANN_ID: abort(403)
if not thing.author:
print(thing.id, flush=True)
if isinstance(thing, Submission):
if thing.author.shadowbanned and not (v and v.admin_level):
thing_id = g.db.query(Submission.id).filter_by(upvotes=thing.upvotes, downvotes=thing.downvotes).order_by(Submission.id).first()[0]
@ -151,8 +146,6 @@ def api_vote_comment(comment_id, new, v):
except: abort(404)
comment = get_comment(comment_id)
if new == 1 and comment.author_id in poll_bots: return {"error": "forbidden."}, 403
existing = g.db.query(CommentVote).filter_by(user_id=v.id, comment_id=comment.id).one_or_none()
@ -207,100 +200,4 @@ def api_vote_comment(comment_id, new, v):
comment.realupvotes = g.db.query(CommentVote).filter_by(comment_id=comment.id, real=True).count()
if comment.author.progressivestack: comment.realupvotes *= 2
g.db.add(comment)
return "", 204
@app.post("/vote/poll/<comment_id>")
@is_not_permabanned
def api_vote_poll(comment_id, v):
vote = request.values.get("vote")
if vote == "true": new = 1
elif vote == "false": new = 0
else: abort(400)
comment_id = int(comment_id)
comment = get_comment(comment_id)
existing = g.db.query(CommentVote).filter_by(user_id=v.id, comment_id=comment.id).one_or_none()
if existing and existing.vote_type == new: return "", 204
if existing:
if new == 1:
existing.vote_type = new
g.db.add(existing)
else: g.db.delete(existing)
elif new == 1:
vote = CommentVote(user_id=v.id, vote_type=new, comment_id=comment.id)
g.db.add(vote)
g.db.flush()
comment.upvotes = g.db.query(CommentVote).filter_by(comment_id=comment.id, vote_type=1).count()
g.db.add(comment)
return "", 204
@app.post("/bet/<comment_id>")
@limiter.limit("1/second;30/minute;200/hour;1000/day")
@limiter.limit("1/second;30/minute;200/hour;1000/day", key_func=lambda:f'{request.host}-{session.get("lo_user")}')
@is_not_permabanned
def bet(comment_id, v):
if not v.can_gamble: return {"error": "You have gambling disabled!"}
if v.coins < 200: return {"error": "You don't have 200 coins!"}
vote = request.values.get("vote")
comment_id = int(comment_id)
comment = get_comment(comment_id)
option_ids = map(lambda x: x.id, comment.post.bet_options)
existing = g.db.query(CommentVote).filter_by(user_id=v.id) \
.filter(CommentVote.comment_id.in_(option_ids)).one_or_none()
if existing: return "", 204
vote = CommentVote(user_id=v.id, vote_type=1, comment_id=comment.id)
g.db.add(vote)
comment.upvotes += 1
g.db.add(comment)
v.coins -= 200
g.db.add(v)
autobetter = get_account(AUTOBETTER_ID)
autobetter.coins += 200
g.db.add(autobetter)
return "", 204
@app.post("/vote/choice/<comment_id>")
@is_not_permabanned
def api_vote_choice(comment_id, v):
comment_id = int(comment_id)
comment = get_comment(comment_id)
existing = g.db.query(CommentVote).filter_by(user_id=v.id, comment_id=comment.id).one_or_none()
if existing and existing.vote_type == 1: return "", 204
if existing:
existing.vote_type = 1
g.db.add(existing)
else:
vote = CommentVote(user_id=v.id, vote_type=1, comment_id=comment.id)
g.db.add(vote)
if comment.parent_comment: parent = comment.parent_comment
else: parent = comment.post
vote = parent.total_choice_voted(v)
if vote:
vote.comment.upvotes = g.db.query(CommentVote).filter_by(comment_id=vote.comment.id, vote_type=1).count() - 1
g.db.add(vote.comment)
g.db.delete(vote)
g.db.flush()
comment.upvotes = g.db.query(CommentVote).filter_by(comment_id=comment.id, vote_type=1).count()
g.db.add(comment)
return "", 204
return "", 204

View File

@ -443,7 +443,7 @@
{% endif %}
{% if not c.ghost or v.id == AEVANN_ID %}<a href="/votes?link={{c.fullname}}" class="btn caction nobackground px-1 text-muted"><i class="fas fa-arrows-v"></i>Votes</a>{% endif %}
{% if not c.ghost or v.id == AEVANN_ID %}<a href="/votes/{{c.fullname}}" class="btn caction nobackground px-1 text-muted"><i class="fas fa-arrows-v"></i>Votes</a>{% endif %}
<a class="btn caction nobackground px-1 text-muted" href="{{c.permalink}}"><i class="fas fa-book-open"></i>Context</a>
@ -646,7 +646,7 @@
<div class="modal-body">
<ul class="list-group comment-actions">
{% if not c.ghost or v.id == AEVANN_ID %}<a href="/votes?link={{c.fullname}}"><li class="list-group-item"><i class="fas fa-arrows-v mr-2"></i>Votes</li></a>{% endif %}
{% if not c.ghost or v.id == AEVANN_ID %}<a href="/votes/{{c.fullname}}"><li class="list-group-item"><i class="fas fa-arrows-v mr-2"></i>Votes</li></a>{% endif %}
<a class="list-group-item" href="{{c.permalink}}"><i class="fas fa-book-open mr-2"></i>Context</a>

View File

@ -142,11 +142,11 @@ Text 2
<td>
<div class="custom-control">
<input autocomplete="off" type="checkbox" class="custom-control-input" id="422741">
<label class="custom-control-label" for="422741">bussy - <a href="/votes?link=t3_422741"><span id="poll-422741">0</span> votes</a></label>
<label class="custom-control-label" for="422741">bussy - <a href="/votes/t3_422741"><span id="poll-422741">0</span> votes</a></label>
</div>
<div class="custom-control">
<input autocomplete="off" type="checkbox" class="custom-control-input" id="422742">
<label class="custom-control-label" for="422742">gussy - <a href="/votes?link=t3_422742"><span id="poll-422742">0</span> votes</a></label>
<label class="custom-control-label" for="422742">gussy - <a href="/votes/t3_422742"><span id="poll-422742">0</span> votes</a></label>
</div>
</td>
</tr>
@ -161,11 +161,11 @@ Text 2
<td>
<div class="custom-control">
<input name="choice" autocomplete="off" type="radio" class="custom-control-input" id="1338113">
<label class="custom-control-label" for="1338113">bussy - <a href="/votes?link=t3_1338113"><span id="choice-1338113">0</span> votes</a></label>
<label class="custom-control-label" for="1338113">bussy - <a href="/votes/t3_1338113"><span id="choice-1338113">0</span> votes</a></label>
</div>
<div class="custom-control">
<input name="choice" autocomplete="off" type="radio" class="custom-control-input" id="1338114">
<label class="custom-control-label" for="1338114">gussy - <a href="/votes?link=t3_1338114"><span id="choice-1338114">0</span> votes</a></label>
<label class="custom-control-label" for="1338114">gussy - <a href="/votes/t3_1338114"><span id="choice-1338114">0</span> votes</a></label>
</div>
</td>
</tr>

View File

@ -0,0 +1,49 @@
{% extends "default.html" %}
{% block title %}
<title>{{SITE_NAME}}</title>
{% endblock %}
{% block content %}
{% if thing %}
<script src="/assets/js/sort_table.js?v=243"></script>
<pre>
</pre>
<h2>Votes: {{ups | length}}</h2>
<div class="overflow-x-auto mt-5">
<table class="table table-striped mb-5">
<thead class="bg-primary text-white">
<tr>
<th>User</th>
<th role="button" onclick="sort_table(1)">User Truescore</th>
<th>Vote Time</th>
</tr>
</thead>
{% for vote in ups %}
<tr>
<td>
<a style="color:#{{vote.user.namecolor}};font-weight:bold" href="/@{{vote.user.username}}">
<img loading="lazy" src="{{vote.user.profile_url}}" class="pp20">
<span {% if vote.user.patron %}class="patron" style="background-color:#{{vote.user.namecolor}}"{% endif %}>
{{vote.user.username}}
</span>
</a>
</td>
<td>{{vote.user.truecoins}}</td>
<td>{{vote.created_datetime}}</td>
</tr>
{% endfor %}
</table>
</div>
{% endif %}
{% endblock %}

View File

@ -6,7 +6,7 @@
{% endif %}
{% if not p.ghost or v.id == AEVANN_ID %}<a class="list-inline-item" href="/votes?link={{p.fullname}}"><i class="fas fa-arrows-v"></i>Votes</a>{% endif %}
{% if not p.ghost or v.id == AEVANN_ID %}<a class="list-inline-item" href="/votes/{{p.fullname}}"><i class="fas fa-arrows-v"></i>Votes</a>{% endif %}
{% if v %}
<a class="list-inline-item text-muted d-none d-md-inline-block" role="button" data-bs-toggle="modal" data-bs-target="#awardModal" data-url='/award/post/{{p.id}}'><i class="fas fa-gift fa-fw"></i>Give Award</a>

View File

@ -6,7 +6,7 @@
{% endif %}
{% if not p.ghost or v.id == AEVANN_ID %}<a class="btn-block" href="/votes?link={{p.fullname}}"><button class="nobackground btn btn-link btn-block btn-lg text-left text-muted"><i class="fas fa-arrows-v text-center text-muted mr-3"></i>Votes</button></a>{% endif %}
{% if not p.ghost or v.id == AEVANN_ID %}<a class="btn-block" href="/votes/{{p.fullname}}"><button class="nobackground btn btn-link btn-block btn-lg text-left text-muted"><i class="fas fa-arrows-v text-center text-muted mr-3"></i>Votes</button></a>{% endif %}
<button data-bs-dismiss="modal" class="copy-link nobackground btn btn-link btn-block btn-lg text-left text-muted" data-clipboard-text="{% if request.host == 'rdrama.net' %}https://chapotraphouse.club{{p.shortlink}}{% else %}{{p.permalink}}{% endif %}"><i class="far fa-copy text-center text-muted mr-3"></i>Copy link</button>

View File

@ -17,7 +17,7 @@ set CACHE_VER = {
'js/award_modal.js': 253,
'js/bootstrap.js': 272,
'js/comments+submission_listing.js': 261,
'js/comments+submission_listing.js': 262,
'js/submission_listing.js': 261,
'js/emoji_modal.js': 297,
'js/formatting.js': 240,

View File

@ -21,19 +21,7 @@ INSERT INTO public.users (username, passhash, created_utc, admin_level, over_18,
('zozbot', '', extract(epoch from now()), 0, true, true, '', '', 0, false,
0, 'zozbot', '', 'hot', 'top', 'day', '62ca56', 'e4432d',
'', 'dark', '30409f', false, 'old.reddit.com', '', '', 0, 0,
0, 0, '', 'Verified', 0, false, '/i/pfps/5.webp', '/i/pfps/5.webp'),
('AutoPoller', '', extract(epoch from now()), 0, true, true, '', '', 0, false,
0, 'AutoPoller', '', 'hot', 'top', 'day', '62ca56', 'e4432d',
'', 'dark', '30409f', false, 'old.reddit.com', '', '', 0, 0,
0, 0, '', 'Verified', 0, false, '', ''),
('AutoBetter', '', extract(epoch from now()), 0, true, true, '', '', 0, false,
0, 'AutoBetter', '', 'hot', 'top', 'day', '62ca56', 'e4432d',
'', 'dark', '30409f', false, 'old.reddit.com', '', '', 0, 0,
0, 0, '', 'Verified', 0, false, '', ''),
('AutoChoice', '', extract(epoch from now()), 0, true, true, '', '', 0, false,
0, 'AutoChoice', '', 'hot', 'top', 'day', '62ca56', 'e4432d',
'', 'dark', '30409f', false, 'old.reddit.com', '', '', 0, 0,
0, 0, '', 'Verified', 0, false, '', '');
0, 0, '', 'Verified', 0, false, '/i/pfps/5.webp', '/i/pfps/5.webp');
INSERT INTO public.marseys VALUES
('marseylaugh',1,'lmao reaction point funny haha lol judgment',0),

View File

@ -0,0 +1,109 @@
CREATE TABLE public.submission_options (
id integer PRIMARY KEY,
submission_id integer NOT NULL,
body_html character varying(500) NOT NULL,
exclusive boolean NOT NULL
);
CREATE INDEX option_submission ON public.submission_options USING btree (submission_id);
ALTER TABLE ONLY public.submission_options
ADD CONSTRAINT option_submission_fkey FOREIGN KEY (submission_id) REFERENCES public.submissions(id) MATCH FULL;
CREATE SEQUENCE public.submission_option_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE ONLY public.submission_options ALTER COLUMN id SET DEFAULT nextval('public.submission_option_id_seq'::regclass);
CREATE TABLE public.submission_option_votes (
option_id integer NOT NULL,
user_id integer NOT NULL,
created_utc integer NOT NULL,
submission_id integer
);
ALTER TABLE ONLY public.submission_option_votes
ADD CONSTRAINT submission_option_votes_pkey PRIMARY KEY (option_id, user_id);
ALTER TABLE ONLY public.submission_option_votes
ADD CONSTRAINT vote_option_fkey FOREIGN KEY (option_id) REFERENCES public.submission_options(id) MATCH FULL;
ALTER TABLE ONLY public.submission_option_votes
ADD CONSTRAINT vote_user_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) MATCH FULL;
ALTER TABLE ONLY public.submission_option_votes
ADD CONSTRAINT vote_submission_fkey FOREIGN KEY (submission_id) REFERENCES public.submissions(id) MATCH FULL;
CREATE TABLE public.comment_options (
id integer PRIMARY KEY,
comment_id integer NOT NULL,
body_html character varying(500) NOT NULL,
exclusive boolean NOT NULL
);
CREATE INDEX option_comment ON public.comment_options USING btree (comment_id);
ALTER TABLE ONLY public.comment_options
ADD CONSTRAINT option_comment_fkey FOREIGN KEY (comment_id) REFERENCES public.comments(id) MATCH FULL;
CREATE SEQUENCE public.comment_option_id_seq
AS integer
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER TABLE ONLY public.comment_options ALTER COLUMN id SET DEFAULT nextval('public.comment_option_id_seq'::regclass);
CREATE TABLE public.comment_option_votes (
option_id integer NOT NULL,
user_id integer NOT NULL,
created_utc integer NOT NULL,
comment_id integer
);
ALTER TABLE ONLY public.comment_option_votes
ADD CONSTRAINT comment_option_votes_pkey PRIMARY KEY (option_id, user_id);
ALTER TABLE ONLY public.comment_option_votes
ADD CONSTRAINT vote_option_fkey FOREIGN KEY (option_id) REFERENCES public.comment_options(id) MATCH FULL;
ALTER TABLE ONLY public.comment_option_votes
ADD CONSTRAINT vote_user_fkey FOREIGN KEY (user_id) REFERENCES public.users(id) MATCH FULL;
ALTER TABLE ONLY public.comment_option_votes
ADD CONSTRAINT vote_comment_fkey FOREIGN KEY (comment_id) REFERENCES public.comments(id) MATCH FULL;