Compare commits
18 Commits
master
...
migrate-ne
Author | SHA1 | Date |
---|---|---|
lza_menace | 90377f8d0a | 2 years ago |
lza_menace | 159504f703 | 2 years ago |
lza_menace | faebd00fd5 | 2 years ago |
lza_menace | a6aea485af | 2 years ago |
lza_menace | 7010790739 | 2 years ago |
lza_menace | c5d0b4a9ff | 2 years ago |
lza_menace | 12758c5bb5 | 2 years ago |
lza_menace | b250ba7460 | 2 years ago |
lza_menace | 1624439bba | 2 years ago |
lza_menace | 70ccd57519 | 2 years ago |
lza_menace | f93bd618ad | 2 years ago |
lza_menace | 08050e56c6 | 2 years ago |
lza_menace | 0d02bf63f7 | 2 years ago |
lza_menace | 289995cd49 | 2 years ago |
lza_menace | df33eaf258 | 2 years ago |
lza_menace | ccb48a16c2 | 2 years ago |
lza_menace | da874c3994 | 2 years ago |
lza_menace | b635e39e04 | 2 years ago |
@ -0,0 +1,82 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# export suchwow data for the purpose of importing into new model definitions
|
||||||
|
|
||||||
|
import pickle
|
||||||
|
|
||||||
|
from suchwow.models import Post, Moderator, Profile, Ban, AuditEvent
|
||||||
|
from suchwow import wownero
|
||||||
|
|
||||||
|
wallet = wownero.Wallet()
|
||||||
|
if not wallet.connected:
|
||||||
|
print('Wallet not connected')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
all_posts = Post.select().order_by(Post.timestamp.asc())
|
||||||
|
all_mods = Moderator.select()
|
||||||
|
all_profiles = Profile.select()
|
||||||
|
all_bans = Ban.select()
|
||||||
|
all_audits = AuditEvent.select()
|
||||||
|
all_data = {
|
||||||
|
'posts': list(),
|
||||||
|
'moderators': list(),
|
||||||
|
'profiles': list(),
|
||||||
|
'bans': list(),
|
||||||
|
'auditevents': list()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
for post in all_posts:
|
||||||
|
post_data = {
|
||||||
|
'id': post.id,
|
||||||
|
'title': post.title,
|
||||||
|
'text': post.text,
|
||||||
|
'submitter': post.submitter,
|
||||||
|
'image_name': post.image_name,
|
||||||
|
'readonly': post.readonly,
|
||||||
|
'hidden': post.hidden,
|
||||||
|
'account_index': post.account_index,
|
||||||
|
'address_index': post.address_index,
|
||||||
|
'timestamp': post.timestamp,
|
||||||
|
'reddit_url': post.reddit_url,
|
||||||
|
'to_reddit': post.to_reddit,
|
||||||
|
'to_discord': post.to_discord,
|
||||||
|
'approved': post.approved,
|
||||||
|
'txes': wallet.make_wallet_rpc('get_transfers', {
|
||||||
|
'account_index': post.account_index,
|
||||||
|
'subaddr_indices': [],
|
||||||
|
'in': True,
|
||||||
|
'out': True
|
||||||
|
})
|
||||||
|
}
|
||||||
|
txes = 0
|
||||||
|
all_data['posts'].append(post_data)
|
||||||
|
if 'in' in post_data['txes']:
|
||||||
|
txes = len(post_data['txes']['in'])
|
||||||
|
print(f'Exporting post {post.id}. Found {txes} txes')
|
||||||
|
|
||||||
|
for mod in all_mods:
|
||||||
|
all_data['moderators'].append(mod.username)
|
||||||
|
|
||||||
|
for profile in all_profiles:
|
||||||
|
all_data['profiles'].append({
|
||||||
|
'username': profile.username,
|
||||||
|
'address': profile.address
|
||||||
|
})
|
||||||
|
|
||||||
|
for ban in all_bans:
|
||||||
|
all_data['bans'].append({
|
||||||
|
'username': ban.user.username,
|
||||||
|
'reason': ban.reason,
|
||||||
|
'timestamp': ban.timestamp
|
||||||
|
})
|
||||||
|
|
||||||
|
for event in all_audits:
|
||||||
|
all_data['auditevents'].append({
|
||||||
|
'username': event.user.username,
|
||||||
|
'timestamp': event.timestamp,
|
||||||
|
'action': event.action
|
||||||
|
})
|
||||||
|
|
||||||
|
with open('data/migrate_data.pkl', 'wb') as f:
|
||||||
|
f.write(pickle.dumps(all_data))
|
@ -0,0 +1,107 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# import pickled suchwow data for the purpose of importing into new model definitions
|
||||||
|
|
||||||
|
import pickle
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from suchwow._models import User, Post, AuditEvent, TipSent, TipReceived, Vote
|
||||||
|
from suchwow import wownero
|
||||||
|
|
||||||
|
wallet = wownero.Wallet()
|
||||||
|
all_data = dict()
|
||||||
|
|
||||||
|
if not wallet.connected:
|
||||||
|
print('Wallet not running and connected')
|
||||||
|
exit()
|
||||||
|
|
||||||
|
with open('data/migrate_data.pkl', 'rb') as f:
|
||||||
|
all_data = pickle.load(f)
|
||||||
|
|
||||||
|
# first import users from old profiles
|
||||||
|
for user in all_data['profiles']:
|
||||||
|
if not User.select().where(User.username == user['username']).first():
|
||||||
|
u = User(
|
||||||
|
username=user['username'],
|
||||||
|
address=user['address'],
|
||||||
|
)
|
||||||
|
u.save()
|
||||||
|
print(f'Added user {u.username}')
|
||||||
|
|
||||||
|
for post in all_data['posts']:
|
||||||
|
if not Post.select().where(Post.id == post['id']).first():
|
||||||
|
user = User.get(username=post['submitter'])
|
||||||
|
account_idx = 0
|
||||||
|
address_idx, address = wallet.new_address(account_idx)
|
||||||
|
print(f'Saving post {post["id"]} for user {user.username} (account {account_idx}, address_idx {address_idx}, {address}')
|
||||||
|
Post.create(
|
||||||
|
id=post['id'],
|
||||||
|
title=post['title'],
|
||||||
|
text=post['text'],
|
||||||
|
user=user,
|
||||||
|
image_name=post['image_name'],
|
||||||
|
account_index=account_idx,
|
||||||
|
address_index=address_idx,
|
||||||
|
address=address,
|
||||||
|
timestamp=post['timestamp'],
|
||||||
|
approved=post['approved']
|
||||||
|
)
|
||||||
|
|
||||||
|
if 'in' in post['txes']:
|
||||||
|
p = Post.get(post['id'])
|
||||||
|
for tx in post['txes']['in']:
|
||||||
|
amount = sum(tx['amounts'])
|
||||||
|
received = TipReceived.select().where(
|
||||||
|
TipReceived.txid == tx['txid'],
|
||||||
|
TipReceived.amount == amount,
|
||||||
|
TipReceived.post == p
|
||||||
|
).first()
|
||||||
|
if not received:
|
||||||
|
TipReceived.create(
|
||||||
|
post=p,
|
||||||
|
timestamp=datetime.utcfromtimestamp(tx['timestamp']),
|
||||||
|
txid=tx['txid'],
|
||||||
|
amount=amount,
|
||||||
|
fee=tx['fee']
|
||||||
|
)
|
||||||
|
print(f'Saving received tip txid {tx["txid"]}')
|
||||||
|
|
||||||
|
if 'out' in post['txes']:
|
||||||
|
p = Post.get(post['id'])
|
||||||
|
for tx in post['txes']['out']:
|
||||||
|
if not TipSent.select().where(TipSent.txid == tx['txid']).first():
|
||||||
|
TipSent.create(
|
||||||
|
from_user=p.user,
|
||||||
|
to_user=p.user,
|
||||||
|
txid=tx['txid'],
|
||||||
|
timestamp=datetime.utcfromtimestamp(tx['timestamp']),
|
||||||
|
amount=tx['amount'],
|
||||||
|
fee=tx['fee']
|
||||||
|
)
|
||||||
|
print(f'Saving sent tip txid {tx["txid"]}')
|
||||||
|
|
||||||
|
for mod in all_data['moderators']:
|
||||||
|
u = User.get(User.username == mod)
|
||||||
|
if not u.moderator:
|
||||||
|
u.moderator = True
|
||||||
|
u.save()
|
||||||
|
print(f'Updated {u.username} as moderator')
|
||||||
|
|
||||||
|
for ban in all_data['bans']:
|
||||||
|
u = User.get(User.username == ban['username'])
|
||||||
|
if not u.banned:
|
||||||
|
u.banned = True
|
||||||
|
u.ban_reason = ban['reason']
|
||||||
|
u.ban_timestamp = ban['timestamp']
|
||||||
|
u.save()
|
||||||
|
print(f'Banned {u.username} ({u.ban_reason})')
|
||||||
|
|
||||||
|
for event in all_data['auditevents']:
|
||||||
|
if not AuditEvent.select().where(AuditEvent.timestamp == event['timestamp']):
|
||||||
|
u = User.get(User.username == event['username'])
|
||||||
|
AuditEvent.create(
|
||||||
|
user=u,
|
||||||
|
action=event['action'],
|
||||||
|
timestamp=event['timestamp']
|
||||||
|
)
|
||||||
|
print(f'Saved audit event ({u.username} -> {event["action"]} @ {event["timestamp"]}')
|
@ -0,0 +1,223 @@
|
|||||||
|
from random import choice
|
||||||
|
from os import path
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from peewee import *
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
from suchwow import wownero
|
||||||
|
from suchwow import config
|
||||||
|
|
||||||
|
|
||||||
|
db = SqliteDatabase(f"{config.DATA_FOLDER}/suchwow_db.sqlite")
|
||||||
|
|
||||||
|
ban_reasons = [
|
||||||
|
'you smell bad',
|
||||||
|
'didnt pass the vibe check, homie',
|
||||||
|
'your memes are bad and you should feel bad',
|
||||||
|
'i just dont like you'
|
||||||
|
]
|
||||||
|
|
||||||
|
def get_ban_reason():
|
||||||
|
return choice(ban_reasons)
|
||||||
|
|
||||||
|
|
||||||
|
class User(Model):
|
||||||
|
id = AutoField()
|
||||||
|
username = CharField()
|
||||||
|
address = CharField(null=True)
|
||||||
|
moderator = BooleanField(default=False)
|
||||||
|
banned = BooleanField(default=False)
|
||||||
|
ban_reason = TextField(null=True)
|
||||||
|
ban_timestamp = DateField(null=True)
|
||||||
|
login_timestamp = DateTimeField(null=True)
|
||||||
|
|
||||||
|
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 + tip.fee for tip in tips])
|
||||||
|
|
||||||
|
def get_post_count(self):
|
||||||
|
posts = Post.select().where(Post.user == self)
|
||||||
|
return posts.count()
|
||||||
|
|
||||||
|
def get_post_addresses(self):
|
||||||
|
posts = Post.select().where(Post.user == self)
|
||||||
|
return [i.address_index for i in posts]
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
database = db
|
||||||
|
|
||||||
|
|
||||||
|
class Post(Model):
|
||||||
|
id = AutoField()
|
||||||
|
title = CharField()
|
||||||
|
text = CharField(null=True)
|
||||||
|
user = ForeignKeyField(User)
|
||||||
|
image_name = CharField()
|
||||||
|
account_index = IntegerField()
|
||||||
|
address_index = IntegerField(unique=True)
|
||||||
|
address = CharField(unique=True)
|
||||||
|
timestamp = DateTimeField(default=datetime.utcnow)
|
||||||
|
approved = BooleanField(default=False)
|
||||||
|
approved_by = ForeignKeyField(User, null=True)
|
||||||
|
|
||||||
|
def get_random(self):
|
||||||
|
all_posts = Post.select().where(Post.approved == True)
|
||||||
|
if all_posts:
|
||||||
|
return choice([i.id for i in all_posts])
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_previous(self):
|
||||||
|
prev = Post.select().where(Post.id == self.id - 1).first()
|
||||||
|
if prev and prev.approved:
|
||||||
|
return prev.id
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_next(self):
|
||||||
|
next = Post.select().where(Post.id == self.id + 1).first()
|
||||||
|
if next and next.approved:
|
||||||
|
return next.id
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_image_path(self, thumbnail=False):
|
||||||
|
save_path_base = path.join(config.DATA_FOLDER, "uploads")
|
||||||
|
if thumbnail:
|
||||||
|
save_path = path.join(save_path_base, self.thumbnail)
|
||||||
|
else:
|
||||||
|
save_path = path.join(save_path_base, self.image_name)
|
||||||
|
return save_path
|
||||||
|
|
||||||
|
def save_thumbnail(self):
|
||||||
|
try:
|
||||||
|
image = Image.open(self.get_image_path())
|
||||||
|
image.thumbnail((200,200), Image.ANTIALIAS)
|
||||||
|
image.save(self.get_image_path(True), format=image.format, quality=90)
|
||||||
|
image.close()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def strip_exif(self):
|
||||||
|
try:
|
||||||
|
image = Image.open(self.get_image_path())
|
||||||
|
data = image.getdata()
|
||||||
|
image_without_exif = Image.new(image.mode, image.size)
|
||||||
|
image_without_exif.putdata(data)
|
||||||
|
image_without_exif.save(self.get_image_path())
|
||||||
|
image_without_exif.close()
|
||||||
|
image.close()
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def resize_image(self):
|
||||||
|
try:
|
||||||
|
with Image.open(self.get_image_path()) as img:
|
||||||
|
img.thumbnail((1800,1800))
|
||||||
|
img.save(self.get_image_path())
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def resized(self):
|
||||||
|
s = path.splitext(self.image_name)
|
||||||
|
return s[0] + '.resized' + s[1]
|
||||||
|
|
||||||
|
@property
|
||||||
|
def thumbnail(self):
|
||||||
|
s = path.splitext(self.image_name)
|
||||||
|
return s[0] + '.thumbnail' + s[1]
|
||||||
|
|
||||||
|
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()
|
||||||
|
diff = now - self.timestamp
|
||||||
|
return diff.total_seconds() / 60 / 60
|
||||||
|
|
||||||
|
def show(self):
|
||||||
|
return {
|
||||||
|
'id': self.id,
|
||||||
|
'title': self.title,
|
||||||
|
'text': self.text,
|
||||||
|
'user': self.user.username,
|
||||||
|
'image_name': self.image_name,
|
||||||
|
'image_path': self.get_image_path(),
|
||||||
|
'thumbnail_name': self.thumbnail,
|
||||||
|
'thumbnail_path': self.get_image_path(True),
|
||||||
|
'account_index': self.account_index,
|
||||||
|
'address_index': self.address_index,
|
||||||
|
'address': self.address,
|
||||||
|
'timestamp': self.timestamp,
|
||||||
|
'approved': self.approved,
|
||||||
|
'approved_by': self.approved_by,
|
||||||
|
'received_wow': self.get_wow_received(),
|
||||||
|
'hours_elapsed': self.hours_elapsed(),
|
||||||
|
'user_tips_received': wownero.from_atomic(self.user.get_wow_received()),
|
||||||
|
'user_tips_sent': wownero.from_atomic(self.user.get_wow_sent())
|
||||||
|
}
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
database = db
|
||||||
|
|
||||||
|
|
||||||
|
class SocialPost(Model):
|
||||||
|
id = AutoField()
|
||||||
|
post = ForeignKeyField(Post)
|
||||||
|
service = CharField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
database = db
|
||||||
|
|
||||||
|
|
||||||
|
class Vote(Model):
|
||||||
|
id = AutoField()
|
||||||
|
post = ForeignKeyField(Post)
|
||||||
|
upvote = BooleanField()
|
||||||
|
timestamp = DateTimeField(default=datetime.now)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
database = db
|
||||||
|
|
||||||
|
|
||||||
|
class AuditEvent(Model):
|
||||||
|
id = AutoField()
|
||||||
|
user = ForeignKeyField(User)
|
||||||
|
timestamp = DateTimeField(default=datetime.now)
|
||||||
|
action = CharField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
database = db
|
||||||
|
|
||||||
|
|
||||||
|
class TipReceived(Model):
|
||||||
|
id = AutoField()
|
||||||
|
post = ForeignKeyField(Post)
|
||||||
|
txid = CharField()
|
||||||
|
timestamp = DateTimeField()
|
||||||
|
amount = IntegerField()
|
||||||
|
fee = IntegerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
database = db
|
||||||
|
|
||||||
|
|
||||||
|
class TipSent(Model):
|
||||||
|
id = AutoField()
|
||||||
|
from_user = ForeignKeyField(User)
|
||||||
|
to_user = ForeignKeyField(User)
|
||||||
|
txid = CharField()
|
||||||
|
timestamp = DateTimeField()
|
||||||
|
amount = IntegerField()
|
||||||
|
fee = IntegerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
database = db
|
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,62 +0,0 @@
|
|||||||
{% extends 'base.html' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<div class="container" style="text-align:center;">
|
|
||||||
|
|
||||||
<h1 class="title">Top Memes Last {{ days }} Days</h1>
|
|
||||||
|
|
||||||
<section class="section">
|
|
||||||
{% if posts %}
|
|
||||||
{% for row in posts | sort(attribute='received_wow', reverse=True) | batch(4) %}
|
|
||||||
<div class="columns">
|
|
||||||
{% if loop.index < 15 %}
|
|
||||||
{% for post in row %}
|
|
||||||
<div class="column">
|
|
||||||
<div class="card">
|
|
||||||
<div class="card-image">
|
|
||||||
<a href="{{ url_for('post.read', id=post.id) }}">
|
|
||||||
<img src="{{ url_for('post.uploaded_file', filename=post.thumbnail_name) }}" alt="Placeholder image">
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="card-content">
|
|
||||||
<div class="media">
|
|
||||||
<div class="media-content">
|
|
||||||
<p class="title is-4">
|
|
||||||
<a href="{{ url_for('post.read', id=post.id) }}">{{ post.title }}</a>
|
|
||||||
</p>
|
|
||||||
<p class="subtitle is-6"><a href="/?submitter={{ post.submitter }}">{{ post.submitter }}</a></p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="content">
|
|
||||||
{{ post.text | truncate(60) }}
|
|
||||||
<p><strong>{{ post.received_wow }} WOW received</strong></p>
|
|
||||||
<time datetime="2016-1-1">{{ post.timestamp.year }}-{{ post.timestamp.month }}-{{ post.timestamp.day }} {{ post.timestamp.hour }}:{{ post.timestamp.minute }} UTC</time>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% else %}
|
|
||||||
<p>No posts yet!</p>
|
|
||||||
{% endif %}
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{% if total_pages %}
|
|
||||||
<nav class="pagination is-centered pb-4" role="navigation" aria-label="pagination">
|
|
||||||
<ul class="pagination-list">
|
|
||||||
{% for p in range(1, total_pages + 1) %}
|
|
||||||
<a href="{% if request.args.submitter %}/?submitter={{ request.args.submitter }}&{% else %}/?{% endif %}page={{ p }}" class="pagination-link {% if p == page %}current-page-btn{% endif %}">{{ p }}</a>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
rsync -avzP australia:/opt/suchwow/data/uploads/ data/uploads/
|
||||||
|
rsync -avzP australia:/opt/suchwow/data/sqlite.db data/sqlite.db
|
||||||
|
rsync -avzP australia:/opt/suchwow/data/migrate_data.pkl data/migrate_data.pkl
|
||||||
|
.venv/bin/python3 import.py
|
||||||
|
|
Loading…
Reference in New Issue