Browse Source

Fixed MutableDict. Redesigned profile,random,settings. Added user bios

master
Brandon Cornejo 11 years ago
parent
commit
23e04edafd
  1. 2
      .gitignore
  2. 2
      __init__.py
  3. 7
      forms.py
  4. 99
      models.py
  5. 1250
      static/country_codes.xml
  6. 38
      static/css/app.css
  7. 2
      static/css/heropedia.css
  8. 5
      static/css/ts3_viewer.css
  9. 9
      teamspeak.py
  10. 87
      templates/hero_random.html
  11. 213
      templates/index.html
  12. 37
      templates/layout.html
  13. 41
      templates/profile.html
  14. 41
      templates/settings.html
  15. 12
      templates/sidenav.html
  16. 2
      templates/teamspeak.html
  17. 69
      utils.py
  18. 99
      views.py

2
.gitignore

@ -3,4 +3,4 @@ __pycache__/
*.py[cod] *.py[cod]
*.so *.so
static/img/hero-images/*.png

2
__init__.py

@ -227,4 +227,4 @@ def utility_processor():
country_abbreviation_mapping=country_abbreviation_mapping) country_abbreviation_mapping=country_abbreviation_mapping)
'''
'''

7
forms.py

@ -0,0 +1,7 @@
from flask.ext.wtf import Form
from wtforms import TextField, BooleanField, TextAreaField
class SettingsForm(Form):
public = BooleanField('public', default=True)
twitch = TextField('twitch')
bio_text = TextAreaField('bio_text')

99
models.py

@ -1,35 +1,110 @@
import simplejson as json import simplejson as json
from random import choice
from flask.ext.sqlalchemy import SQLAlchemy from flask.ext.sqlalchemy import SQLAlchemy
from sqlalchemy.ext.mutable import Mutable
from time import time from time import time
from app import db from app import db
from utils import parse_valve_heropedia, complete_hero_data
# Get a little of that Mongoness back # Get a little of that Mongoness back
class Json(db.TypeDecorator): class Json(db.TypeDecorator):
impl = db.Unicode
impl = db.Text
def process_bind_param(self, value, dialect):
if value is not None:
value = json.dumps(value)
return value
def process_result_value(self, value, dialect):
if value is not None:
value = json.loads(value)
return value
class MutableDict(Mutable, dict):
@classmethod
def coerce(cls, key, value):
if not isinstance(value, MutableDict):
if isinstance(value, dict):
return MutableDict(value)
return Mutable.coerce(key,value)
else:
return value
def __delitem__(self, key):
dict.__delitem__(self, key)
self.changed()
def __setitem__(self, key, value):
dict.__setitem__(self, key, value)
self.changed()
def __getstate__(self):
return dict(self)
def __setstate__(self, state):
self.update(self)
def process_bind_param(self, value, dialect):
return unicode(json.dumps(value))
def process_result_value(self, value, dialect):
return json.loads(value)
class User(db.Model): class User(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
steam_id = db.Column(db.String(40), unique=True) steam_id = db.Column(db.String(40), unique=True)
nickname = db.Column(db.String(80)) nickname = db.Column(db.String(80))
avatar = db.Column(db.String(255)) avatar = db.Column(db.String(255))
random_heroes = db.Column(MutableDict.as_mutable(Json))
bio_text = db.Column(db.String(4096))
last_seen = db.Column(db.DateTime)
twitch = db.Column(db.String(60))
@staticmethod @staticmethod
def get_or_create(steam_id): def get_or_create(steam_id):
rv = User.query.filter_by(steam_id=steam_id).first()
if rv is None:
rv = User()
rv.steam_id = steam_id
db.session.add(rv)
return rv
rv = User.query.filter_by(steam_id=steam_id).first()
if rv is None:
rv = User()
rv.steam_id = steam_id
rv.random_heroes = {'current':None, 'completed':[]}
bio_text = ''
db.session.add(rv)
return rv
@property
def random_hero(self):
if not self.random_heroes['current']:
heroes = []
for (tavern_name, tavern) in parse_valve_heropedia():
heroes.extend([complete_hero_data('name', entry['name']) for entry in tavern])
if heroes:
self.random_heroes['current'] = choice(heroes)
self.random_heroes = self.random_heroes
db.session.commit()
return self.random_heroes['current']
@random_hero.setter
def random_hero(self, herodata):
self.random_heroes['current'] = herodata
self.random_heroes = self.random_heroes
db.session.commit()
@property
def random_completed(self):
return self.random_heroes['completed']
def random_success(self):
self.random_heroes['completed'].append(self.random_heroes['current']['name'])
self.random_heroes['current'] = None
self.random_heroes = self.random_heroes
db.session.commit()
return self.random_hero
def random_skip(self):
self.random_heroes['current'] = None
self.random_heroes = self.random_heroes
db.session.commit()
return self.random_hero
def __repr__(self): def __repr__(self):
return '<User {}>'.format(self.steam_id)
return '<User {}>'.format(self.nickname)
class TeamspeakData(db.Model): class TeamspeakData(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)

1250
static/country_codes.xml
File diff suppressed because it is too large
View File

38
static/css/app.css

@ -6,14 +6,9 @@ body {
margin: 1em auto 6em; margin: 1em auto 6em;
} }
#biglogo { #biglogo {
width:75%;
}
.dark-panel {
background: -moz-linear-gradient(center top, #222222 0%, #313331 100%);
border: 5px solid #999999;
border-radius: 4px;
box-shadow: 0 2px 5px rgba(0,0,0,0.25);
color: #fff;
width:65%;
max-width:650px;
min-width:250px;
} }
.uk-navbar-brand > img, .uk-navbar-flip > img { .uk-navbar-brand > img, .uk-navbar-flip > img {
height:35px; height:35px;
@ -58,3 +53,30 @@ footer {
#streams-online > .uk-article > a > h4 { #streams-online > .uk-article > a > h4 {
margin:0px; margin:0px;
} }
#tsviewer > .uk-modal-dialog {
max-height: 80%;
overflow-y: scroll;
}
.ts_num {
color: #D9A859;
}
.dn-streamer, .dn-streamer-offline {
display: inline-block;
width: 110px;
padding: 5px;
margin: 5px;
}
.dn-streamer-offline > img {
height:50px;
}
.dn-streamer-hover {
background:red;
}
.dn-hero-icon {
background:#000;
padding:4px;
margin:2px;
}

2
static/css/heropedia.css

@ -13,7 +13,7 @@
background-repeat:no-repeat; background-repeat:no-repeat;
overflow:visible; overflow:visible;
text-transform:uppercase; text-transform:uppercase;
color: rgba(105,211,50,0.75);
color: black;
padding:6px 10px 15px 40px; padding:6px 10px 15px 40px;
} }
#tavernAgility{ #tavernAgility{

5
static/css/ts3_viewer.css

@ -20,11 +20,10 @@
{ {
font-family: sans-serif; font-family: sans-serif;
font-size: 14px; font-size: 14px;
white-space: nowrap;
white-space: normal;
width: 100%; width: 100%;
line-height: 1; line-height: 1;
overflow-x: hidden;
overflow-y: scroll;
overflow: hidden;
} }
.tswv-link .tswv-link

9
teamspeak.py

@ -8,10 +8,10 @@ from flask import url_for
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from app import app from app import app
from models import TeamspeakData
def getTeamspeakWindow(window=605800): def getTeamspeakWindow(window=605800):
current_time = time.time() current_time = time.time()
from models import TeamspeakData
return TeamspeakData.query.filter(TeamspeakData.time < current_time, TeamspeakData.time > current_time-window).order_by(TeamspeakData.time).all() return TeamspeakData.query.filter(TeamspeakData.time < current_time, TeamspeakData.time > current_time-window).order_by(TeamspeakData.time).all()
def create_teamspeak_viewer(): def create_teamspeak_viewer():
@ -159,8 +159,11 @@ def create_teamspeak_viewer():
return "error: %s" % inst return "error: %s" % inst
def get_ISO3166_mapping(): def get_ISO3166_mapping():
data = requests.get('http://www.iso.org/iso/home/standards/country_codes/country_names_and_code_elements_xml.html')
xml = ElementTree.fromstring(data.text.encode('utf-8'))
#data = requests.get(url_for('static', filename='country_codes.xml'))
#xml = ElementTree.fromstring(data.text.encode('utf-8'))
with open('app/static/country_codes.xml', mode='r') as d:
data = d.read()
xml = ElementTree.fromstring(data)
d = dict() d = dict()
for entry in xml.findall('ISO_3166-1_Entry'): for entry in xml.findall('ISO_3166-1_Entry'):
d[entry.find('ISO_3166-1_Alpha-2_Code_element').text] = entry.find('ISO_3166-1_Country_name').text d[entry.find('ISO_3166-1_Alpha-2_Code_element').text] = entry.find('ISO_3166-1_Country_name').text

87
templates/hero_random.html

@ -0,0 +1,87 @@
{% extends "layout.html" %}
{% block head %}
<link rel="stylesheet" href="{{ url_for('static', filename='css/heropedia.css') }}" >
{% endblock %}
{% block title %} A-Z Challenge: {{ user.nickname }}{% endblock %}
{% block content %}
{% set taverns = heropedia() %}
<h2 class="uk-text-center">{{ user.nickname }}'s A-Z Challenge</h2>
<hr />
<div class="uk-grid">
<div class="uk-width-large-1-3 uk-width-medium-1-1 uk-panel uk-text-center uk-margin-bottom">
<h3 class="uk-panel-title">Current Hero</h3>
<h4 class="uk-text-bold uk-margin-remove">{{ user.random_hero['localized_name'] }}</h4>
<img src="{{ url_for('static', filename=hero_image_large(user.random_hero)) }}" class="dn-hero-icon" />
<p>
<span id="heroes_completed">Heroes Completed: {{ user.random_heroes.completed | length }}</span><br/>
<span id="heroes_left">Heroes Left: {{ total_hero_pool() - user.random_heroes.completed|length }}</span>
</span>
</p>
{% if g.user and g.user.steamid == user.steamid %}
<form action="{{ url_for('user_random_hero', userid=g.user.id) }}" method="post" id="random_form" class="uk-margin">
<input type="checkbox" name="completed" id="completed_checkbox" style="display:none;">
<input type="checkbox" name="skip" id="skip_checkbox" style="display:none;">
<a class="uk-button uk-button-success" id="completed_button">Completed!</a>
<a class="uk-button uk-button-primary" id="skip_button">Skip</a>
</form>
{% endif %}
</div>
<div class="uk-width-large-2-3 uk-width-medium-1-1 uk-panel uk-text-center uk-margin">
<div class="uk-badge uk-panel-badge uk-badge-notification uk-badge-success">x1</div>
<h3 class="uk-panel-title">Completed</h3>
{% for hero in user.random_heroes.completed %}
<img src="{{ url_for('static', filename=hero_image_small(hero)) }}" class="dn-hero-icon" />
{% endfor %}
<br/><br/>
<br/><br/>
</div>
<div class="uk-width-large-2-3 uk-width-medium-1-1 uk-container-center">
<ul class="uk-tab" data-uk-tab="{connect:'#taverns'}">
<li class="uk-active"><a href="">Strength</a></li>
<li><a href="">Agility</a></li>
<li><a href="">Intelligence</a></li>
</ul>
<ul id="taverns" class="uk-switcher uk-margin">
<li><div class="uk-panel tavern">
<label id="tavernStrength">Strength</label>
{% for hero in taverns[0][1] + taverns[3][1] %}
<img class="{{hero['name'] in user.random_completed and 'filterUnmatchedHero' or 'filterMatchedHero' }}" id="{{ hero['name'] }}" src="{{ url_for('static', filename=hero_image_small(hero)) }}" />
{% endfor %}
</div></li>
<li><div class="uk-panel tavern">
<label id="tavernAgility">Agility</label>
{% for hero in taverns[1][1] + taverns[4][1] %}
<img class="{{hero['name'] in user.random_completed and 'filterUnmatchedHero' or 'filterMatchedHero' }}" id="{{ hero['name'] }}" src="{{ url_for('static', filename=hero_image_small(hero)) }}" />
{% endfor %}
</div> </li>
<li><div class="uk-panel tavern">
<label id="tavernIntelligence">Intelligence</label>
{% for hero in taverns[2][1] + taverns[5][1] %}
<img class="{{hero['name'] in user.random_completed and 'filterUnmatchedHero' or 'filterMatchedHero' }}" id="{{ hero['name'] }}" src="{{ url_for('static', filename=hero_image_small(hero)) }}" />
{% endfor %}
</div> </li>
</ul>
</div>
</div>
{% endblock %}
{% if g.user and g.user.id == user.id %}
{% block pagescripts %}
<script>
$(document).ready(function() {
$('#completed_button').click( function() {
$('#completed_checkbox').attr('checked', true);
$('#random_form').submit();
});
$('#skip_button').click( function() {
$('#skip_checkbox').attr('checked', true);
$('#random_form').submit();
});
});
</script>
{% endblock %}
{% endif %}

213
templates/index.html

@ -5,163 +5,128 @@
{% block title %}Dota Noobs{% endblock %} {% block title %}Dota Noobs{% endblock %}
{% block content %} {% block content %}
{% cache 60*5 %}
{#{% cache 60*5 %}#}
{% set teamspeak_data = get_teamspeak_window() %} {% set teamspeak_data = get_teamspeak_window() %}
<div class="uk-grid" data-uk-grid-margin>
<div class="uk-width-large-1-2 uk-width-medium-1-1">
<div class=" uk-panel uk-panel-space">
<h1 class="uk-panel-title">Who Are The Doobs!?</h1>
<p>Welcome to DotaNoobs! We are a community formed by a group of redditors in an effort to provide a fun and rage-free environment for Dota2 players of all skill levels to enjoy the game and learn from each other. We have a TeamSpeak server open to the public for voice communication; hopping into the server is the best way to get involved, so click on "Server" to the right to get started. </p>
<div id="about-us-more">
<p>Be sure to visit the forum board to keep in touch with the community even when not playing. Create an account, introduce yourself, and chat about all things DotA.</p>
<p>There is a "Dota Noobs" chat room inside the game client, and a "DotaNoobs" guild to help organzie games outside of TeamSpeak. Ask an admin for an invite today!</p>
</div>
<button class="uk-button uk-button-small uk-float-right" id="more">More <i class="uk-icon-angle-down"></i></button>
<button class="uk-button uk-button-small uk-float-right" id="less">Less <i class="uk-icon-angle-up"></i></button>
</div>
</div>
<div class="uk-width-large-1-2 uk-width-medium-1-1">
<div class="uk-panel uk-panel-space uk-panel-box">
<a href="ts3server://voice.dotanoobs.com"><div class="uk-panel-badge uk-badge uk-badge-success"><i class="uk-icon-microphone"> Connect to Teamspeak</i></div></a>
<h1 class="uk-panel-title">Our TeamSpeak Server</h1>
<ul>
<li>Current Users: {{ ts3_current_clients() }}</li>
<li>Active Countries: {{ ts3_countries_active(teamspeak_data) }}</li>
</ul>
<button class="uk-button" data-uk-modal="{target:'#tsviewer'}">Viewer</button>
<a class="uk-button" href="{{ url_for('teamspeak') }}">Statistics</a>
<hr class="uk-panel-divider" />
<h1 class="uk-panel-title">Events</h1>
{#
{% if active_event %}
<article class="uk-article">
<h4>Right Now</h4>
<a href="{{ url_for('event_summary', eventid=active_event._id) }}">{{ active_event.name }}</a>
<p class="uk-article-meta">Ends at: {{ timestamp_to_js_date(active_event.end_time) }}</p>
</article>
{% endif %}
{% if upcoming_event %}
<article class="uk-article">
<h4>Next Event</h4>
<a href="{{ url_for('event_summary', eventid=upcoming_event._id) }}">{{ upcoming_event.name }}</a>
<p class="uk-article-meta">Starts at: {{ timestamp_to_js_date(upcoming_event.start_time) }}</p>
</article>
{% else %}
<em>No events planned for the near future. Suggest one on the forum!</em>
{% endif %}
#}
<div class="uk-grid">
<div class="uk-width-large-1-2 uk-width-medium-1-1 uk-panel uk-panel-space">
<h1 class="uk-panel-title">Events</h1>
{#
{% if active_event %}
<article class="uk-article">
<h4>Right Now</h4>
<a href="{{ url_for('event_summary', eventid=active_event._id) }}">{{ active_event.name }}</a>
<p class="uk-article-meta">Ends at: {{ timestamp_to_js_date(active_event.end_time) }}</p>
</article>
{% endif %}
{% if upcoming_event %}
<article class="uk-article">
<h4>Next Event</h4>
<a href="{{ url_for('event_summary', eventid=upcoming_event._id) }}">{{ upcoming_event.name }}</a>
<p class="uk-article-meta">Starts at: {{ timestamp_to_js_date(upcoming_event.start_time) }}</p>
</article>
{% else %}
<em>No events planned for the near future. Suggest one on the forum!</em> <em>No events planned for the near future. Suggest one on the forum!</em>
{% endif %}
#}
<em>No events planned for the near future. Suggest one on the forum!</em>
</div>
<div class="uk-width-large-1-2 uk-width-medium-1-1 uk-panel uk-panel-box uk-text-center">
<a href="#" data-uk-modal="{target: '#tsviewer'}"><div class="uk-badge uk-panel-badge uk-badge-success"><i class="uk-icon-user"></i> Users</div></a>
<h1 class="uk-panel-title">Teamspeak</h1>
<div class="uk-grid uk-margin-bottom">
<div class="uk-width-1-3 uk-panel">
<h3>Users<br/><span class="ts_num">{{ ts3_current_clients() }}</span></h3>
</div>
<div class="uk-width-1-3 uk-panel">
<h3 class="uk-text-success">Online</h3>
</div>
<div class="uk-width-1-3 uk-panel">
<h3>Countries<br/><span class="ts_num">{{ ts3_countries_active(teamspeak_data) }}</span></h3>
</div>
</div> </div>
<div id="tsviewer" class="uk-modal">
<a class="uk-button uk-button-success uk-width-1-1" href="ts3server://voice.dotanoobs.com"><i class="uk-icon-microphone"></i> Connect</a>
<a class="uk-button uk-width-1-1 uk-margin" href="{{ url_for('teamspeak') }}"><i class="uk-icon-globe"></i> Stats</a>
<a class="uk-button uk-width-1-1" href="http://www.teamspeak.com/?page=downloads"><i class="uk-icon-download"></i> Download</a>
<div id="tsviewer" class="uk-modal uk-text-left">
<div class="uk-modal-dialog"> <div class="uk-modal-dialog">
<a class="uk-modal-close uk-close"></a> <a class="uk-modal-close uk-close"></a>
{{ ts3_viewer() | safe }} {{ ts3_viewer() | safe }}
</div> </div>
</div> </div>
</div> </div>
</div>
<div class="uk-grid" data-uk-grid-margin>
<div class="uk-width-large-2-3 uk-width-medium-1-1">
<div class="uk-panel uk-panel-space uk-panel-box uk-panel-header">
<h1 class="uk-panel-title">News and Announcements</h1>
{% for news in latest_news %}
<article class="uk-article dn-news-article">
<h4 class="uk-article-title uk-text-truncate" data-uk-tooltip="{pos:'bottom-right'}" title="{{ news['title'] }}"><a href="{{ news['url'] }}">{{ news['title'] }}</a></h4>
<p class="uk-article-meta">{{ news['date'] }}</p>
<p>{{ news['text'] | shorten }}</p>
</article>
{% endfor %}
</div>
<div id="streams" class="uk-width-1-1 uk-text-center uk-panel uk-panel-space">
</div> </div>
{#
<div class="uk-hidden-large uk-width-medium-1-1">
<div class="uk-panel uk-panel-space uk-panel-box uk-panel-header">
<a href="{{ url_for('list_events') }}"><div class="uk-panel-badge uk-badge uk-badge-success"><i class="uk-icon-calendar"> View All Events</i></div></a>
<h1 class="uk-panel-title">Events</h1>
{% if active_event %}
<article class="uk-article">
<h4>Right Now</h4>
<a href="{{ url_for('event_summary', eventid=active_event._id) }}">{{ active_event.name }}</a>
<p class="uk-article-meta">Ends at: {{ timestamp_to_js_date(active_event.end_time) }}</p>
</article>
{% endif %}
{% if upcoming_event %}
<article class="uk-article">
<h4>Next Event</h4>
<a href="{{ url_for('event_summary', eventid=upcoming_event._id) }}">{{ upcoming_event.name }}</a>
<p class="uk-article-meta">Starts at: {{ timestamp_to_js_date(upcoming_event.start_time) }}</p>
<div class="uk-width-1-1 uk-panel uk-panel-header">
<h4 class="uk-panel-title">News and Announcements</h4>
{% for news in latest_news %}
<article class="uk-article dn-news-article">
<h4 class="uk-article-title" title="{{ news['title'] }}"><a href="{{ news['url'] }}">{{ news['title'] }}</a></h4>
<p class="uk-article-meta">{{ news['date'] }}</p>
<p>{{ news['text'] | shorten }}</p>
</article> </article>
{% else %}
<em>No events planned for the near future. Suggest one on the forum!</em>
{% endif %}
<em>No events planned for the near future. Suggest one on the forum!</em>
</div>
</div>
#}
<div class="uk-width-large-1-3 uk-width-medium-1-1">
<div class="uk-panel uk-panel-space">
<h1 class="uk-panel-title">Live Streams</h1>
<ul id="streams-online"></ul>
<hr id="stream-divider" class="uk-article-divider" style="display:none;" />
<ul id="streams-offline"></ul>
</div>
{% endfor %}
</div> </div>
</div> </div>
</div>
{% endcache %}
{#{% endcache %}#}
{% endblock %} {% endblock %}
{% block pagescripts %} {% block pagescripts %}
<script> <script>
{% cache 60*5 %}
$(document).ready(function() { $(document).ready(function() {
{% cache 60*5 %}
// Add the streams // Add the streams
var stream_url = "https://api.twitch.tv/kraken/streams/"; var stream_url = "https://api.twitch.tv/kraken/streams/";
var channels = ["dotanoobs", "bearhugdota", "kreejaffakree", "prettypenguins", "shaneomad"]; var channels = ["dotanoobs", "bearhugdota", "kreejaffakree", "prettypenguins", "shaneomad"];
for (var idx in channels) { for (var idx in channels) {
$.getJSON(stream_url+channels[idx]+"?callback=?", function(data) { $.getJSON(stream_url+channels[idx]+"?callback=?", function(data) {
if (data.stream) {
$('#streams-online').append("<article class='uk-article' id='" + data.stream.channel.name + "'>");
var jquery_selector = '#'+data.stream.channel.name;
$(jquery_selector).append("<a href='"+data.stream.channel.url+"'><h4>" + data.stream.channel.display_name + "</h4></a>")
var span_text = "<span class='uk-article-meta'>";
span_text = span_text + "Playing: " + data.stream.game + "<br />";
span_text = span_text + "Viewers: " + data.stream.viewers + "<br />";
span_text = span_text + "</span>";
$(jquery_selector).append(span_text);
$(jquery_selector).append("<img src='" + data.stream.preview.medium + "' />")
$('#streams-online').append("</article>");
$('#stream-divider').show();
} else {
$.getJSON(data._links.channel+"?callback=?", function(data) {
$('#streams-offline').append("<article class='uk-article' id='" + data.name + "'>");
$('#'+data.name).append("<a href='"+data.url+"'><h4>" + data.display_name + "</h4></a>");
//$('#'+data.name).append("<img src='" + data.logo + "' style='float: right;' width='62' height='62' /><br />");
$('#'+data.name).append("<p class='uk-article-meta'><strong>Offline</strong></p>");
$('#streams-offline').append("</div>");
});
}
if (data.stream) {
var $a = $("<a href='"+data.stream.channel.url+"'></a>");
var $strm = $("<div class='dn-streamer uk-text-success uk-panel uk-panel-box' id='"+data.stream.channel.name+"'></div>");
$strm.append("<p class='uk-text-bold'>" + data.stream.channel.display_name + "</p>");
$strm.append("<img src='" + data.stream.preview.small + "' />");
$strm.append("<p><i class='uk-icon-male'></i> "+data.stream.viewers+"</p>");
$a.append($strm);
$("#streams").prepend($a);
} else {
$.getJSON(data._links.channel+"?callback=?", function(data) {
var $a = $("<a href='"+data.url+"'></a>");
var $strm = $("<div class='dn-streamer-offline uk-text-success uk-panel uk-panel-box' id='"+data.name+"'></div>");
$strm.append("<p class='uk-text-bold'>" + data.display_name + "</p>");
$strm.append("<img src='" + data.logo + "' />");
$strm.append("<p class='dn-offline'>Offline</p>");
$a.append($strm);
$("#streams").append($a);
});
}
}); });
} }
{% endcache %} {% endcache %}
$(".dn-streamer, .dn-streamer-offline").on({
mouseover: function() {
$(this).addClass('dn-streamer-hover');
},
mouseleave: function() {
$(this).removeClass('dn-streamer-hover');
}
}, "div");
// Localize the events // Localize the events
$('.date').each( function( index ) { $('.date').each( function( index ) {
var d = new Date($(this).text()); var d = new Date($(this).text());
$(this).text( d.toLocaleDateString() + ' @ ' + d.toLocaleTimeString() ); $(this).text( d.toLocaleDateString() + ' @ ' + d.toLocaleTimeString() );
}); });
// About-us toggle
$('#more,#less').click(function() {
$('#more').toggle();
$('#less').toggle();
$('#about-us-more').toggle();
});
}); });
</script> </script>
{% endblock %} {% endblock %}

37
templates/layout.html

@ -10,6 +10,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- CSS includes --> <!-- CSS includes -->
<!--<link rel="stylesheet" href="{{ url_for('static', filename='css/uikit.min.css') }}" />-->
<link rel="stylesheet" href="{{ url_for('static', filename='css/uikit.gradient.min.css') }}" /> <link rel="stylesheet" href="{{ url_for('static', filename='css/uikit.gradient.min.css') }}" />
<link rel="stylesheet" href="{{ url_for('static', filename='css/app.css') }}" /> <link rel="stylesheet" href="{{ url_for('static', filename='css/app.css') }}" />
@ -22,36 +23,18 @@
<nav class="uk-navbar uk-navbar-attached" data-uk-navbar> <nav class="uk-navbar uk-navbar-attached" data-uk-navbar>
<a href="#offcanvas" class="uk-navbar-brand uk-hidden-large" data-uk-offcanvas><img src="{{ url_for('static', filename='img/navlogo.png') }}" /><i class="uk-icon-double-angle-down"></i></a> <a href="#offcanvas" class="uk-navbar-brand uk-hidden-large" data-uk-offcanvas><img src="{{ url_for('static', filename='img/navlogo.png') }}" /><i class="uk-icon-double-angle-down"></i></a>
<a href="" class="uk-navbar-brand uk-visible-large" data-uk-offcanvas><img src="{{ url_for('static', filename='img/navlogo.png') }}" /></a> <a href="" class="uk-navbar-brand uk-visible-large" data-uk-offcanvas><img src="{{ url_for('static', filename='img/navlogo.png') }}" /></a>
{# /* In header drop-down navigation, possibly reenable as static "you are here" marker */
<ul class="uk-navbar-nav">
<li data-uk-dropdown="" class="uk-parent">
<a href="{{ url_for('main') }}"><i class="uk-icon-home"></i> Home</a>
<div class="uk-dropdown uk-dropdown-navbar">
<ul class="uk-nav uk-nav-navbar">
<li><a href="http://board.dotanoobs.com">Board</a></li>
<li><a href="{{ url_for('teamspeak') }}">TeamSpeak</a></li>
<li class="uk-nav-header">Doobs</li>
<li><a href="{{ url_for('doobs_stuff.list_events') }}">Events</a></li>
<li><a href="{{ url_for('doobs_stuff.ladder') }}">Ladder</a></li>
</ul>
</div>
</li>
</ul>
#}
<!-- Check if user is logged in --> <!-- Check if user is logged in -->
{% if g.user %} {% if g.user %}
<ul class="uk-navbar-nav uk-navbar-flip"> <ul class="uk-navbar-nav uk-navbar-flip">
<li data-uk-dropdown="" class="uk-parent"> <li data-uk-dropdown="" class="uk-parent">
<a href="#"><img class="uk-responsive-height" src="{{ g.user.avatar
}}" /> {{ g.user.nickname }} </a>
<a href="#"><img class="uk-responsive-height" src="{{ g.user.avatar }}" /> {{ g.user.nickname }} </a>
<div class="uk-dropdown uk-dropdown-navbar"> <div class="uk-dropdown uk-dropdown-navbar">
<ul class="uk-nav uk-nav-navbar"> <ul class="uk-nav uk-nav-navbar">
{#
<li><a href="{{ url_for('doobs_stuff.doob_summary', playerid=g.doob._id) }}">Profile</a></li>
<li><a href="{{ url_for('doobs_stuff.doob_random', playerid=g.doob._id) }}">A-Z Challenge</a></li>
<!--<li class="uk-disabled"><a href="">Recent Events</a></li>-->
<li><a href="{{ url_for('user_profile', userid=g.user.id) }}">Profile</a></li>
<li><a href="{{ url_for('user_random_hero', userid=g.user.id) }}">A-Z Challenge</a></li>
<li><a href="http://board.dotanoobs.com/?page=lastposts">Latest Posts</a></li>
<li class="uk-nav-divider"></li> <li class="uk-nav-divider"></li>
#}
<li><a href="{{ url_for('user_settings', userid=g.user.id) }}">Settings</a></li>
<li><a href="{{ url_for('logout') }}">Logout</a></li> <li><a href="{{ url_for('logout') }}">Logout</a></li>
</ul> </ul>
</div> </div>
@ -65,10 +48,10 @@
<!-- Flash Error Messages --> <!-- Flash Error Messages -->
{% with messages = get_flashed_messages() %} {% with messages = get_flashed_messages() %}
{% if messages %} {% if messages %}
<ul class="flashes">
<ul class="flashes uk-width-1-3 uk-container-center">
<a href="" class="uk-alert-close uk-close"></a> <a href="" class="uk-alert-close uk-close"></a>
{% for message in messages %} {% for message in messages %}
<li class="uk-alert uk-alert-danger">{{ category }}:{{ message }}</li>
<li class="uk-alert uk-alert-danger">{{ message }}</li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
@ -79,12 +62,12 @@
<!-- Big top logo --> <!-- Big top logo -->
<div class="uk-width-1-1"><img class="uk-align-center" id="biglogo" src="{{ url_for('static', filename='img/biglogo.png') }}"></div> <div class="uk-width-1-1"><img class="uk-align-center" id="biglogo" src="{{ url_for('static', filename='img/biglogo.png') }}"></div>
<!-- Side navigation --> <!-- Side navigation -->
<div class="uk-visible-large uk-width-1-5 uk-panel uk-panel-box">
<div class="uk-visible-large uk-width-1-5 uk-panel">
{% if g.doob%} {% if g.doob%}
<h3 class="uk-panel-title">{{ g.doob.name }}</h3> <h3 class="uk-panel-title">{{ g.doob.name }}</h3>
<hr class="uk-nav-divider" /> <hr class="uk-nav-divider" />
{% endif %} {% endif %}
<ul class="uk-nav uk-nav-side">
<ul class="uk-nav uk-nav-side uk-text-right uk-nav-parent-icon" data-uk-nav>
{% include 'sidenav.html' %} {% include 'sidenav.html' %}
</ul> </ul>
</div> </div>

41
templates/profile.html

@ -4,29 +4,38 @@
{% block content %} {% block content %}
<div class="uk-grid" data-uk-grid-margin> <div class="uk-grid" data-uk-grid-margin>
<div class="uk-width-1-1 uk-panel">
<img class="uk-align-center" src="{{ user.avatar }}" />
<h2 class="uk-panel-title uk-text-center">Profile for {{ user.nickname }}</h2>
<div class="uk-width-2-3">
<h2 class="uk-float-left"><img class="" src="{{ user.avatar }}" />&nbsp;{{ user.nickname }}</h2>
</div>
<div class="uk-width-1-3 uk-text-center">
<a href="http://steamcommunity.com/profiles/{{ user.steam_id | safe }}">Steam</a> |
<a href="http://board.dotanoobs.com/?page=profile&id={{ user.id | safe }}">Forum Profile</a> |
<a href="http://dotabuff.com/search?q={{ user.steam_id }}">Dotabuff</a>
</div> </div>
<!--Main content area --> <!--Main content area -->
<div class="uk-width-large-2-3 uk-width-medium-1-1 uk-panel"> <div class="uk-width-large-2-3 uk-width-medium-1-1 uk-panel">
<div class="uk-panel">
main content area
</div>
{% if user.bio_text == None %}
<em class="uk-text-danger">This user's profile bio is empty!</em>
{% else %}
<em class="uk-text-bold">{{ user.bio_text }}</em>
{% endif %}
{% if user.id == g.user.id %}&nbsp;<a href="{{ url_for('user_settings')}}"><i class="uk-icon-edit"></i></a>{% endif %}
</div> </div>
<!-- Side bar --> <!-- Side bar -->
<div class="uk-width-large-1-3 uk-width-medium-1-1 uk-panel uk-panel-box">
<ul>
<li>Points [value]</li>
<li><a href="http://board.dotanoobs.com/?page=profile&id={{ user.id | safe }}">Forum Profile</a>
<li>Last seen on TeamSpeak: [value]<li>
</ul>
<hr class="uk-panel-divider" />
<div class="uk-width-large-1-3 uk-width-medium-1-1 uk-panel uk-panel-box uk-panel-box-secondary">
<div class="uk-container-center uk-text-center"> <div class="uk-container-center uk-text-center">
Randomstats
<span>Current Hero</span>
Heroimg
<span class="uk-text-bold">Current Hero</span><br/>
<span>{{ user.random_hero['localized_name'] }}</span><br/>
<a href={{ url_for('user_random_hero', userid=user.id) }}>
<img src="{{ url_for('static', filename=hero_image_large(user.random_hero)) }}" class="dn-hero-icon" /><br/>
<span>View A-Z Progress</span>
</a>
</div> </div>
<ul class="uk-list uk-list-space uk-list-striped">
<li>Points: <span id='points_total'>0</span></li>
<li>Last Seen: <span id='date'></span></li>
<li>Heroes Randomed: <span id='rands'>{{ user.random_heroes.completed | length }}</span></li>
</ul>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

41
templates/settings.html

@ -0,0 +1,41 @@
{% extends "layout.html" %}
{% block title %}Settings for {{ g.user.nickname }} - DotaNoobs {% endblock %}
{% block content %}
<div class="uk-grid" data-uk-grid-margin>
<div class="uk-width-2-3">
<h2 class="uk-float-left"><img class="" src="{{ user.avatar }}" />&nbsp;{{ user.nickname }}</h2>
</div>
<div class="uk-width-1-3 uk-text-center">
</div>
<!--Main content area -->
<div class="uk-width-large-2-3 uk-width-medium-1-1 uk-panel">
<form class="uk-form uk-form-width-large" action="" method="post" name="settings">
{{ form.hidden_tag() }}
<fieldset data-uk-margin>
<legend>Settings</legend>
<div class="uk-form-row">
<ul class="uk-list">
<li><label class="uk-form-label"> {{ form.public }} Public Profile</label></li>
<li><label class="uk-form-label"><input type="checkbox" disabled> Show Big Logo</label></li>
<li><label class="uk-form-label"><input type="checkbox" disabled> Nonexistant Setting</label></li>
</ul>
</div>
<div class="uk-form-row">
<label class="uk-form-label">Twitch.tv Username:</label> <br/>
{{ form.twitch(placeholder="e.g. shaneomad") }}
</div>
<div class="uk-form-row">
<label class="uk-form-label">Biography text:</label><br/>
{{ form.bio_text(rows=14, class='uk-width-1-1', data=g.user.bio_text, placedholder='What you place here is displayed in your profile when other users view it.') }}
</div>
<div class="uk-form-controls uk-margin-top">
<button class="uk-button uk-button-success" type="submit">Save</button>
<a class="uk-button" href="{{ url_for('user_profile', userid=g.user.id) }}">Cancel</a>
</div>
</fieldset>
</form>
</div>
</div>
{% endblock %}

12
templates/sidenav.html

@ -2,13 +2,19 @@
('index', 'Home'), ('index', 'Home'),
('friends', 'Friends'), ('friends', 'Friends'),
('list_events', 'Events'), ('list_events', 'Events'),
('community', 'Community'),
('ladder', 'Ladder'),
('teamspeak', 'TS3 Stats'),
] %} ] %}
<li{% if endpoint == request.endpoint %} class='uk-active' {% endif %}> <li{% if endpoint == request.endpoint %} class='uk-active' {% endif %}>
<a href="{{ url_for(endpoint) }}">{{ title }}</a> <a href="{{ url_for(endpoint) }}">{{ title }}</a>
</li> </li>
{% endfor %} {% endfor %}
<li class="uk-parent">
<a href="#">Stream Stats</a>
<ul class="uk-nav-sub">
<li><a href="http://potatr.dotanoobs.com">Potato_Bot</a></li>
<li><a href="http://potatr.dotanoobs.com">Cider_Bot</a></li>
</ul>
</li>
<li class="uk-nav-divider"></li> <li class="uk-nav-divider"></li>
<li class="uk-nav-header">Social</li> <li class="uk-nav-header">Social</li>
<li><a href="http://board.dotanoobs.com"><i class="uk-icon-group"> Board</i></a></li> <li><a href="http://board.dotanoobs.com"><i class="uk-icon-group"> Board</i></a></li>
@ -20,6 +26,6 @@
<li><a href="{{ url_for('login') }}"><i class="uk-icon-cog"> Register</i></a></li> <li><a href="{{ url_for('login') }}"><i class="uk-icon-cog"> Register</i></a></li>
{% else %} {% else %}
<li {% if request.endpoint == 'user_profile' %} class='uk-active' {% endif %}><a href="{{ url_for('user_profile', userid=g.user.id) }}"><i class="uk-icon-home"> Profile</i></a></li> <li {% if request.endpoint == 'user_profile' %} class='uk-active' {% endif %}><a href="{{ url_for('user_profile', userid=g.user.id) }}"><i class="uk-icon-home"> Profile</i></a></li>
<li><a href="{{ url_for('user_profile', userid=g.user.id) }}"><i class="uk-icon-cog"> Settings</i></a></li>
<li><a href="{{ url_for('user_settings', userid=g.user.id) }}"><i class="uk-icon-cog"> Settings</i></a></li>
<li><a href="{{ url_for('logout') }}"><i class="uk-icon-off"> Logout</i></a></li> <li><a href="{{ url_for('logout') }}"><i class="uk-icon-off"> Logout</i></a></li>
{% endif %} {% endif %}

