Browse Source

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

master
Brandon Cornejo 8 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]
*.so
static/img/hero-images/*.png

2
__init__.py

@ -227,4 +227,4 @@ def utility_processor():
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
from random import choice
from flask.ext.sqlalchemy import SQLAlchemy
from sqlalchemy.ext.mutable import Mutable
from time import time
from app import db
from utils import parse_valve_heropedia, complete_hero_data
# Get a little of that Mongoness back
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):
id = db.Column(db.Integer, primary_key=True)
steam_id = db.Column(db.String(40), unique=True)
nickname = db.Column(db.String(80))
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
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):
return '<User {}>'.format(self.steam_id)
return '<User {}>'.format(self.nickname)
class TeamspeakData(db.Model):
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;
}
#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 {
height:35px;
@ -58,3 +53,30 @@ footer {
#streams-online > .uk-article > a > h4 {
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;
overflow:visible;
text-transform:uppercase;
color: rgba(105,211,50,0.75);
color: black;
padding:6px 10px 15px 40px;
}
#tavernAgility{

5
static/css/ts3_viewer.css

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

9
teamspeak.py

@ -8,10 +8,10 @@ from flask import url_for
from bs4 import BeautifulSoup
from app import app
from models import TeamspeakData
def getTeamspeakWindow(window=605800):
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()
def create_teamspeak_viewer():
@ -159,8 +159,11 @@ def create_teamspeak_viewer():
return "error: %s" % inst
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()
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

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 content %}
{% cache 60*5 %}
{#{% cache 60*5 %}#}
{% 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>
{% 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 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">
<a class="uk-modal-close uk-close"></a>
{{ ts3_viewer() | safe }}
</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 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>
{% 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>
{% endcache %}
{#{% endcache %}#}
{% endblock %}
{% block pagescripts %}
<script>
{% cache 60*5 %}
$(document).ready(function() {
{% cache 60*5 %}
// Add the streams
var stream_url = "https://api.twitch.tv/kraken/streams/";
var channels = ["dotanoobs", "bearhugdota", "kreejaffakree", "prettypenguins", "shaneomad"];
for (var idx in channels) {
$.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 %}
$(".dn-streamer, .dn-streamer-offline").on({
mouseover: function() {
$(this).addClass('dn-streamer-hover');
},
mouseleave: function() {
$(this).removeClass('dn-streamer-hover');
}
}, "div");
// Localize the events
$('.date').each( function( index ) {
var d = new Date($(this).text());
$(this).text( d.toLocaleDateString() + ' @ ' + d.toLocaleTimeString() );
});
// About-us toggle
$('#more,#less').click(function() {
$('#more').toggle();
$('#less').toggle();
$('#about-us-more').toggle();
});
});
</script>
{% endblock %}

37
templates/layout.html

@ -10,6 +10,7 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- 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/app.css') }}" />
@ -22,36 +23,18 @@
<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="" 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 -->
{% if g.user %}
<ul class="uk-navbar-nav uk-navbar-flip">
<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">
<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><a href="{{ url_for('user_settings', userid=g.user.id) }}">Settings</a></li>
<li><a href="{{ url_for('logout') }}">Logout</a></li>
</ul>
</div>
@ -65,10 +48,10 @@
<!-- Flash Error Messages -->
{% with messages = get_flashed_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>
{% for message in messages %}
<li class="uk-alert uk-alert-danger">{{ category }}:{{ message }}</li>
<li class="uk-alert uk-alert-danger">{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
@ -79,12 +62,12 @@
<!-- 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>
<!-- 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%}
<h3 class="uk-panel-title">{{ g.doob.name }}</h3>
<hr class="uk-nav-divider" />
{% 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' %}
</ul>
</div>

41
templates/profile.html

@ -4,29 +4,38 @@
{% block content %}
<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>
<!--Main content area -->
<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>
<!-- 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">
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>
<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>
{% 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'),
('friends', 'Friends'),
('list_events', 'Events'),
('community', 'Community'),
('ladder', 'Ladder'),
('teamspeak', 'TS3 Stats'),
] %}
<li{% if endpoint == request.endpoint %} class='uk-active' {% endif %}>
<a href="{{ url_for(endpoint) }}">{{ title }}</a>
</li>
{% 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-header">Social</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>
{% 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><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>
{% endif %}

2
templates/teamspeak.html

@ -26,7 +26,7 @@
<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-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">
{{ ts3_viewer() | safe }}
</div>

69
utils.py

@ -1,6 +1,10 @@
import requests
import re
from time import strptime, strftime, gmtime
from bs4 import BeautifulSoup
from itertools import product
from os import path, makedirs
from calendar import timegm
from app import app, cache
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'],
'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 {}
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
@app.template_filter('shorten')
def shorten_filter(s, num_words=20):
def shorten_filter(s, num_words=40):
space_iter = re.finditer('\s+', s)
output = u''
while num_words > 0:
@ -30,6 +57,7 @@ def shorten_filter(s, num_words=20):
@app.context_processor
def utility_processor():
''' For Teamspeak '''
@cache.memoize(60*5)
def ts3_viewer():
html = create_teamspeak_viewer()[0]
@ -63,6 +91,38 @@ def utility_processor():
for key, name in ISO3166_MAPPING.iteritems():
mapping[key.lower()] = ' '.join([word.capitalize() for word in name.split(' ')])
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):
return strftime('%B %d, %Y %H:%M:%S UTC', gmtime(timestamp))
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, \
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, \
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 datetime import datetime
from app import app, db, oid, cache
from models import User
from utils import get_steam_userinfo
from board import latest_news
from forms import SettingsForm
@app.before_request
def before_request():
g.user = None
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('/main')
def index():
return render_template("index.html", latest_news=latest_news())
@ -32,7 +36,7 @@ def create_or_login(resp):
g.user.avatar = steamdata['avatar']
db.session.commit()
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())
@app.route('/logout')
@ -42,69 +46,56 @@ def logout():
### TEMPORARY ###
@app.route('/teamspeak')
def teamspeak():
return render_template('teamspeak.html')
@app.route('/list_events')
def list_events():
return "Events list!"
@app.route('/friends')
def friends():
return render_template('friends.html')
@app.route('/community')
def community():
return "Community!"
@app.route('/ladder')
def 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')
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