From ecf3a01dcb004a2a1b2f703f735a7c61f07a9295 Mon Sep 17 00:00:00 2001 From: Sander Ferdinand Date: Thu, 25 Oct 2018 20:47:31 +0200 Subject: [PATCH 1/5] Add CORS for the API endpoints --- funding/bin/utils_request.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/funding/bin/utils_request.py b/funding/bin/utils_request.py index d7e968a..26f1dab 100644 --- a/funding/bin/utils_request.py +++ b/funding/bin/utils_request.py @@ -1,10 +1,11 @@ from datetime import datetime -from flask import session, g +from flask import session, g, request import settings from funding.bin.utils import Summary from funding.factory import app, db_session from funding.orm.orm import Proposal, User, Comment + @app.context_processor def templating(): from flask.ext.login import current_user @@ -19,15 +20,21 @@ def templating(): recent_comments=recent_comments, newest_users=newest_users) + @app.before_request def before_request(): pass + @app.after_request def after_request(res): if hasattr(g, 'funding_prices'): delattr(g, 'funding_prices') res.headers.add('Accept-Ranges', 'bytes') + + if request.full_path.startswith('/api/'): + res.headers.add('Access-Control-Allow-Origin', '*') + if settings.DEBUG: res.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate' res.headers['Pragma'] = 'no-cache' @@ -35,10 +42,12 @@ def after_request(res): res.headers['Cache-Control'] = 'public, max-age=0' return res + @app.teardown_appcontext def shutdown_session(**kwargs): db_session.remove() + @app.errorhandler(404) def error(err): - return 'Error', 404 \ No newline at end of file + return 'Error', 404 From 80c79966a558f850d622803a1a8c47c3ab1a8b66 Mon Sep 17 00:00:00 2001 From: Sander Ferdinand Date: Thu, 25 Oct 2018 20:58:51 +0200 Subject: [PATCH 2/5] Optional parameters for the Daemon class --- funding/bin/daemon.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/funding/bin/daemon.py b/funding/bin/daemon.py index cc89bbc..06126ca 100644 --- a/funding/bin/daemon.py +++ b/funding/bin/daemon.py @@ -6,10 +6,14 @@ from funding.orm.orm import User class Daemon: - def __init__(self): - self.url = settings.RPC_LOCATION - self.username = settings.RPC_USERNAME - self.password = settings.RPC_PASSWORD + def __init__(self, url=None, username=None, password=None): + if url is None: + self.url = settings.RPC_LOCATION + if username is None: + self.username = settings.RPC_USERNAME + if password is None: + self.password = settings.RPC_PASSWORD + self.headers = {"User-Agent": "Mozilla"} def create_address(self, account_index, label_name): From 25caa397fdcebdaef7d2aac77017fd7df610694a Mon Sep 17 00:00:00 2001 From: Sander Ferdinand Date: Thu, 25 Oct 2018 22:26:23 +0200 Subject: [PATCH 3/5] Jinja2 macro for transaction items --- .../proposal/macros/transaction.html | 28 ++++++++++++++ funding/templates/proposal/proposal.html | 38 ++----------------- 2 files changed, 31 insertions(+), 35 deletions(-) create mode 100644 funding/templates/proposal/macros/transaction.html diff --git a/funding/templates/proposal/macros/transaction.html b/funding/templates/proposal/macros/transaction.html new file mode 100644 index 0000000..3b2866e --- /dev/null +++ b/funding/templates/proposal/macros/transaction.html @@ -0,0 +1,28 @@ +{% macro tx_item(tx) %} +
  • + + {{tx['datetime'].strftime('%Y-%m-%d %H:%M')}} + + + + Blockheight: {{tx['height']}} +
    + + {{tx['txid'][:32]}}... + + {% if tx['type'] == 'in' %} + + + {% else %} + - + {% endif %} + + {{tx['amount_human']|round(3)}} WOW + + {% if 'amount_usd' in tx %} + + ➞ $ {{tx['amount_usd']}} + + {% endif %} + +
  • +{% endmacro %} diff --git a/funding/templates/proposal/proposal.html b/funding/templates/proposal/proposal.html index bcd7cec..bff2ed4 100644 --- a/funding/templates/proposal/proposal.html +++ b/funding/templates/proposal/proposal.html @@ -165,12 +165,7 @@ {% include 'comments.html' %} - + {% from 'proposal/macros/transaction.html' import tx_item %} {% if proposal.balance['txs'] %}
    @@ -180,20 +175,7 @@
      {% for tx in proposal.balance['txs'] %} -
    • - {{tx['datetime'].strftime('%Y-%m-%d %H:%M')}} - Blockheight: {{tx['height']}} -
      - {{tx['txid'][:32]}}... - - + {{tx['amount_human']|round(3)}} WOW - {% if 'amount_usd' in tx %} - - ➞ $ {{tx['amount_usd']}} - - {% endif %} - -
    • + {{ tx_item(tx) }} {% endfor %}
    @@ -203,7 +185,6 @@ {% endif %} - {% if proposal.spends['txs'] %}
    @@ -212,20 +193,7 @@
      {% for tx in proposal.spends['txs'] %} -
    • - {{tx['datetime'].strftime('%Y-%m-%d %H:%M')}} - Blockheight: {{tx['height']}} -
      - {{tx['txid'][:32]}}... - - - {{tx['amount_human']|round(3)}} WOW - {% if 'amount_usd' in tx %} - - ➞ $ {{tx['amount_usd']}} - - {% endif %} - -
    • + {{ tx_item(tx) }} {% endfor %}
    From e94176215c60711876c4fba8da29bf51a2484b31 Mon Sep 17 00:00:00 2001 From: Sander Ferdinand Date: Thu, 25 Oct 2018 22:46:28 +0200 Subject: [PATCH 4/5] Added the development fund page --- funding/bin/daemon.py | 46 ++++++++++++++++++++++++------- funding/routes.py | 30 ++++++++++++++++++++ funding/static/css/wow.css | 41 ++++++++++++++++++++++++++++ funding/templates/devfund.html | 50 ++++++++++++++++++++++++++++++++++ settings.py_example | 6 ++++ 5 files changed, 163 insertions(+), 10 deletions(-) create mode 100644 funding/templates/devfund.html diff --git a/funding/bin/daemon.py b/funding/bin/daemon.py index 06126ca..5736a63 100644 --- a/funding/bin/daemon.py +++ b/funding/bin/daemon.py @@ -1,12 +1,18 @@ -import settings +from datetime import datetime + import requests from requests.auth import HTTPDigestAuth +import settings from funding.orm.orm import User class Daemon: def __init__(self, url=None, username=None, password=None): + self.url = url + self.username = username + self.password = password + if url is None: self.url = settings.RPC_LOCATION if username is None: @@ -86,14 +92,12 @@ class Daemon: return def get_transfers_in(self, proposal): - daemon = Daemon() - - account = daemon.get_accounts(proposal.id) + account = self.get_accounts(proposal.id) if not account: raise Exception('wallet error; pid not found found') index = account['account_index'] - address = daemon.get_address(index, proposal_id=proposal.id) + address = self.get_address(index, proposal_id=proposal.id) if not address: print('Could not fetch transfers_in for proposal id %d' % proposal.id) return {'sum': [], 'txs': []} @@ -119,16 +123,38 @@ class Daemon: 'sum': sum([float(z['amount'])/1e11 for z in txs]), 'txs': txs } - - def get_transfers_out(self, proposal): - daemon = Daemon() - account = daemon.get_accounts(proposal.id) + def get_transfers_in_simple(self): + data = { + "method": "get_transfers", + "params": {"pool": True, "in": True}, + "jsonrpc": "2.0", + "id": "0", + } + + data = self._make_request(data) + data = data['result'] + data = data.get('in', []) + data.get('pool', []) + + for d in data: + d['datetime'] = datetime.fromtimestamp(d['timestamp']) + d['amount_human'] = float(d['amount'])/1e11 + + # most recent tx first + data = sorted(data, key=lambda k: k['datetime'], reverse=True) + + return { + 'sum': sum([float(z['amount'])/1e11 for z in data]), + 'txs': data + } + + def get_transfers_out(self, proposal): + account = self.get_accounts(proposal.id) if not account: raise Exception('wallet error; pid not found found') index = account['account_index'] - address = daemon.get_address(index, proposal_id=proposal.id) + address = self.get_address(index, proposal_id=proposal.id) if not address: print('Could not fetch transfers_in for proposal id %d' % proposal.id) return {'sum': [], 'txs': []} diff --git a/funding/routes.py b/funding/routes.py index 985c681..71af117 100644 --- a/funding/routes.py +++ b/funding/routes.py @@ -1,6 +1,8 @@ from datetime import datetime + from flask import request, redirect, Response, abort, render_template, url_for, flash, make_response, send_from_directory, jsonify from flask.ext.login import login_user , logout_user , current_user, login_required, current_user +from dateutil.parser import parse as dateutil_parse from flask_yoloapi import endpoint, parameter import settings @@ -236,6 +238,34 @@ def proposals(status, page, cat): proposals=proposals, status=status, cat=cat)) +@app.route('/fund') +def devfund(): + from funding.bin.daemon import Daemon + from funding.factory import cache, db_session + + data_default = {'sum': 0, 'txs': []} + cache_key = 'devfund_txs_in' + data = cache.get(cache_key) + if not data: + daemon = Daemon(url=settings.RPC_LOCATION_DEVFUND, + username=settings.RPC_USERNAME_DEVFUND, + password=settings.RPC_PASSWORD_DEVFUND + ) + + txs_in = daemon.get_transfers_in_simple() + if not txs_in['txs']: + cache.set(cache_key, data=data_default, expiry=60) + else: + txs_in['txs'] = txs_in['txs'][:50] # truncate to last 50 + cache.set(cache_key, data=txs_in, expiry=60) + else: + for tx in data['txs']: + tx['datetime'] = dateutil_parse(tx['datetime']) + txs_in = data + + return make_response(render_template('devfund.html', txs_in=txs_in)) + + @app.route('/register', methods=['GET', 'POST']) def register(): if settings.USER_REG_DISABLED: diff --git a/funding/static/css/wow.css b/funding/static/css/wow.css index 50151aa..17d3c3d 100644 --- a/funding/static/css/wow.css +++ b/funding/static/css/wow.css @@ -616,4 +616,45 @@ ul.b { .table-proposal tr { background: #00000005; +} + +.tx_item { + padding-top: 4px; + padding-bottom: 4px; + background: #ffffff80; +} + +.tx_item .amount { + float:right; + font-weight:bold; + color:#890000; +} + +.tx_item .amount.in { + color:#008926; +} + +.tx_item .datetime { + font-size: 14px; + color: #999; +} + +.tx_item .height { + float:right +} + +.tx_item .height b { + font-size:14px; +} + +.page_devfund .tx_item { + padding-left: .85rem; + padding-right: .85rem; +} + +.container>.content h1, +.container>.content h2, +.container>.content h3, +.container>.content h4{ + margin-bottom:20px; } \ No newline at end of file diff --git a/funding/templates/devfund.html b/funding/templates/devfund.html new file mode 100644 index 0000000..b5f84b2 --- /dev/null +++ b/funding/templates/devfund.html @@ -0,0 +1,50 @@ +{% extends "base.html" %} +{% block content %} + +
    +
    +
    +

    Development Fund

    +

    + Ongoing development is supported by donations and sponsorships. +

    +
    +
    +
    +
    +
    +

    Donating Wownero

    +

    + Donations may be sent to: Wo3MWeKwtA918DU4c69hVSNgejdWFCRCuWjShRY66mJkU2Hv58eygJWDJS1MNa2Ge5M1WjUkGHuLqHkweDxwZZU42d16v94mP +

    + + +

    View-only wallet

    +

    + e62e40bfd5ca7e3a7f199602a3c97df511780489e1c1861884b00c28abaea406 +

    +

    Donations

    +

    + Current balance: {{ txs_in['sum']|round(4) }} WOW +

    +

    + 50 most recent donations: +

    + {% from 'proposal/macros/transaction.html' import tx_item %} + +
      + {% for tx in txs_in['txs'] %} + {{ tx_item(tx) }} + {% endfor %} +
    +
    + + {% include 'sidebar.html' %} + +
    + +
    + +
    + +{% endblock %} diff --git a/settings.py_example b/settings.py_example index 37c6c2d..0132121 100644 --- a/settings.py_example +++ b/settings.py_example @@ -39,6 +39,12 @@ RPC_LOCATION = "http://{host}:{rpc_port}/json_rpc".format(host=RPC_HOST, rpc_por RPC_USERNAME = "" RPC_PASSWORD = "" +RPC_HOST_DEVFUND = '127.0.0.1' +RPC_PORT_DEVFUND = '45679' +RPC_LOCATION_DEVFUND = "http://{host}:{rpc_port}/json_rpc".format(host=RPC_HOST, rpc_port=RPC_PORT) +RPC_USERNAME_DEVFUND = None +RPC_PASSWORD_DEVFUND = None + FUNDING_CATEGORIES = [ 'wallets', 'marketing', From e3252d007155c66f3fcf5d84c4d77d328ac7c578 Mon Sep 17 00:00:00 2001 From: Sander Ferdinand Date: Thu, 25 Oct 2018 23:01:07 +0200 Subject: [PATCH 5/5] Adding donate page to the navbar --- funding/routes.py | 4 ++-- funding/static/css/wow.css | 5 ----- funding/templates/api.html | 3 ++- funding/templates/devfund.html | 4 ++-- funding/templates/navbar.html | 3 +++ 5 files changed, 9 insertions(+), 10 deletions(-) diff --git a/funding/routes.py b/funding/routes.py index 71af117..d96ca25 100644 --- a/funding/routes.py +++ b/funding/routes.py @@ -238,8 +238,8 @@ def proposals(status, page, cat): proposals=proposals, status=status, cat=cat)) -@app.route('/fund') -def devfund(): +@app.route('/donate') +def donate(): from funding.bin.daemon import Daemon from funding.factory import cache, db_session diff --git a/funding/static/css/wow.css b/funding/static/css/wow.css index 17d3c3d..e6b5d74 100644 --- a/funding/static/css/wow.css +++ b/funding/static/css/wow.css @@ -647,11 +647,6 @@ ul.b { font-size:14px; } -.page_devfund .tx_item { - padding-left: .85rem; - padding-right: .85rem; -} - .container>.content h1, .container>.content h2, .container>.content h3, diff --git a/funding/templates/api.html b/funding/templates/api.html index 753765b..4a5e504 100644 --- a/funding/templates/api.html +++ b/funding/templates/api.html @@ -4,7 +4,8 @@
    -

    API documentation

    +

    API documentation

    +

    Requests are made using standard HTTP and responses are returned in JSON format.

    diff --git a/funding/templates/devfund.html b/funding/templates/devfund.html index b5f84b2..bf590b9 100644 --- a/funding/templates/devfund.html +++ b/funding/templates/devfund.html @@ -6,7 +6,7 @@

    Development Fund

    - Ongoing development is supported by donations and sponsorships. + Ongoing development is supported by donations and sponsorships *cough*.

    @@ -15,7 +15,7 @@

    Donating Wownero

    - Donations may be sent to: Wo3MWeKwtA918DU4c69hVSNgejdWFCRCuWjShRY66mJkU2Hv58eygJWDJS1MNa2Ge5M1WjUkGHuLqHkweDxwZZU42d16v94mP + Donations may be send to: Wo3MWeKwtA918DU4c69hVSNgejdWFCRCuWjShRY66mJkU2Hv58eygJWDJS1MNa2Ge5M1WjUkGHuLqHkweDxwZZU42d16v94mP

    diff --git a/funding/templates/navbar.html b/funding/templates/navbar.html index da1f024..a231ed9 100644 --- a/funding/templates/navbar.html +++ b/funding/templates/navbar.html @@ -25,6 +25,9 @@ +