2
templates/teamspeak.html

@ -26,7 +26,7 @@
<h3 class="uk-text-bold uk-text-center">Current Status</h3> <h3 class="uk-text-bold uk-text-center">Current Status</h3>
<a class="uk-button uk-button-success" href="ts3server://voice.dotanoobs.com">Connect</a> <a class="uk-button uk-button-success" href="ts3server://voice.dotanoobs.com">Connect</a>
<a class="uk-button uk-button-primary" href="http://www.teamspeak.com/download">Download</a> <a class="uk-button uk-button-primary" href="http://www.teamspeak.com/download">Download</a>
<h5>Server: voice.dotanoobs.com</h5>
<h5><strong>Server: voice.dotanoobs.com</strong></h5>
<div class="uk-panel uk-text-left"> <div class="uk-panel uk-text-left">
{{ ts3_viewer() | safe }} {{ ts3_viewer() | safe }}
</div> </div>

69
utils.py

@ -1,6 +1,10 @@
import requests import requests
import re import re
from time import strptime, strftime, gmtime from time import strptime, strftime, gmtime
from bs4 import BeautifulSoup
from itertools import product
from os import path, makedirs
from calendar import timegm from calendar import timegm
from app import app, cache from app import app, cache
from teamspeak import create_teamspeak_viewer, getTeamspeakWindow, ISO3166_MAPPING from teamspeak import create_teamspeak_viewer, getTeamspeakWindow, ISO3166_MAPPING
@ -10,13 +14,36 @@ def get_steam_userinfo(steam_id):
'key': app.config['DOTA2_API_KEY'], 'key': app.config['DOTA2_API_KEY'],
'steamids': steam_id 'steamids': steam_id
} }
data = requests.get('http://api.steampowered.com/ISteamUser/' \
'GetPlayerSummaries/v0001/', params=options).json()
data = requests.get('http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0001/', params=options).json()
return data['response']['players']['player'][0] or {} return data['response']['players']['player'][0] or {}
def get_api_hero_data():
data = requests.get("https://api.steampowered.com/IEconDOTA2_570/GetHeroes/v0001/?key="+app.config['DOTA2_API_KEY']+"&language=en_us").json()
return data
API_DATA = get_api_hero_data()
def complete_hero_data(key, value):
# Possible keys are id, localized_name and name
for hero_data in API_DATA['result']['heroes']:
if hero_data[key] == value: return hero_data
def get_hero_data_by_id(hero_id):
return API_DATA['result']['heroes'][hero_id-1]
def parse_valve_heropedia():
data = requests.get('http://www.dota2.com/heroes/')
soup = BeautifulSoup(data.text)
taverns = []
tavern_names = [' '.join(entry) for entry in product(('Radiant', 'Dire'), ('Strength', 'Agility', 'Intelligence'))]
for tavern_name, tavern in zip(tavern_names, soup.find_all(class_=re.compile('^heroCol'))):
img_base = lambda tag: tag.name == 'img' and 'base' in tag.get('id')
taverns.append((tavern_name, [complete_hero_data('name', 'npc_dota_hero_%s' % tag['id'].replace('base_', '')) for tag in tavern.find_all(img_base)]))
return taverns
# For Templates # For Templates
@app.template_filter('shorten') @app.template_filter('shorten')
def shorten_filter(s, num_words=20):
def shorten_filter(s, num_words=40):
space_iter = re.finditer('\s+', s) space_iter = re.finditer('\s+', s)
output = u'' output = u''
while num_words > 0: while num_words > 0:
@ -30,6 +57,7 @@ def shorten_filter(s, num_words=20):
@app.context_processor @app.context_processor
def utility_processor(): def utility_processor():
''' For Teamspeak '''
@cache.memoize(60*5) @cache.memoize(60*5)
def ts3_viewer(): def ts3_viewer():
html = create_teamspeak_viewer()[0] html = create_teamspeak_viewer()[0]
@ -63,6 +91,38 @@ def utility_processor():
for key, name in ISO3166_MAPPING.iteritems(): for key, name in ISO3166_MAPPING.iteritems():
mapping[key.lower()] = ' '.join([word.capitalize() for word in name.split(' ')]) mapping[key.lower()] = ' '.join([word.capitalize() for word in name.split(' ')])
return mapping return mapping
''' Dota2 info '''
def total_hero_pool():
return len(API_DATA['result']['heroes'])
def hero_image_large(hero_data):
if type(hero_data) is unicode:
stripped_name = hero_data.replace('npc_dota_hero_', '')
else:
stripped_name = hero_data['name'].replace('npc_dota_hero_', '')
img_file = path.join(app.config['HERO_IMAGE_PATH'], stripped_name + '.png')
img_src = path.join(app.root_path, app.static_folder, img_file)
if not path.exists(img_src):
i = requests.get('http://media.steampowered.com/apps/dota2/images/heroes/{}_hphover.png'.format(stripped_name)).content
if not path.exists(path.split(img_src)[0]):
makedirs(path.split(img_src)[0])
with open(img_src, 'wb') as img:
img.write(i)
return img_file
def hero_image_small(hero_data):
if type(hero_data) is unicode:
stripped_name = hero_data.replace('npc_dota_hero_', '')
else:
stripped_name = hero_data['name'].replace('npc_dota_hero_', '')
img_file = path.join(app.config['HERO_IMAGE_PATH'], stripped_name + '_small.png')
img_src = path.join(app.root_path, app.static_folder, img_file)
if not path.exists(img_src):
i = requests.get('http://media.steampowered.com/apps/dota2/images/heroes/{}_sb.png'.format(stripped_name)).content
if not path.exists(path.split(img_src)[0]):
makedirs(path.split(img_src)[0])
with open(img_src, 'wb') as img:
img.write(i)
return img_file
''' Misc '''
def timestamp_to_js_date(timestamp): def timestamp_to_js_date(timestamp):
return strftime('%B %d, %Y %H:%M:%S UTC', gmtime(timestamp)) return strftime('%B %d, %Y %H:%M:%S UTC', gmtime(timestamp))
def js_date_to_timestamp(date): def js_date_to_timestamp(date):
@ -70,4 +130,5 @@ def utility_processor():
return dict(ts3_viewer=ts3_viewer, ts3_current_clients=ts3_current_clients, get_teamspeak_window=get_teamspeak_window, \ return dict(ts3_viewer=ts3_viewer, ts3_current_clients=ts3_current_clients, get_teamspeak_window=get_teamspeak_window, \
ts3_active_clients=ts3_active_clients, timestamp_to_js_date=timestamp_to_js_date, js_date_to_timestamp=js_date_to_timestamp, \ ts3_active_clients=ts3_active_clients, timestamp_to_js_date=timestamp_to_js_date, js_date_to_timestamp=js_date_to_timestamp, \
num_unique_clients_by_country=num_unique_clients_by_country, country_abbreviation_mapping=country_abbreviation_mapping, \ num_unique_clients_by_country=num_unique_clients_by_country, country_abbreviation_mapping=country_abbreviation_mapping, \
ts3_countries_active=ts3_countries_active)
ts3_countries_active=ts3_countries_active, hero_image_large=hero_image_large, hero_image_small=hero_image_small, \
heropedia=parse_valve_heropedia, total_hero_pool=total_hero_pool)

