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 }}
-
+{% 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
+
+{% for s in platform_subs %}
+ -
+ Platform Subscription ID: {{ s.id }}
+ Created: {{ s.create_date }}
+ TX ID: {{ s.tx.tx_id }}
+ Paid: {{ s.tx.atomic_xmr | from_atomic }} XMR
+ Term Length: {{ s.term_hours }} hours ({{ s.hours_until_content_hidden }} hours)
+ Hide Content: {{ s.hours_until_content_hidden }} hours
+ Archive Content: {{ s.hours_until_content_archived }} hours
+ Active Subscription: {{ s.is_active }}
+
+
+{% endfor %}
+
+
+Content Subscriptions
+{% if not content_subs %}
+ No subscriptions to your content yet.
+ {% include 'includes/form.html' %}
+{% else %}
+
+ {% for s in content_subs %}
+ -
+ Content Subscription ID: {{ s.id }}
+ Created: {{ s.create_date }}
+ Price: {{ s.atomic_xmr | from_atomic }} XMR
+ Term Length: {{ s.number_hours }} hours
+ Active Subscriptions: {{ s.get_active_subscriptions().count() }}
+
+ {% endfor %}
+
+{% 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 @@
+
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 %}