diff --git a/suchwow/_models.py b/suchwow/_models.py index 54731c7..52625d8 100644 --- a/suchwow/_models.py +++ b/suchwow/_models.py @@ -32,8 +32,13 @@ class User(Model): ban_timestamp = DateField(null=True) login_timestamp = DateTimeField(null=True) - def __repr__(self): - return self.username + def get_wow_received(self): + tips = TipReceived.select().join(Post).where(Post.user == self) + return sum(tip.amount for tip in tips) + + def get_wow_sent(self): + tips = TipSent.select().where(TipSent.from_user == self) + return sum(tip.amount for tip in tips) class Meta: database = db @@ -74,17 +79,9 @@ class Post(Model): s = path.splitext(self.image_name) return s[0] + '.thumbnail' + s[1] - def get_received_wow(self): - try: - w = wownero.Wallet() - it = w.incoming_transfers(self.account_index) - if 'transfers' in it: - amounts = [amt['amount'] for amt in it['transfers'] if 'transfers' in it] - return wownero.as_wownero(wownero.from_atomic(sum(amounts))) - else: - return 0 - except: - return '?' + def get_wow_received(self): + tips = TipReceived.select().where(TipReceived.post == self) + return sum(tip.amount for tip in tips) def hours_elapsed(self): now = datetime.utcnow() @@ -107,7 +104,7 @@ class Post(Model): 'timestamp': self.timestamp, 'approved': self.approved, 'approved_by': self.approved_by, - 'received_wow': self.get_received_wow(), + 'received_wow': self.get_wow_received(), 'hours_elapsed': self.hours_elapsed() } @@ -149,7 +146,8 @@ class TipReceived(Model): class TipSent(Model): id = AutoField() - user = ForeignKeyField(User) + from_user = ForeignKeyField(User) + to_user = ForeignKeyField(User) txid = CharField(unique=True) timestamp = DateTimeField() amount = IntegerField() diff --git a/suchwow/cli.py b/suchwow/cli.py index 289680c..7f6ad31 100644 --- a/suchwow/cli.py +++ b/suchwow/cli.py @@ -1,12 +1,9 @@ from os import makedirs -import click -from flask import Blueprint, url_for, current_app +from flask import Blueprint from suchwow._models import db, User, Post, AuditEvent, TipSent, TipReceived, Vote -from suchwow.utils.helpers import get_latest_tipped_posts -from suchwow.utils.helpers import get_top_posters, get_top_posts -from suchwow.reddit import make_post +from suchwow.models import Post as OldPost from suchwow import wownero from suchwow import config @@ -23,6 +20,29 @@ def init(): db.create_tables([User, Post, AuditEvent, TipSent, TipReceived, Vote]) +@bp.cli.command('rescan') +def rescan(): + wallet = wownero.Wallet() + wallet.make_wallet_rpc('rescan_blockchain') + + +@bp.cli.command('save') +def rescan(): + wallet = wownero.Wallet() + wallet.make_wallet_rpc('store') + print('Saved wallet.') + + +@bp.cli.command("create_accounts") +def create_accounts(): + wallet = wownero.Wallet() + highest_account = OldPost.select().order_by(OldPost.timestamp.desc()).first().account_index + print(f'Highest post account index is {highest_account} but highest wallet account is {wallet.accounts()[-1]}. Generating new accounts!') + while wallet.accounts()[-1] < highest_account: + account = wallet.new_account() + print(f"Created account {account}") + wallet.make_wallet_rpc('store') + # @bp.cli.command("post_reddit") # @click.argument('last_hours') # def post_reddit(last_hours): @@ -40,15 +60,6 @@ def init(): # return -# @bp.cli.command("create_accounts") -# def create_accounts(): -# wallet = wownero.Wallet() -# for post in Post.select(): -# if post.account_index not in wallet.accounts(): -# account = wallet.new_account() -# print(f"Created account {account}") - - # @bp.cli.command("payout_users") # def payout_users(): # wallet = wownero.Wallet() diff --git a/suchwow/routes/leaderboard.py b/suchwow/routes/leaderboard.py index 93cd575..f210aa0 100644 --- a/suchwow/routes/leaderboard.py +++ b/suchwow/routes/leaderboard.py @@ -1,13 +1,27 @@ +import peewee from flask import render_template, Blueprint, request -from suchwow.utils.helpers import get_top_posters, get_top_posts +from suchwow._models import Post, TipReceived, User bp = Blueprint("leaderboard", "leaderboard") @bp.route("/leaderboards/top_posters") def top_posters(): - top_posters = get_top_posters() + top_posters = TipReceived.select( + TipReceived.post, peewee.fn.SUM(TipReceived.amount) + ).join(Post).order_by( + peewee.fn.SUM(TipReceived.amount).desc() + ).group_by(TipReceived.post) + # revenue = fn.SUM(Booking.slots * Case(None, ( + # (Booking.member == 0, Facility.guestcost), + # ), Facility.membercost)) + # + # query = (Facility + # .select(Facility.name, revenue.alias('revenue')) + # .join(Booking) + # .group_by(Facility.name) + # .order_by(SQL('revenue'))) return render_template("leaderboard.html", posters=top_posters) @bp.route("/leaderboards/top_posts") diff --git a/suchwow/routes/main.py b/suchwow/routes/main.py index e3b3834..96b8088 100644 --- a/suchwow/routes/main.py +++ b/suchwow/routes/main.py @@ -2,8 +2,7 @@ from math import ceil from flask import Blueprint, request, render_template, flash -from suchwow._models import Post, User -from suchwow.utils.helpers import get_latest_tipped_posts +from suchwow._models import Post, User, TipReceived bp = Blueprint('main', 'main') @@ -16,10 +15,10 @@ def index(): content = request.args.get("content", None) if content == 'latest_tipped': - posts = get_latest_tipped_posts() + posts = Post.select().join(TipReceived).distinct().order_by(TipReceived.timestamp.desc()).limit(30) return render_template( "index.html", - posts=posts[0:30], + posts=posts, title="Latest Tipped Memes" ) @@ -29,9 +28,9 @@ def index(): flash("Wow, wtf hackerman. Cool it.", "is-danger") page = 1 - posts = Post.select().where(Post.approved==True).order_by(Post.timestamp.desc()) + posts = Post.select().where(Post.approved == True).order_by(Post.timestamp.desc()) if submitter: - user = Post.select().where(Post.username == submitter).first() + user = User.select().where(User.username == submitter) if not user: flash('That user does not exist!', 'is-warning') else: diff --git a/suchwow/routes/post.py b/suchwow/routes/post.py index 9f52907..c44e180 100644 --- a/suchwow/routes/post.py +++ b/suchwow/routes/post.py @@ -11,7 +11,7 @@ from werkzeug.utils import secure_filename from suchwow import wownero, config from suchwow._models import User, Post, TipReceived from suchwow.utils.decorators import login_required, address_required, moderator_required -from suchwow.utils.helpers import allowed_file, is_moderator, get_session_user +from suchwow.utils.helpers import allowed_file, get_session_user from suchwow.utils.helpers import audit_event from suchwow.discord import post_discord_webhook diff --git a/suchwow/templates/index.html b/suchwow/templates/index.html index cc4757e..1f2af8c 100644 --- a/suchwow/templates/index.html +++ b/suchwow/templates/index.html @@ -16,19 +16,18 @@ {% if posts %} {% for row in posts | batch(4) %}
- {% for p in row %} - {% set post = p.show() %} + {% for post in row %}
-
{{ post.text | truncate(60) }} -

