From 3da8c9951d0c1b36c462b9611959ef3c5a32b71f Mon Sep 17 00:00:00 2001 From: lza_menace Date: Sat, 1 Oct 2022 22:48:45 -0700 Subject: [PATCH] setting up subscrption management for creators --- xmrbackers/config.py | 7 +- xmrbackers/filters.py | 4 +- xmrbackers/forms.py | 7 +- xmrbackers/models.py | 33 ++++++-- xmrbackers/routes/creator.py | 37 +++++++++ xmrbackers/routes/main.py | 19 ++--- xmrbackers/templates/creator/join.html | 19 +---- .../creator/manage_subscriptions.html | 49 ++++++++++++ xmrbackers/templates/includes/base.html | 6 +- xmrbackers/templates/includes/debug.html | 11 +++ xmrbackers/templates/includes/form.html | 18 +++++ xmrbackers/templates/includes/header.html | 8 -- xmrbackers/templates/index.html | 80 ++++++++----------- 13 files changed, 201 insertions(+), 97 deletions(-) create mode 100644 xmrbackers/templates/creator/manage_subscriptions.html create mode 100644 xmrbackers/templates/includes/debug.html create mode 100644 xmrbackers/templates/includes/form.html diff --git a/xmrbackers/config.py b/xmrbackers/config.py index e091f06..ae79796 100644 --- a/xmrbackers/config.py +++ b/xmrbackers/config.py @@ -15,10 +15,9 @@ SERVER_NAME = getenv('SERVER_NAME', 'localhost:5000') PLATFORM_WALLET = getenv('PLATFORM_WALLET') # Constants - here for easy template references -CREATOR_SUBSCRIPTION_TERM = 60 # term of how long creator subscriptions are valid for -CREATOR_SUBSCRIPTION_GRACE = 10 # grace period after expiration of creator subscriptions until content is hidden -CREATOR_SUBSCRIPTION_DELETE = 20 # time after grace period until content is deleted -CREATOR_SUBSCRIPTION_FEE_XMR = .15 # default flat rate fee in XMR for creator subscriptions +CREATOR_SUBSCRIPTION_TERM = 60 # 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 = .3 # XMR flat rate fee for creator subscriptions # Crypto RPC XMR_WALLET_PASS = getenv('XMR_WALLET_PASS') diff --git a/xmrbackers/filters.py b/xmrbackers/filters.py index 0c30283..8f8744b 100644 --- a/xmrbackers/filters.py +++ b/xmrbackers/filters.py @@ -1,7 +1,7 @@ from datetime import datetime import arrow -import monero +from monero import numbers from quart import Blueprint, current_app @@ -22,4 +22,4 @@ def xmr_block_explorer(v): @bp.app_template_filter('from_atomic') def from_atomic(amt): - return monero.numbers.from_atomic(amt) + return numbers.as_monero(numbers.from_atomic(amt)) diff --git a/xmrbackers/forms.py b/xmrbackers/forms.py index 7c193f2..f1600ea 100644 --- a/xmrbackers/forms.py +++ b/xmrbackers/forms.py @@ -1,5 +1,5 @@ from flask_wtf import FlaskForm -from wtforms import StringField +from wtforms import StringField, FloatField from wtforms.validators import DataRequired, ValidationError from monero.address import address @@ -35,3 +35,8 @@ class UserChallenge(FlaskForm): class ConfirmSubscription(FlaskForm): tx_id = StringField('TX ID:', validators=[DataRequired()], render_kw={"placeholder": "TX ID", "class": "form-control", "type": "text"}) tx_key = StringField('TX Key:', validators=[DataRequired()], render_kw={"placeholder": "TX Key", "class": "form-control", "type": "text"}) + + +class CreateSubscription(FlaskForm): + price_xmr = FloatField('Price (XMR):', validators=[DataRequired()], render_kw={'placeholder': '.5', 'class': 'form-control', 'type': 'text'}) + number_days = FloatField('Length (Days)', validators=[DataRequired()], render_kw={'placeholder': '30', 'class': 'form-control', 'type': 'text'}) diff --git a/xmrbackers/models.py b/xmrbackers/models.py index bdeb197..4d065db 100644 --- a/xmrbackers/models.py +++ b/xmrbackers/models.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta from enum import IntEnum, unique from typing import List from secrets import token_urlsafe @@ -116,7 +116,29 @@ 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=config.CREATOR_SUBSCRIPTION_DELETE * 24) + delete_hours = pw.IntegerField(default=0) # delete me + + @property + def grace_start_date(self): + # Hide content after this date if no new valid platform subscription + return self.create_date + timedelta(hours=self.term_hours) + + @property + def delete_start_date(self): + # Archive content after this date if no new valid platform subscription + return self.grace_start_date + timedelta(hours=self.grace_hours) + + @property + def hours_until_content_hidden(self): + return self.grace_start_date - datetime.utcnow() + + @property + def hours_until_content_archived(self): + return self.delete_start_date - datetime.utcnow() + + @property + def is_active(self): + return self.grace_start_date > datetime.utcnow() class Meta: database = db @@ -136,9 +158,10 @@ class SubscriptionMeta(pw.Model): number_hours = pw.IntegerField() wallet_address = pw.CharField() - def get_end_date(self) -> datetime: - # some timedelta shiz - pass + def get_active_subscriptions(self): + return Subscription.select().where( + Subscription.meta == self + ) class Meta: database = db diff --git a/xmrbackers/routes/creator.py b/xmrbackers/routes/creator.py index e695605..0d7c14a 100644 --- a/xmrbackers/routes/creator.py +++ b/xmrbackers/routes/creator.py @@ -122,6 +122,43 @@ async def show(handle): ) +@bp.route('/creator/subscriptions/manage', methods=['GET', 'POST']) +@login_required +async def manage_subscriptions(): + form = forms.CreateSubscription() + if UserRole.creator not in current_user.roles: + await flash('You are not a creator!', 'warning') + return redirect(url_for('main.index')) + + platform_subs = CreatorSubscription.select().where( + CreatorSubscription.user == current_user + ).order_by(CreatorSubscription.create_date.desc()) + content_subs = SubscriptionMeta.select().where( + SubscriptionMeta.user == current_user + ).order_by(SubscriptionMeta.create_date.desc()) + subscribers = Subscription.select().where( + Subscription.meta.in_(content_subs) + ).order_by(Subscription.subscribe_date.desc()) + + if form.validate_on_submit(): + s = SubscriptionMeta( + user=current_user, + atomic_xmr=to_atomic(form.price_xmr.data), + number_hours=form.number_days.data * 24.0, + wallet_address=current_user.wallet_address + ) + s.save() + await flash('posting form data', 'success') + + return await render_template( + 'creator/manage_subscriptions.html', + platform_subs=platform_subs, + content_subs=content_subs, + subscribers=subscribers, + form=form + ) + + # @bp.route('/creator//subscription') # async def subscription(username): # user = User.select().where(User.username == username) diff --git a/xmrbackers/routes/main.py b/xmrbackers/routes/main.py index 8030998..1ea1d94 100644 --- a/xmrbackers/routes/main.py +++ b/xmrbackers/routes/main.py @@ -8,26 +8,23 @@ bp = Blueprint('main', 'main') @bp.route('/') async def index(): - feed = None + feed = dict() + new_creators = User.select().where( + User.roles.contains_any(UserRole.creator) + ).order_by(User.register_date.desc()).execute() + feed['new_creators'] = new_creators if current_user.is_authenticated: - new_creators = User.select().where( - User.roles.contains_any(UserRole.creator) - ).order_by(User.register_date.desc()).execute() active_subscriptions = Subscription.select().where( Subscription.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() - feed = { - 'new_creators': new_creators, - 'new_posts': new_posts, - 'active_subscriptions': active_subscriptions - } + feed['new_posts'] = new_posts return await render_template( - 'index.html', + 'index.html', feed=feed ) - \ No newline at end of file diff --git a/xmrbackers/templates/creator/join.html b/xmrbackers/templates/creator/join.html index c1cad6c..79c02f2 100644 --- a/xmrbackers/templates/creator/join.html +++ b/xmrbackers/templates/creator/join.html @@ -8,23 +8,6 @@

