From ad6bad9b5ed64e8de839532fefe2dd4364dc3258 Mon Sep 17 00:00:00 2001 From: lza_menace Date: Tue, 4 Oct 2022 10:37:24 -0700 Subject: [PATCH] dynamic subscription fees --- xmrbackers/config.py | 2 ++ xmrbackers/models.py | 62 ++++++++++++++++++++++++++++++++---- xmrbackers/routes/creator.py | 10 +++--- xmrbackers/routes/main.py | 12 +++---- 4 files changed, 70 insertions(+), 16 deletions(-) diff --git a/xmrbackers/config.py b/xmrbackers/config.py index c4bf186..2e3ce58 100644 --- a/xmrbackers/config.py +++ b/xmrbackers/config.py @@ -18,6 +18,8 @@ PLATFORM_WALLET = getenv('PLATFORM_WALLET') CREATOR_SUBSCRIPTION_TERM = 30 # days term of how long creator subscriptions are valid for until content is hidden CREATOR_SUBSCRIPTION_GRACE = 21 # days grace period after expiration of creator subscriptions until content is archived CREATOR_SUBSCRIPTION_FEE_XMR = .15 # XMR flat rate fee for creator subscriptions +CREATOR_CONTENT_FEE_PERCENT = 5 # percentage of base fee to charge for content/posts +CREATOR_ACTIVE_SUBSCRIBER_FEE_PERCENT = 10 # percentage of received subscriber fees to charge creator for platform reup # Crypto RPC XMR_WALLET_PASS = getenv('XMR_WALLET_PASS') diff --git a/xmrbackers/models.py b/xmrbackers/models.py index 62f8bf4..995dc36 100644 --- a/xmrbackers/models.py +++ b/xmrbackers/models.py @@ -4,6 +4,7 @@ from typing import List from secrets import token_urlsafe import peewee as pw +from monero.numbers import to_atomic, from_atomic from xmrbackers import config from xmrbackers.helpers import EnumArrayField, EnumIntField @@ -28,12 +29,20 @@ class UserRole(IntEnum): @unique -class ContentType(IntEnum): +class PostType(IntEnum): text = 0 gallery = 1 stream = 2 +@unique +class ContentType(IntEnum): + text = 0 + image = 1 + video = 2 + stream = 3 + + class User(pw.Model): """ User model is for base user management and reporting. @@ -78,6 +87,29 @@ class User(pw.Model): return True return False + def derive_subscription_fees(self): + base_fee_atomic = to_atomic(config.CREATOR_SUBSCRIPTION_FEE_XMR) + posts = Post.select().where(Post.creator == self) + content = Content.select().join(Post).where(Post.creator == self) + content_base_fee_atomic = base_fee_atomic * config.CREATOR_CONTENT_FEE_PERCENT + content_fee_atomic = (posts.count() + content.count()) * content_base_fee_atomic + active_subs = Subscription.select().where( + Subscription.creator == self, + Subscription.is_active == True + ) + received_sub_xmr_atomic = 0 + for sub in active_subs: + received_sub_xmr_atomic += sub.meta.atomic_xmr + + print('User can expect to pay the following fees on their next reup:\nBase Fee: {} XMR\nContent Fees: {} ({} posts, {} uploads)\nSubscriber Fees: {}'.format( + config.CREATOR_SUBSCRIPTION_FEE_XMR, + from_atomic(content_fee_atomic), + posts.count(), + content.count(), + from_atomic(received_sub_xmr_atomic) + )) + + class Meta: database = db @@ -127,7 +159,6 @@ class CreatorSubscription(pw.Model): atomic_xmr = pw.BigIntegerField() term_hours = pw.IntegerField(default=config.CREATOR_SUBSCRIPTION_TERM * 24) grace_hours = pw.IntegerField(default=config.CREATOR_SUBSCRIPTION_GRACE * 24) - delete_hours = pw.IntegerField(default=0) # delete me @property def grace_start_date(self): @@ -188,19 +219,23 @@ class Subscription(pw.Model): id = pw.AutoField() subscribe_date = pw.DateTimeField(default=datetime.utcnow) tx = pw.ForeignKeyField(Transaction) - active = pw.BooleanField(default=True) creator = pw.ForeignKeyField(User) backer = pw.ForeignKeyField(User) meta = pw.ForeignKeyField(SubscriptionMeta) + @property + def is_active(self): + end_date = self.subscribe_date + timedelta(hours=self.meta.number_hours) + return end_date > datetime.utcnow() + class Meta: database = db -class Content(pw.Model): +class Post(pw.Model): """ - Content model represents any uploaded content from a creator which is only - viewable by backers with an active subscription. + Post model represents a post from a creator consisting of Content objects + which is only viewable by backers with an active subscription. """ id = pw.AutoField() post_date = pw.DateTimeField(default=datetime.utcnow) @@ -210,6 +245,21 @@ class Content(pw.Model): last_edit_date = pw.DateTimeField(default=datetime.utcnow) creator = pw.ForeignKeyField(User) + type: List[PostType] = EnumIntField(enum_class=PostType, default=PostType.text) + + class Meta: + database = db + + +class Content(pw.Model): + """ + Content model is any uploaded content from a creator. + """ + id = pw.AutoField() + location = pw.CharField() + upload_date = pw.DateTimeField(default=datetime.utcnow) + post = pw.ForeignKeyField(Post) + type: List[ContentType] = EnumIntField(enum_class=ContentType, default=ContentType.text) class Meta: diff --git a/xmrbackers/routes/creator.py b/xmrbackers/routes/creator.py index 7b5f654..a23f370 100644 --- a/xmrbackers/routes/creator.py +++ b/xmrbackers/routes/creator.py @@ -114,10 +114,12 @@ async def show(handle): await flash('That creator does not exist.', 'warning') return redirect(url_for('main.index')) - posts = Content.select().where( - Content.creator == creator, - Content.hidden == False - ).order_by(Content.post_date.desc()) + creator.derive_subscription_fees() + + posts = Post.select().where( + Post.creator == creator, + Post.hidden == False + ).order_by(Post.post_date.desc()) subscriptions = SubscriptionMeta.select().where( SubscriptionMeta.user == creator ).order_by(SubscriptionMeta.create_date.desc()) diff --git a/xmrbackers/routes/main.py b/xmrbackers/routes/main.py index 1ea1d94..8db35c8 100644 --- a/xmrbackers/routes/main.py +++ b/xmrbackers/routes/main.py @@ -1,7 +1,7 @@ from quart import Blueprint, render_template from flask_login import current_user -from xmrbackers.models import User, Profile, Subscription, Content, UserRole +from xmrbackers.models import * bp = Blueprint('main', 'main') @@ -15,14 +15,14 @@ async def index(): feed['new_creators'] = new_creators if current_user.is_authenticated: active_subscriptions = Subscription.select().where( - Subscription.active == True, + Subscription.is_active == True, Subscription.backer == current_user ).order_by(Subscription.subscribe_date.desc()).execute() feed['active_subscriptions'] = active_subscriptions - new_posts = Content.select().where( - Content.hidden == False, - Content.creator in [c.creator for c in active_subscriptions] - ).order_by(Content.post_date.desc()).execute() + new_posts = Post.select().where( + Post.hidden == False, + Post.creator in [c.creator for c in active_subscriptions] + ).order_by(Post.post_date.desc()).execute() feed['new_posts'] = new_posts return await render_template( 'index.html',