{{ post.received_wow }} WOW received

+

{{ post.get_wow_received() | from_atomic }} WOW received

({{ post.timestamp | humanize }})

diff --git a/suchwow/utils/helpers.py b/suchwow/utils/helpers.py index 82df455..c08fbd8 100644 --- a/suchwow/utils/helpers.py +++ b/suchwow/utils/helpers.py @@ -5,8 +5,6 @@ from datetime import datetime, timedelta from flask import session from suchwow._models import AuditEvent, User -from suchwow.models import Moderator, Post, Profile -from suchwow.wownero import Wallet, from_atomic from suchwow import config @@ -14,13 +12,6 @@ def allowed_file(filename): return "." in filename and \ filename.rsplit(".", 1)[1].lower() in config.ALLOWED_EXTENSIONS -def is_moderator(username): - m = Moderator.filter(username=username) - if m: - return True - else: - return False - def get_current_user(): u = User.select().where(User.username == get_session_user()).first() return u @@ -34,116 +25,3 @@ def get_session_user(): return None return session["auth"]["preferred_username"].strip() -def get_latest_tipped_posts(): - key_name = 'latest_tips' - posts = [] - tipped_posts = rw_cache(key_name) - if not tipped_posts: - w = Wallet() - data = {} - for acc in w.accounts(): - txes = w.transfers(acc) - if 'in' in txes: - for tx in txes['in']: - p = Post.select().where( - Post.account_index==acc - ).first() - if p: - data[tx['timestamp']] = p - - dates = sorted(data, reverse=True) - for d in dates: - if not data[d] in posts: - posts.append(data[d]) - - tipped_posts = rw_cache(key_name, posts) - - return tipped_posts - -def get_top_posters(): - top_posters = {} - posts = rw_cache('top_posters') - if not posts: - posts = Post.select().where(Post.approved==True) - for post in posts: - transfers = [] - incoming = Wallet().incoming_transfers(post.account_index) - if "transfers" in incoming: - for xfer in incoming["transfers"]: - transfers.append(from_atomic(xfer["amount"])) - total = sum(transfers) - if post.submitter not in top_posters: - top_posters[post.submitter] = {"amount": 0, "posts": []} - - top_posters[post.submitter]["amount"] += float(total) - top_posters[post.submitter]["posts"].append(post) - rw_cache('top_posters', top_posters) - else: - top_posters = posts - return top_posters - -def get_top_posts(days=1): - top_posts = [] - try: - days = int(days) - except: - days = 1 - - # stupid magic number bcuz fuck it - if days not in [1, 3, 7, 30, 9999]: - days = 7 - - hours = 24 * days - diff = datetime.now() - timedelta(hours=hours) - key_name = f'top_posts_{str(hours)}' - - posts = rw_cache(key_name) - if not posts: - posts = Post.select().where( - Post.approved==True, - Post.timestamp > diff - ).order_by( - Post.timestamp.desc() - ) - for post in posts: - p = post.show() - if isinstance(p['received_wow'], float): - top_posts.append(p) - - posts = rw_cache(key_name, top_posts) - return posts - -# Use hacky filesystem cache since i dont feel like shipping redis -def rw_cache(key_name, data=None, diff_seconds=3600): - pickle_file = path.join(config.DATA_FOLDER, f'{key_name}.pkl') - try: - if path.isfile(pickle_file): - mtime_ts = path.getmtime(pickle_file) - mtime = datetime.fromtimestamp(mtime_ts) - now = datetime.now() - diff = now - mtime - # If pickled data file is less than an hour old, load it and render page - # Otherwise, determine balances, build json, store pickled data, and render page - if diff.seconds < diff_seconds: - print(f'unpickling {key_name}') - with open(pickle_file, 'rb') as f: - pickled_data = pickle.load(f) - return pickled_data - else: - if data: - print(f'pickling {key_name}') - with open(pickle_file, 'wb') as f: - f.write(pickle.dumps(data)) - return data - else: - return None - else: - if data: - print(f'pickling {key_name}') - with open(pickle_file, 'wb') as f: - f.write(pickle.dumps(data)) - return data - else: - return None - except: - return None diff --git a/suchwow/wownero.py b/suchwow/wownero.py index d6c7e3b..ec0183b 100644 --- a/suchwow/wownero.py +++ b/suchwow/wownero.py @@ -62,7 +62,6 @@ class Wallet(object): def new_account(self, label=None): _account = self.make_wallet_rpc('create_account', {'label': label}) - self.store() return _account['account_index'] def addresses(self, account, addr_indices=None):