Platform Wallet: {{ config.PLATFORM_WALLET }}

-
- {% for f in form %} - {% if f.name == 'csrf_token' %} - {{ f }} - {% else %} -
- {{ f.label }} - {{ f }} -
- {% endif %} - {% endfor %} -
    - {% for field, errors in form.errors.items() %} -
  • {{ form[field].label }}: {{ ', '.join(errors) }}
  • - {% endfor %} -
- -
+{% include 'includes/form.html' %} {% endblock %} diff --git a/xmrbackers/templates/creator/manage_subscriptions.html b/xmrbackers/templates/creator/manage_subscriptions.html new file mode 100644 index 0000000..b5ad478 --- /dev/null +++ b/xmrbackers/templates/creator/manage_subscriptions.html @@ -0,0 +1,49 @@ +{% extends 'includes/base.html' %} + +{% block content %} + +

Platform Subscriptions

+ + +

Content Subscriptions

+{% if not content_subs %} +

No subscriptions to your content yet.

+ {% include 'includes/form.html' %} +{% else %} + +{% endif %} + +

Subscribers

+{% if not subscribers %} +

No subscribers yet.

+{% else %} + {% for subscriber in subscribers %} + {{ subscriber }} + {% endfor %} +{% endif %} + +{% endblock %} diff --git a/xmrbackers/templates/includes/base.html b/xmrbackers/templates/includes/base.html index 4959b1d..9a642d8 100644 --- a/xmrbackers/templates/includes/base.html +++ b/xmrbackers/templates/includes/base.html @@ -2,9 +2,9 @@ {% include 'includes/head.html' %} - {% include 'includes/header.html' %} - {% block content %}{% endblock %} - {% include 'includes/footer.html' %} + {% include 'includes/header.html' %} + {% block content %}{% endblock %} + {% include 'includes/debug.html' %} {% include 'includes/scripts.html' %} diff --git a/xmrbackers/templates/includes/debug.html b/xmrbackers/templates/includes/debug.html new file mode 100644 index 0000000..98bc6b7 --- /dev/null +++ b/xmrbackers/templates/includes/debug.html @@ -0,0 +1,11 @@ +
+ +{% if config.DEBUG and current_user.is_authenticated %} +