99
views.py

@ -1,18 +1,22 @@
from flask import render_template, flash, redirect, g, request, url_for, session from flask import render_template, flash, redirect, g, request, url_for, session
from datetime import datetime
from app import app, db, oid, cache from app import app, db, oid, cache
from models import User from models import User
from utils import get_steam_userinfo from utils import get_steam_userinfo
from board import latest_news from board import latest_news
from forms import SettingsForm
@app.before_request @app.before_request
def before_request(): def before_request():
g.user = None g.user = None
if 'user_id' in session: if 'user_id' in session:
g.user = User.query.get(session['user_id'])
g.user = User.query.get(session['user_id'])
if g.user:
g.user.last_seen = datetime.utcnow()
db.session.commit()
@app.route('/') @app.route('/')
@app.route('/main')
def index(): def index():
return render_template("index.html", latest_news=latest_news()) return render_template("index.html", latest_news=latest_news())
@ -32,7 +36,7 @@ def create_or_login(resp):
g.user.avatar = steamdata['avatar'] g.user.avatar = steamdata['avatar']
db.session.commit() db.session.commit()
session['user_id'] = g.user.id session['user_id'] = g.user.id
flash("You are logged in as {}".format(g.user.nickname))
flash("You are logged in as {}".format(g.user.nickname))
return redirect(oid.get_next_url()) return redirect(oid.get_next_url())
@app.route('/logout') @app.route('/logout')
@ -42,69 +46,56 @@ def logout():
### TEMPORARY ### ### TEMPORARY ###
@app.route('/teamspeak')
def teamspeak():
return render_template('teamspeak.html')
@app.route('/list_events') @app.route('/list_events')
def list_events(): def list_events():
return "Events list!" return "Events list!"
@app.route('/friends')
def friends():
return render_template('friends.html')
@app.route('/community') @app.route('/community')
def community(): def community():
return "Community!" return "Community!"
@app.route('/ladder') @app.route('/ladder')
def ladder(): def ladder():
return "Ladder!" return "Ladder!"
### ###
#From league/doobs_blueprint.py
@app.route('/profile/<int:userid>')
def user_profile(userid):
user = User.query.filter_by(id=userid).first_or_404()
return render_template('profile.html', user=user)
'''
from flask import render_template, flash, redirect, g, request, url_for
from app import app, oid
@app.route('/login')
@oid.loginhandler
def login():
if g.user is not None:
return redirect(oid.get_next_url())
return oid.try_login('http://www.steamcommunity.com/openid')
@oid.after_login
def check_login(resp):
match = app.config['STEAM_ID_RE'].search(resp.identity_url)
return "none"
@app.route('/')
def main():
return render_template('main.html')
@app.route('/community')
def community():
return render_template('community.html', latest_posts=latest_posts())
# Teamspeak statistics page
@app.route('/teamspeak')
def teamspeak():
return render_template('teamspeak.html')
# Friends of doobs page
@app.route('/friends') @app.route('/friends')
def friends(): def friends():
return render_template('friends.html')
return render_template('friends.html')
@app.route('/teamspeak')
def teamspeak():
return render_template('teamspeak.html')
@app.route('/events')
def list_events():
return render_template('events.html')
@app.route('/events/<int:id>')
def event_summary(id):
return render_template('events.html')
@app.route('/ladder')
def ladder():
return render_template('ladder.html')
'''
# User profile page
@app.route('/user/<int:userid>')
def user_profile(userid):
user = User.query.filter_by(id=userid).first_or_404()
return render_template('profile.html', user=user)
# User random a-z challenge progress page
@app.route('/user/<int:userid>/random', methods=['POST', 'GET'])
def user_random_hero(userid):
user = User.query.filter_by(id=userid).first_or_404()
if request.method == 'POST':
if request.form.get('skip', False):
user.random_skip()
elif request.form.get('completed', False):
user.random_success()
return render_template('hero_random.html', user=user)
# User settings page
@app.route('/settings', methods=['POST', 'GET'])
def user_settings():
user = User.query.filter_by(id=g.user.id).first_or_404()
form = SettingsForm(obj=user)
if form.validate_on_submit():
g.user.bio_text = form.bio_text.data
g.user.twitch = form.twitch.data
db.session.commit()
flash('Settings updated!')
return render_template('profile.html', user=g.user)
else:
form.populate_obj(user)
return render_template('settings.html', user=g.user, form=form)
Loading…
Cancel
Save