Fixed a caching bug, moved summary/stats to Redis and included code for a WOW->BTC->USD calculator

update-readme
Sander Ferdinand 6 years ago
parent a40cd265af
commit 35fa3f51c6

@ -1,8 +1,93 @@
from flask.json import JSONEncoder
from datetime import datetime, date
import requests
from flask import g
from flask.json import JSONEncoder
import settings
def json_encoder(obj):
if isinstance(obj, (datetime, date)):
return obj.isoformat()
raise TypeError ("Type %s not serializable" % type(obj))
class Summary:
@staticmethod
def fetch_prices():
if hasattr(g, 'wowfunding_prices') and g.wow_prices:
return g.wow_prices
from wowfunding.factory import cache
cache_key = 'wowfunding_prices'
data = cache.get(cache_key)
if data:
return data
data = {
'wow-btc': price_tradeogre_wow_btc(),
'btc-usd': price_cmc_btc_usd()
}
cache.set(cache_key, data=data, expiry=7200)
g.wow_prices = data
return data
@staticmethod
def fetch_stats(purge=False):
from wowfunding.factory import db_session
from wowfunding.orm.orm import Proposal, User, Comment
from wowfunding.factory import cache
cache_key = 'wowfunding_stats'
data = cache.get(cache_key)
if data and not purge:
return data
categories = settings.FUNDING_CATEGORIES
statuses = settings.FUNDING_STATUSES.keys()
for cat in categories:
q = db_session.query(Proposal)
q = q.filter(Proposal.category == cat)
res = q.count()
data.setdefault('cats', {})
data['cats'][cat] = res
for status in statuses:
q = db_session.query(Proposal)
q = q.filter(Proposal.status == status)
res = q.count()
data.setdefault('statuses', {})
data['statuses'][status] = res
data.setdefault('users', {})
data['users']['count'] = db_session.query(User.id).count()
cache.set(cache_key, data=data, expiry=300)
return data
def price_cmc_btc_usd():
headers = {'User-Agent': 'Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0'}
try:
r = requests.get('https://api.coinmarketcap.com/v2/ticker/1/?convert=USD', headers=headers)
r.raise_for_status()
return r.json().get('data', {}).get('quotes', {}).get('USD', {}).get('price')
except:
return
def price_tradeogre_wow_btc():
headers = {'User-Agent': 'Mozilla/5.0 (Android 4.4; Mobile; rv:41.0) Gecko/41.0 Firefox/41.0'}
try:
r = requests.get('https://tradeogre.com/api/v1/ticker/BTC-WOW', headers=headers)
r.raise_for_status()
return float(r.json().get('high'))
except:
return
def wow_to_usd(wows: float, usd_per_btc: float, btc_per_wow: float):
try:
return round(usd_per_btc / (1.0 / (wows * btc_per_wow)), 2)
except:
pass

@ -2,59 +2,33 @@ from datetime import datetime
from flask import session, g
import settings
from wowfunding.factory import app, db_session, summary_data
from wowfunding.bin.utils import Summary
from wowfunding.factory import app, db_session
from wowfunding.orm.orm import Proposal, User, Comment
@app.context_processor
def templating():
global summary_data
from flask.ext.login import current_user
recent_comments = db_session.query(Comment).order_by(Comment.date_added.desc()).limit(3).all()
summary_data = Summary.fetch_stats()
return dict(logged_in=current_user.is_authenticated,
current_user=current_user,
funding_categories=settings.FUNDING_CATEGORIES,
funding_statuses=settings.FUNDING_STATUSES,
summary_data=summary_data[1],
summary_data=summary_data,
recent_comments=recent_comments)
def fetch_summary(purge=False):
global summary_data
if summary_data and not purge:
if (datetime.now() - summary_data[0]).total_seconds() <= 120:
return
data = {}
categories = settings.FUNDING_CATEGORIES
statuses = settings.FUNDING_STATUSES.keys()
for cat in categories:
q = db_session.query(Proposal)
q = q.filter(Proposal.category == cat)
res = q.count()
data.setdefault('cats', {})
data['cats'][cat] = res
for status in statuses:
q = db_session.query(Proposal)
q = q.filter(Proposal.status == status)
res = q.count()
data.setdefault('statuses', {})
data['statuses'][status] = res
data.setdefault('users', {})
data['users']['count'] = db_session.query(User.id).count()
summary_data = [datetime.now(), data]
@app.before_request
def before_request():
fetch_summary()
pass
@app.after_request
def after_request(res):
if hasattr(g, 'wowfunding_prices'):
delattr(g, 'wowfunding_prices')
res.headers.add('Accept-Ranges', 'bytes')
if settings.DEBUG:
res.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'