Debug

+

+ Authenticated: {{ current_user.is_authenticated }}
+ Username: {{ current_user.handle }}
+ Email: {{ current_user.email }}
+ Wallet Address: {{ current_user.wallet_address }}
+

+{% endif %} diff --git a/xmrbackers/templates/includes/form.html b/xmrbackers/templates/includes/form.html new file mode 100644 index 0000000..98f3518 --- /dev/null +++ b/xmrbackers/templates/includes/form.html @@ -0,0 +1,18 @@ +
+ {% for f in form %} + {% if f.name == 'csrf_token' %} + {{ f }} + {% else %} +
+ {{ f.label }} + {{ f }} +
+ {% endif %} + {% endfor %} +
    + {% for field, errors in form.errors.items() %} +
  • {{ form[field].label }}: {{ ', '.join(errors) }}
  • + {% endfor %} +
+ +
diff --git a/xmrbackers/templates/includes/header.html b/xmrbackers/templates/includes/header.html index cf52208..6bd6e2e 100644 --- a/xmrbackers/templates/includes/header.html +++ b/xmrbackers/templates/includes/header.html @@ -7,16 +7,8 @@
  • Logout
  • - {% if current_user.is_authenticated %} -

    Authenticated: {{ current_user.is_authenticated }}

    -

    Username: {{ current_user.handle }}

    -

    Email: {{ current_user.email }}

    -

    Wallet Address: {{ current_user.wallet_address }}

    - {% endif %} -
    - {% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} diff --git a/xmrbackers/templates/index.html b/xmrbackers/templates/index.html index 6842a3d..7f21435 100644 --- a/xmrbackers/templates/index.html +++ b/xmrbackers/templates/index.html @@ -1,45 +1,35 @@ - - - - {% include 'includes/head.html' %} - - -
    - - {% include 'includes/header.html' %} - - {% if current_user.is_authenticated %} - {% if 2 not in current_user.roles %} - Become a Creator - {% endif %} - {% endif %} - - {% if feed %} - {% if feed['new_creators'] %} -

    New Creators

    - {% for c in feed['new_creators'] %} -

    {{ c.handle }}

    - {% endfor %} - {% endif %} - {% if feed['new_posts'] %} -

    New Posts

    - {% for p in feed['new_posts'] %} -

    {{ p.id }}

    - {% endfor %} - {% endif %} - {% if feed['active_subscriptions'] %} -

    Active Subscriptions

    - {% for s in feed['active_subscriptions'] %} -

    {{ s.id }}

    - {% endfor %} - {% endif %} - {% endif %} - - {% include 'includes/footer.html' %} - -
    - - {% include 'includes/scripts.html' %} - - - +{% extends 'includes/base.html' %} + +{% block content %} + +{% if current_user.is_authenticated %} + {% if 2 not in current_user.roles %} + Become a Creator + {% else %} + Manage Subscriptions + {% endif %} +{% endif %} + +{% if feed %} +

    Feed

    + {% if feed['new_creators'] %} +

    New Creators

    + {% for c in feed['new_creators'] %} +

    {{ c.handle }}

    + {% endfor %} + {% endif %} + {% if feed['new_posts'] %} +

    New Posts

    + {% for p in feed['new_posts'] %} +

    {{ p.id }}

    + {% endfor %} + {% endif %} + {% if feed['active_subscriptions'] %} +

    Active Subscriptions

    + {% for s in feed['active_subscriptions'] %} +

    {{ s.id }}

    + {% endfor %} + {% endif %} +{% endif %} + +{% endblock %}