@ -13,7 +13,8 @@ def redis_args():
"port": settings.REDIS_PORT,
'socket_connect_timeout': 2,
'socket_timeout': 2,
'retry_on_timeout': True
'retry_on_timeout': True,
'decode_responses': True
}
if settings.REDIS_PASSWD:
args["password"] = settings.REDIS_PASSWD
@ -41,7 +42,7 @@ class JsonRedis(RedisSessionInterface):
def __init__(self, key_prefix, use_signer=False, decode_responses=True):
super(JsonRedis, self).__init__(
redis=redis.Redis(decode_responses=decode_responses, **redis_args()),
redis=redis.Redis(**redis_args()),
key_prefix=key_prefix,
use_signer=use_signer)

@ -8,7 +8,6 @@ sentry = None
cache = None
db_session = None
bcrypt = None
summary_data = []
def create_app():
@ -53,8 +52,5 @@ def create_app():
from wowfunding import api
from wowfunding.bin import utils_request
# generate some statistics
utils_request.fetch_summary()
app.app_context().push()
return app

@ -146,6 +146,14 @@ class Proposal(base):
return result
@property
def funds_target_usd(self):
from wowfunding.bin.utils import Summary, wow_to_usd
prices = Summary.fetch_prices()
if not prices:
return
return wow_to_usd(wows=self.funds_target, btc_per_wow=prices['wow-btc'], usd_per_btc=prices['btc-usd'])
@property
def comment_count(self):
from wowfunding.factory import db_session
@ -176,6 +184,7 @@ class Proposal(base):
of this proposal. It uses Redis cache to not spam the
wownerod too much. Returns a nice dictionary containing
all relevant proposal funding info"""
from wowfunding.bin.utils import Summary, wow_to_usd
from wowfunding.factory import cache, db_session
rtn = {'sum': 0.0, 'txs': [], 'pct': 0.0}
@ -193,7 +202,10 @@ class Proposal(base):
print('error; get_transfers; %d' % self.id)
return rtn
prices = Summary.fetch_prices()
for tx in data['txs']:
if prices:
tx['amount_usd'] = wow_to_usd(wows=tx['amount_human'], btc_per_wow=prices['wow-btc'], usd_per_btc=prices['btc-usd'])
tx['datetime'] = datetime.fromtimestamp(tx['timestamp'])
if data.get('sum', 0.0):

@ -160,8 +160,8 @@ def proposal_api_add(title, content, pid, funds_target, addr_receiving, category
db_session.flush()
# reset cached statistics
from wowfunding.bin import utils_request
utils_request.fetch_summary(purge=True)
from wowfunding.bin.utils import Summary
Summary.fetch_stats(purge=True)
return make_response(jsonify({'url': url_for('proposal', pid=p.id)}))

@ -1,7 +1,6 @@
{% extends "base.html" %}
{% block content %}
<!-- Page Content -->
<div class="container" style="margin-bottom:140px;">
{% include 'messages.html' %}
@ -35,6 +34,9 @@
<br>
<span>
Target: <b>{{proposal.funds_target|round}}</b> WOW
{% if proposal.funds_target_usd %}
<small>➞ {{proposal.funds_target_usd}} USD</small>
{% endif %}
</span>
{% endif %}
</p>
@ -60,7 +62,13 @@
<tbody>
<tr>
<td>Target</td>
<td><span class="badge">{{proposal.funds_target|round}} WOW</span></td>
<td>
<span class="badge">{{proposal.funds_target|round}} WOW
{% if proposal.funds_target_usd %}
<small>➞ {{proposal.funds_target_usd}} USD</small>
{% endif %}
</span>
</td>
</tr>
<tr>
<td>Progress</td>
@ -143,7 +151,14 @@
<span style="float:right"><b>Blockheight</b>: {{tx['height']}}</span>
<br>
<a target="_blank" href="https://explore.wownero.com/tx/{{tx['txid']}}">{{tx['txid'][:32]}}...</a>
<span style="float:right;color:#008926;font-weight:bold;">+ {{tx['amount_human']|round(2)}} WOW</span>
<span style="float:right;color:#008926;font-weight:bold;">
+ {{tx['amount_human']|round(2)}} WOW
{% if 'amount_usd' in tx %}
<small style="color: black">
➞ $ {{tx['amount_usd']}}
</small>
{% endif %}
</span>
</li>
{% endfor %}
</ul>

Loading…
Cancel
Save