Browse Source

Update before moving GIT heirarchy

master
Brandon Cornejo 11 years ago
parent
commit
3160967fa8
  1. 220
      __init__.py
  2. 24
      board.py
  3. 20
      forms.py
  4. 219
      models.py
  5. 32
      static/css/app.css
  6. 26
      teamspeak.py
  7. 66
      templates/friends.html
  8. 59
      templates/index.html
  9. 46
      templates/layout.html
  10. 27
      templates/profile.html
  11. 9
      templates/settings.html
  12. 17
      templates/sidenav.html
  13. 17
      templates/teamspeak.html
  14. 27
      utils.py
  15. 130
      views.py

220
__init__.py

@ -9,222 +9,4 @@ db = SQLAlchemy(app)
oid = OpenID(app) oid = OpenID(app)
cache = Cache(app, config={'CACHE_TYPE': app.config['CACHE_TYPE']}) cache = Cache(app, config={'CACHE_TYPE': app.config['CACHE_TYPE']})
from app import views
'''
from flask import Flask, render_template
from flask.ext.mongoengine import MongoEngine
from flask.ext.openid import OpenID
from flask.ext.cache import Cache
import utils
import ts3
app = Flask(__name__)
app.config.from_object('config')
#Setup mongo database
db = MongoEngine(app)
#Setup OpenID and Caching
oid = OpenID(app)
cache = Cache(app, config={'CACHE_TYPE': app.config['CACHE_TYPE']})
from app import views
@app.route('/')
def inx():
return render_template('main.html')
##### INTO UTILS LATER #####
RADIANT_TEAM = 2
DIRE_TEAM = 3
RADIANT_COLOR = 'b'
DIRE_COLOR = 'r'
def get_hero_data():
xhr = urllib2.build_opener().open(urllib2.Request("https://api.steampowered.com/IEconDOTA2_570/GETHeroes/v0001/?key="+DOTA2_API_KEY+"&language=en_us"))
data = json.load(xhr)
return data
@app.context_processor
def utility_processor():
@cache.memoize(60*5)
def ts3_viewer():
try:
server = ts3.TS3Server(app.config['TS3_HOST'], app.config['TS3_PORT'])
server.login(app.config['TS3_USERNAME'], app.config['TS3_PASSWORD'])
server.use(1)
serverinfo = server.send_command('serverinfo').data
channellist = server.send_command('channellist', opts=("limits", "flags", "voice", "icon")).data
clientlist = server.send_command('clientlist', opts=("away", "voice", "info", "icon", "groups", "country")).data
servergrouplist = server.send_command('servergrouplist').data
channelgrouplist = server.send_command('channelgrouplist').data
soup = BeautifulSoup()
div_tag = soup.new_tag('div')
div_tag['class'] ='devmx-webviewer'
soup.append(div_tag)
def construct_channels(parent_tag, cid):
num_clients = 0
for channel in channellist:
if int(channel['pid']) == int(cid):
# Construct the channel
channel_tag = soup.new_tag('div')
channel_tag['class'] = 'tswv-channel'
# Channel image
image_tag = soup.new_tag('span')
image_tag['class'] = 'tswv-image tswv-image-right'
if int(channel['channel_flag_password']) == 1:
image_tag['class'] += ' tswv-channel-password-right'
if int(channel['channel_flag_default']) == 1:
image_tag['class'] += ' tswv-channel-home'
if int(channel['channel_needed_talk_power']) > 0:
image_tag['class'] += ' tswv-channel-moderated'
if int(channel['channel_icon_id']) != 0:
raise NotImplementedError
image_tag.append(' ')
channel_tag.append(image_tag)
# Status image
status_tag = soup.new_tag('span')
status_tag['class'] = 'tswv-image'
if int(channel['channel_flag_password']) == 1:
status_tag['class'] += ' tswv-channel-password'
elif int(channel['total_clients']) == int(channel['channel_maxclients']):
status_tag['class'] += ' tswv-channel-full'
else:
status_tag['class'] += ' tswv-channel-normal'
status_tag.append(' ')
channel_tag.append(status_tag)
# Label
label_tag = soup.new_tag('span')
label_tag['class'] = 'tswv-label'
label_tag.append(channel['channel_name'])
channel_tag.append(label_tag)
# Clients
channel_tag, channel_clients = construct_clients(channel_tag, channel['cid'])
# Recurse through sub-channels, collecting total number of clients as we go
channel_tag, sub_clients = construct_channels(channel_tag, channel['cid'])
channel_clients += sub_clients
# Only show non-empty channels
if channel_clients > 0:
parent_tag.append(channel_tag)
num_clients += channel_clients
return parent_tag, num_clients
def construct_clients(parent_tag, cid):
num_clients = 0
for client in clientlist:
if int(client['cid']) == int(cid):
# Skip ServerQuery clients
if int(client['client_type']) == 1: continue
num_clients += 1
client_tag = soup.new_tag('div')
client_tag['class'] = 'tswv-client'
# Status image
status_tag = soup.new_tag('span')
status_tag['class'] = 'tswv-image'
if int(client['client_type']) == 1:
status_tag['class'] += ' tswv-client-query'
elif int(client['client_away']) == 1:
status_tag['class'] += " tswv-client-away"
elif int(client['client_input_muted']) == 1:
status_tag['class'] += " tswv-client-input-muted"
elif int(client['client_output_muted']) == 1:
status_tag['class'] += " tswv-client-output-muted"
elif int(client['client_input_hardware']) == 0:
status_tag['class'] += " tswv-client-input-muted-hardware"
elif int(client['client_output_hardware']) == 0:
status_tag['class'] += " tswv-client-output-muted-hardware"
elif (int(client['client_flag_talking']) == 1) and (int(client['client_is_channel_commander']) == 1):
status_tag['class'] += " tswv-client-channel-commander-talking"
elif int(client['client_is_channel_commander']) == 1:
status_tag['class'] += " tswv-client-channel-commander"
elif int(client['client_flag_talking']) == 1:
status_tag['class'] += " tswv-client-talking"
else:
status_tag['class'] += " tswv-client-normal"
status_tag.append(' ')
client_tag.append(status_tag)
# Country image
country_tag = soup.new_tag('span')
country_tag['class'] = 'tswv-image tswv-image-right'
country_tag['title'] = ' '.join([word.capitalize() for word in utils.ISO3166_MAPPING[client['client_country']].split(' ')])
country_tag['style'] = 'background: url("%s") center center no-repeat;' % url_for('static', filename='img/ts3_viewer/countries/%s.png' % client['client_country'].lower())
country_tag.append(' ')
client_tag.append(country_tag)
# Server group images
sgids = [int(sg) for sg in client['client_servergroups'].split(',')]
servergroups = [servergroup for servergroup in servergrouplist if int(servergroup['sgid']) in sgids]
servergroups.sort(key=operator.itemgetter('sortid'))
for servergroup in servergroups:
if not servergroup['iconid']: continue
img_fname = 'img/ts3_viewer/%s.png' % servergroup['iconid']
if not os.path.exists(os.path.join(app.static_folder, img_fname)):
continue
image_tag = soup.new_tag('span')
image_tag['class'] = 'tswv-image tswv-image-right'
image_tag['title'] = servergroup['name']
image_tag['style'] = 'background-image: url("%s")' % url_for('static', filename=img_fname)
image_tag.append(' ')
client_tag.append(image_tag)
# Check if client is in a moderated channel
channel = [channel for channel in channellist if int(channel['cid']) == int(client['cid'])][0]
if int(channel['channel_needed_talk_power']) > 0:
status_tag = soup.new_tag('span')
status_tag['class'] = 'tswv-image tswv-image-right'
if int(client['client_is_talker']) == 0:
status_tag['class'] += ' tswv-client-input-muted'
else:
status_tag['class'] += ' tswv-client-talkpower-granted'
status_tag.append(' ')
client_tag.append(status_tag)
# Label
label_tag = soup.new_tag('span')
label_tag['class'] = 'tswv-label'
label_tag.append(client['client_nickname'])
client_tag.append(label_tag)
parent_tag.append(client_tag)
return parent_tag, num_clients
div_tag, num_clients = construct_channels(div_tag, 0)
return soup.prettify()
except Exception as inst:
return "error: %s" % inst
def shorten_text(text, num_words=10):
text = utils.fix_bad_unicode(text)
space_iter = re.finditer('\s+', text)
output = u''
while num_words > 0:
match = space_iter.next()
if not match: break
output = text[:match.end()]
num_words -= 1
else:
output += '...'
return output
def num_unique_clients(teamspeak_data):
unique_clients = set()
for data in teamspeak_data:
unique_clients.update(data.clients)
return len(unique_clients)
def num_unique_clients_by_country(teamspeak_data):
unique_clients = {}
for data in teamspeak_data:
for client_id, client_data in data.clients.iteritems():
unique_clients[client_id] = (client_data['country'] or 'Unknown').lower()
country = {}
for client_id, country_code in unique_clients.iteritems():
country[country_code] = country.get(country_code, 0) + 1
return country
def country_abbreviation_mapping():
mapping = {}
for key, name in utils.ISO3166_MAPPING.iteritems():
mapping[key.lower()] = ' '.join([word.capitalize() for word in name.split(' ')])
return mapping
return dict(timestamp_to_js_date=utils.timestamp_to_js_date, ts3_viewer=ts3_viewer, shorten_text=shorten_text, getTeamspeakWindow=doob.getTeamspeakWindow,
num_unique_clients=num_unique_clients,
num_unique_clients_by_country=num_unique_clients_by_country,
country_abbreviation_mapping=country_abbreviation_mapping)
'''
from app import views, models

24
board.py

@ -1,17 +1,31 @@
import os import os
import re import re
from time import strftime, gmtime from time import strftime, gmtime
from hashlib import sha256
from peewee import * from peewee import *
from app import app, cache
db = MySQLDatabase(app.config['FORUM_NAME'], **{'passwd': app.config['FORUM_PASSWORD'],
from app import app, cache, db
board_db = MySQLDatabase(app.config['FORUM_NAME'], **{'passwd': app.config['FORUM_PASSWORD'],
'host': app.config['FORUM_HOST'], 'user': app.config['FORUM_USERNAME']}) 'host': app.config['FORUM_HOST'], 'user': app.config['FORUM_USERNAME']})
def registerUserForumId(user, username, password):
try:
u = Users.filter(name=username).get()
hashpass = sha256(password+app.config['FORUM_SALT']+u.pss).hexdigest()
if hashpass == u.password:
user.forum_id = u.id
db.session.commit()
return {"forum_name":u.name, "forum_id":u.id}
except DoesNotExist:
pass
return False
@cache.memoize(60*5) @cache.memoize(60*5)
def latest_news(num=3): def latest_news(num=3):
latest_news = [] latest_news = []
try: try:
db.connect()
board_db.connect()
news_forum = Forums.get(fn.Lower(Forums.title) % '%news%') news_forum = Forums.get(fn.Lower(Forums.title) % '%news%')
for thread in Threads.select().where( for thread in Threads.select().where(
Threads.forum == news_forum.id).order_by(Threads.date.desc()).limit(num): Threads.forum == news_forum.id).order_by(Threads.date.desc()).limit(num):
@ -32,7 +46,7 @@ def latest_news(num=3):
except Exception as e: except Exception as e:
latest_news.append({'title':'Error with forum db', 'text':e, 'url':''}) latest_news.append({'title':'Error with forum db', 'text':e, 'url':''})
finally: finally:
db.close()
board_db.close()
return latest_news return latest_news
class UnknownFieldType(object): class UnknownFieldType(object):
@ -40,7 +54,7 @@ class UnknownFieldType(object):
class BaseModel(Model): class BaseModel(Model):
class Meta: class Meta:
database = db
database = board_db
class Badges(BaseModel): class Badges(BaseModel):
color = IntegerField() color = IntegerField()

20
forms.py

@ -1,7 +1,23 @@
from flask.ext.wtf import Form
from wtforms import TextField, BooleanField, TextAreaField
from flask_wtf import Form
from wtforms import TextField, BooleanField, TextAreaField, PasswordField, SelectField, IntegerField, DateTimeField, validators
from datetime import datetime
class SettingsForm(Form): class SettingsForm(Form):
public = BooleanField('public', default=True) public = BooleanField('public', default=True)
logo = BooleanField('biglogo', default=True)
twitch = TextField('twitch') twitch = TextField('twitch')
bio_text = TextAreaField('bio_text') bio_text = TextAreaField('bio_text')
class EnableStatsForm(Form):
teamspeak_id = TextField('teamspeak_id')
forum_username = TextField('forum_username')
forum_password = PasswordField('forum_password')
class EventForm(Form):
name = TextField('name', [validators.Required()])
desc = TextAreaField('desc', [validators.Required()])
type = SelectField(u'Event Type', choices=[('coaching', 'Coaching'), ('inhouse', 'In-House'), ('tournament', 'Tournament'), ('other', 'Other')])
start_time = DateTimeField('start_time', format='%d.%m.%Y %H:%M')
end_time = DateTimeField('end_time', format='%d.%m.%Y %H:%M')
points = IntegerField('points', [validators.Required()])
reward_threshold = IntegerField('reward_threshold')

219
models.py

@ -1,11 +1,23 @@
import simplejson as json import simplejson as json
from datetime import datetime
from random import choice from random import choice
from flask.ext.sqlalchemy import SQLAlchemy from flask.ext.sqlalchemy import SQLAlchemy
from sqlalchemy.ext.mutable import Mutable from sqlalchemy.ext.mutable import Mutable
from time import time
from app import db
import board
from app import db, app
from utils import parse_valve_heropedia, complete_hero_data from utils import parse_valve_heropedia, complete_hero_data
# Model independant get_or_create
def get_or_create_instance(session, model, **kwargs):
instance = session.query(model).filter_by(**kwargs).first()
if instance:
return instance
else:
instance = model(**kwargs)
session.add(instance)
return instance
# Get a little of that Mongoness back # Get a little of that Mongoness back
class Json(db.TypeDecorator): class Json(db.TypeDecorator):
impl = db.Text impl = db.Text
@ -20,7 +32,7 @@ class Json(db.TypeDecorator):
value = json.loads(value) value = json.loads(value)
return value return value
# Mongoness factor - phase 2
class MutableDict(Mutable, dict): class MutableDict(Mutable, dict):
@classmethod @classmethod
def coerce(cls, key, value): def coerce(cls, key, value):
@ -46,27 +58,51 @@ class MutableDict(Mutable, dict):
self.update(self) self.update(self)
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)
forum_id = db.Column(db.Integer)
teamspeak_id = db.Column(db.String(200), 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))
admin = db.Column(db.Boolean)
bio_text = db.Column(db.String(4096)) bio_text = db.Column(db.String(4096))
created = db.Column(db.DateTime)
last_seen = db.Column(db.DateTime) last_seen = db.Column(db.DateTime)
twitch = db.Column(db.String(60)) twitch = db.Column(db.String(60))
random_heroes = db.Column(MutableDict.as_mutable(Json))
public = db.Column(db.Boolean)
logo = db.Column(db.Boolean)
points_from_events = db.Column(db.Integer)
points_from_ts3 = db.Column(db.Integer)
points_from_forum = db.Column(db.Integer)
ts3_starttime = db.Column(db.DateTime)
ts3_endtime = db.Column(db.DateTime)
ts3_rewardtime = db.Column(db.DateTime)
ts3_connections = db.Column(MutableDict.as_mutable(Json))
last_post_reward = db.Column(db.Integer)
@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
rv.random_heroes = {'current':None, 'completed':[]}
bio_text = ''
db.session.add(rv)
return rv
@classmethod
def get_or_create(self, steam_id):
return get_or_create_instance(db.session, User, steam_id=steam_id)
def __init__(self, steam_id):
self.steam_id = steam_id
self.random_heroes = {'current':None, 'completed':[]}
self.created = datetime.utcnow()
self.last_seen = datetime.utcnow()
self.bio_text = None
self.points_from_events = 0
self.points_from_ts3 = 0
self.points_from_forum = 0
self.admin = False
self.public = True
self.biglogo = True
@property @property
def random_hero(self): def random_hero(self):
@ -103,14 +139,161 @@ class User(db.Model):
db.session.commit() db.session.commit()
return self.random_hero return self.random_hero
def update_connection(self, reward_threshold=30):
now = datetime.utcnow()
self.ts3_starttime = self.ts3_starttime or now
self.ts3_endtime = now
# Add general TS3 points here
if self.ts3_endtime and self.ts3_rewardtime:
duration = (self.ts3_endtime - self.ts3_rewardtime) / 60.0
if duration > reward_threshold:
self.ts3_rewardtime = datetime.utcnow()
self.points_from_ts3 += 1
else:
self.ts3_rewardtime = datetime.utcnow()
self.last_seen = datetime.utcnow()
print self.ts3_starttime, self.ts3_endtime, self.ts3_rewardtime
db.session.commit();
def finalize_connection(self):
self.ts3_connections.append({'starttime': self.ts3_starttime, 'endtime': self.ts3_endtime})
self.ts3_startime = None
self.ts3_endtime = None
db.session.commit();
def update_forum_posts(self, reward_threshold=5):
if self.forum_id:
posts = board.Users.select().where(board.Users.id == int(self.forum_id))[0].posts
if self.last_post_reward:
num_points = (posts - self.last_post_reward) / reward_threshold
if num_points > 0:
self.points_from_forum += num_points
self.last_post_reward += num_points * reward_threshold
else:
# Initialize if this is the first reward
self.last_post_reward = posts
db.session.commit()
@property
def is_active(self):
return self.ts3_starttime and True or False
def __repr__(self): def __repr__(self):
return '<User {}>'.format(self.nickname)
return '<User {}>'.format(self.id)
class TeamspeakData(db.Model): class TeamspeakData(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
time = db.Column(db.Float())
time = db.Column(db.DateTime())
clients = db.Column(Json()) clients = db.Column(Json())
def __init__(self, clientlist): def __init__(self, clientlist):
self.time = time()
self.time = datetime.utcnow()
self.clients = clientlist self.clients = clientlist
class Event(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200))
desc = db.Column(db.String(4096))
type = db.Column(db.String(20))
start_time = db.Column(db.DateTime)
end_time = db.Column(db.DateTime)
points = db.Column(db.Integer)
reward_threshold = db.Column(db.Integer)
total_subchans = db.Column(db.Integer)
channels = db.Column(MutableDict.as_mutable(Json))
participants = db.Column(MutableDict.as_mutable(Json))
def __init__(self, id):
self.channels = {'event_cid':None, 'cids':[]}
@classmethod
def get_or_create(self, event_id):
return get_or_create_instance(db.session, Event, id=event_id)
@property
def cids(self):
return self.channels['cids']
@property
def event_cid(self):
return self.channels['event_cid']
@property
def channel_ids(self):
cids = self.channels['cids']
if self.channels['event_cid']:
cids.append(self.channels['event_cid'])
return cids
def create_channels(self):
import ts3
server = ts3.TS3Server(app.config['TS3_HOST'], app.config['TS3_PORT'])
server.login(app.config['TS3_USERNAME'], app.config['TS3_PASSWORD'])
server.use(1)
# Create the parent channel
if not self.event_cid:
# Find the LFG channel and place this one after
response = server.send_command('channelfind', keys={'pattern': 'Looking for Group'})
if response.is_successful:
cid = response.data[0]['cid']
response = server.send_command('channelcreate', keys={'channel_name':self.name.encode('utf-8'), 'channel_flag_semi_permanent':'1', 'channel_order':cid})
if response.is_successful:
self.channels['event_cid'] = response.data[0]['cid']
# Create the subchannels
if not self.cids:
cids = []
keys = {'channel_name':'Event Room #{}'.format(len(self.cids) + 1), 'channel_flag_semi_permanent':'1', 'cpid':self.event_cid.encode('utf-8')}
response = server.send_command('channelcreate', keys=keys)
if response.is_successful:
parent_cid = response.data[0]['cid']
cids.append(parent_cid)
else:
raise UserWarning("channelcreate failed")
response = server.send_command('channelcreate', keys={'channel_name':'Radiant Team', 'channel_flag_semi_permanent':'1', 'cpid':parent_cid})
if response.is_successful:
cids.append(response.data[0]['cid'])
response = server.send_command('channelcreate', keys={'channel_name':'Dire Team', 'channel_flag_semi_permanent':'1', 'cpid':parent_cid})
if response.is_successful:
cids.append(response.data[0]['cid'])
response = server.send_command('channelcreate', keys={'channel_name':'Spectators', 'channel_flag_semi_permanent':'1', 'cpid':parent_cid})
if response.is_successful:
cids.append(response.data[0]['cid'])
self.channels['cids'] = cids
db.session.commit()
def remove_channels(self):
import ts3
server = ts3.TS3Server(app.config['TS3_HOST'], app.config['TS3_PORT'])
server.login(app.config['TS3_USERNAME'], app.config['TS3_PASSWORD'])
server.use(1)
response = server.send_command('channeldelete', keys={'cid':self.event_cid.encode('utf-8'), 'force':'1'})
if response.is_successful:
self.channels = {'event_cid': None, 'cids':[]}
db.session.commit()
@property
def active(self):
current_time = datetime.utcnow()
return self.start_time < current_time and current_time < self.end_time
@property
def expired(self):
current_time = datetime.utcnow()
return self.end_time < curent_time
def add_participant(self, user):
entry = self.participants.setdefault(user, {'start_time': datetime.utcnow() })
entry['end_time'] = datetime.utcnow()
if 'points' not in entry and (entry['end_time'] - entry['start_time']) > self.reward_threshold:
user.points_from_events += self.points
entry['points'] = self.points
db.session.commit()
@property
def participants(self):
return tuple(self.participants)
def __repr__(self):
return '<Event {}>'.format(self.id)

32
static/css/app.css

@ -50,9 +50,6 @@ footer {
padding-bottom:80px; padding-bottom:80px;
} }
#streams-online > .uk-article > a > h4 {
margin:0px;
}
#tsviewer > .uk-modal-dialog { #tsviewer > .uk-modal-dialog {
max-height: 80%; max-height: 80%;
overflow-y: scroll; overflow-y: scroll;
@ -63,16 +60,21 @@ footer {
} }
.dn-streamer, .dn-streamer-offline { .dn-streamer, .dn-streamer-offline {
display: inline-block; display: inline-block;
width: 110px;
padding: 5px;
margin: 5px;
width: 105px;
padding: 2px;
margin: 2px;
}
#streams p.uk-text-bold {
font-size: 12px;
} }
.dn-streamer-offline > img { .dn-streamer-offline > img {
height:50px; height:50px;
} }
.dn-streamer-hover { .dn-streamer-hover {
background:red;
background:#CACACA;
} }
.dn-hero-icon { .dn-hero-icon {
@ -80,3 +82,19 @@ footer {
padding:4px; padding:4px;
margin:2px; margin:2px;
} }
#streams {
padding:10px;
}
#events_small > dl.uk-description-list-line > dt, #events_large > dl.uk-description-list-line > dt {
border-top: none;
}
#events_small .uk-badge, #events_large .uk-badge {
float:right;
}
#profile_links > a {
font-size:14px;
}

26
teamspeak.py

@ -1,19 +1,35 @@
from flask import url_for
import operator import operator
import os import os
import ts3 import ts3
import time
import requests import requests
from datetime import datetime, timedelta
from xml.etree import ElementTree from xml.etree import ElementTree
from flask import url_for
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from app import app
from app import app, db
def getTeamspeakWindow(window=605800):
current_time = time.time()
def getTeamspeakWindow(window=timedelta(weeks=1)):
current_time = datetime.utcnow()
from models import TeamspeakData 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 registerUserTeamspeakId(user, tsid):
server = ts3.TS3Server(app.config['TS3_HOST'], app.config['TS3_PORT'])
server.login(app.config['TS3_USERNAME'], app.config['TS3_PASSWORD'])
server.use(1)
response = server.send_command('clientdbfind', {'pattern':tsid.encode('utf-8')}, ('uid',))
if response.is_successful:
cdbid = response.data[0]['cldbid']
user.teamspeak_id = tsid
sgid = [entry['sgid'] for entry in server.send_command('servergrouplist').data if entry['name'] == 'Normal' and entry['type'] == '1'][0]
server.send_command('servergroupaddclient', {'sgid': sgid, 'cldbid': cdbid})
db.session.commit()
return True
return False
def create_teamspeak_viewer(): def create_teamspeak_viewer():
try: try:
server = ts3.TS3Server(app.config['TS3_HOST'], app.config['TS3_PORT']) server = ts3.TS3Server(app.config['TS3_HOST'], app.config['TS3_PORT'])

66
templates/friends.html

@ -16,7 +16,7 @@
</div> </div>
<div class="uk-width-large-1-2 uk-width-medium-1-1 uk-text-center uk-panel botpad"> <div class="uk-width-large-1-2 uk-width-medium-1-1 uk-text-center uk-panel botpad">
<h2>Internet Friendlies:</h2> <h2>Internet Friendlies:</h2>
<p>DotA related websites worth the occasional visit. We know they are no DotaNoobs, but we set a pretty high quality standard.</p>
<p>DotA related websites worth the occasional visit. We know they are no DotaNoobs, but we set a pretty high quality standard around here.</p>
<ul> <ul>
<li><a href="http://www.reddit.com/r/DotA2/">Reddit's r/dota2</a></li> <li><a href="http://www.reddit.com/r/DotA2/">Reddit's r/dota2</a></li>
<li><a href="http://www.dotainsight.com">DotaInsight Podcast</a></li> <li><a href="http://www.dotainsight.com">DotaInsight Podcast</a></li>
@ -26,10 +26,7 @@
</div> </div>
<div class="uk-width-large-1-2 uk-width-medium-1-1 uk-text-center uk-panel"> <div class="uk-width-large-1-2 uk-width-medium-1-1 uk-text-center uk-panel">
<h2>Streams We Like</h2> <h2>Streams We Like</h2>
<ul id="streams-online">
</ul>
<hr id="stream-divider" class="uk-article-divider" style="display:none;" />
<ul id="streams-offline">
<ul id="streams">
</ul> </ul>
</div> </div>
</div> </div>
@ -38,34 +35,49 @@
{% block pagescripts %} {% block pagescripts %}
<script> <script>
$(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 = ["sheevergaming", "purgegamers", "synderen", "luminousinverse", "lddota"]
var channels = ["2gd", "purgegamers", "synderen", "luminousinverse", "thegdstudio"]
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 = "<p 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 + "</p>";
$(jquery_selector).append(span_text);
$(jquery_selector).append("<img src='" + data.stream.preview.medium + "' class='' />")
$('#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 %}
$("#streams").on({
mouseover: function() {
$(this).addClass('dn-streamer-hover');
},
mouseleave: function() {
$(this).removeClass('dn-streamer-hover');
}
}, "div");
}); });
</script> </script>
{% endblock %} {% endblock %}

59
templates/index.html

@ -5,32 +5,16 @@
{% 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() %}
{% set latest_news = get_latest_news() %}
<div class="uk-grid"> <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 id="events_large" class="uk-width-1-2 uk-visible-large uk-panel uk-panel-space">
{% include "events_widget.html" %}
</div> </div>
<div class="uk-width-large-1-2 uk-width-medium-1-1 uk-panel uk-panel-box uk-text-center">
<div id="teamspeak" 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> <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> <h1 class="uk-panel-title">Teamspeak</h1>
<div class="uk-grid uk-margin-bottom"> <div class="uk-grid uk-margin-bottom">
@ -57,11 +41,15 @@
</div> </div>
</div> </div>
<div id="streams" class="uk-width-1-1 uk-text-center uk-panel uk-panel-space">
<div id="events_small" class="uk-width-1-1 uk-hidden-large uk-panel uk-panel-space">
{% include "events_widget.html" %}
</div>
<div id="streams" class="uk-width-1-1 uk-text-center uk-panel">
</div> </div>
<div class="uk-width-1-1 uk-panel uk-panel-header">
<h4 class="uk-panel-title">News and Announcements</h4>
<div class="uk-width-1-1 uk-panel uk-panel-header uk-panel-space">
<h1 class="uk-panel-title">News and Announcements</h1>
{% for news in latest_news %} {% for news in latest_news %}
<article class="uk-article dn-news-article"> <article class="uk-article dn-news-article">
<h4 class="uk-article-title" title="{{ news['title'] }}"><a href="{{ news['url'] }}">{{ news['title'] }}</a></h4> <h4 class="uk-article-title" title="{{ news['title'] }}"><a href="{{ news['url'] }}">{{ news['title'] }}</a></h4>
@ -70,15 +58,22 @@
</article> </article>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
{#{% endcache %}#}
{% endcache %}
{% endblock %} {% endblock %}
{% block pagescripts %} {% block pagescripts %}
<script> <script>
{% cache 60*5 %}
$(document).ready(function() { $(document).ready(function() {
if ($('#events_large > dl').length < 1) {
var msg = "<h2>Events</h2><em>No events planned for the near future. Suggest one on the forum!</em>"
$('#events_small').append(msg);
$('#events_large').append(msg);
}
{% 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"];
@ -113,7 +108,7 @@ $(document).ready(function() {
{% endcache %} {% endcache %}
$(".dn-streamer, .dn-streamer-offline").on({
$("#streams").on({
mouseover: function() { mouseover: function() {
$(this).addClass('dn-streamer-hover'); $(this).addClass('dn-streamer-hover');
}, },
@ -121,12 +116,6 @@ $(document).ready(function() {
$(this).removeClass('dn-streamer-hover'); $(this).removeClass('dn-streamer-hover');
} }
}, "div"); }, "div");
// Localize the events
$('.date').each( function( index ) {
var d = new Date($(this).text());
$(this).text( d.toLocaleDateString() + ' @ ' + d.toLocaleTimeString() );
});
}); });
</script> </script>
{% endblock %} {% endblock %}

46
templates/layout.html

@ -10,13 +10,14 @@
<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="//cdnjs.cloudflare.com/ajax/libs/uikit/2.4.0/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') }}" />
<!-- Javascript includes --> <!-- Javascript includes -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script src="{{ url_for('static', filename='js/uikit.min.js') }}"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/uikit/2.4.0/js/uikit.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.5.1/moment.min.js"></script>
</head> </head>
<body> <body>
<!-- Navigation bar --> <!-- Navigation bar -->
@ -33,25 +34,27 @@
<li><a href="{{ url_for('user_profile', userid=g.user.id) }}">Profile</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="{{ 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><a href="http://board.dotanoobs.com/?page=lastposts">Latest Posts</a></li>
{% if g.user.admin %}
<li><a href="{{ url_for('event_edit') }}">Add Event</a></li>
{% endif %}
<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('user_settings') }}">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>
</ul> </ul>
{% else %} {% else %}
<a class="uk-navbar-flip uk-navbar-content" href="{{ url_for('login') }}"><img src="http://cdn.steamcommunity.com/public/images/signinthroughsteam/sits_large_border.png" /></a>
<a class="uk-navbar-flip uk-navbar-content" href="{{ url_for('login') }}"><img src="http://steamcommunity-a.akamaihd.net/public/images/signinthroughsteam/sits_large_border.png" /></a>
{% endif %} {% endif %}
<div class="uk-navbar-content uk-navbar-center uk-hidden-small uk-text-bold">DotaNoobs</div> <div class="uk-navbar-content uk-navbar-center uk-hidden-small uk-text-bold">DotaNoobs</div>
</nav> </nav>
<!-- Flash Error Messages --> <!-- Flash Error Messages -->
{% with messages = get_flashed_messages() %}
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %} {% if messages %}
<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">{{ message }}</li>
<ul class="uk-list flashes uk-width-2-3 uk-container-center">
{% for category, message in messages %}
<li class="uk-alert uk-alert-{{ category }}">{{ message }} <a href="#" class="uk-alert-close uk-close"></a> </li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endif %} {% endif %}
@ -60,7 +63,9 @@
<!-- Start Main Container --> <!-- Start Main Container -->
<div id="container" class="uk-grid"> <div id="container" class="uk-grid">
<!-- Big top logo --> <!-- Big top logo -->
{% if not g.user.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>
{% endif %}
<!-- Side navigation --> <!-- Side navigation -->
<div class="uk-visible-large uk-width-1-5 uk-panel"> <div class="uk-visible-large uk-width-1-5 uk-panel">
{% if g.doob%} {% if g.doob%}
@ -78,9 +83,10 @@
{% endif %} {% endif %}
<ul class="uk-nav uk-nav-offcanvas" data-uk-nav> <ul class="uk-nav uk-nav-offcanvas" data-uk-nav>
{% include 'sidenav.html' %} {% include 'sidenav.html' %}
</ul>
</div>
</ul>
</div>
</div> </div>
<!-- Main content area --> <!-- Main content area -->
<div class="uk-width-large-4-5 uk-width-medium-5-5"> <div class="uk-width-large-4-5 uk-width-medium-5-5">
{% block content %} {% block content %}
@ -91,6 +97,7 @@
<!-- Footer section --> <!-- Footer section -->
<footer class="uk-clearfix uk-align-center"> <footer class="uk-clearfix uk-align-center">
{% cache 60*5 %}
<div class="uk-float-left uk-visible-large"> <div class="uk-float-left uk-visible-large">
<a href="http://flask.pocoo.org/"><img src="{{ url_for('static', filename='img/powered-by-flask-s.png') }}" alt="Flask"></a> &amp; <a href="http://flask.pocoo.org/"><img src="{{ url_for('static', filename='img/powered-by-flask-s.png') }}" alt="Flask"></a> &amp;
<a href="http://store.steampowered.com/"><img src="{{ url_for('static', filename='img/steam.png') }}" alt="Steam"></a> <a href="http://store.steampowered.com/"><img src="{{ url_for('static', filename='img/steam.png') }}" alt="Steam"></a>
@ -101,10 +108,23 @@
<a href="http://www.youtube.com/user/DotaNoobsVods"><img src="{{ url_for('static', filename='img/youtube.png') }}" alt="YouTube" /></a> <a href="http://www.youtube.com/user/DotaNoobsVods"><img src="{{ url_for('static', filename='img/youtube.png') }}" alt="YouTube" /></a>
<a href="emailto:admin@dotanoobs.com"><img src="{{ url_for('static', filename='img/email.png') }}" /></a> <a href="emailto:admin@dotanoobs.com"><img src="{{ url_for('static', filename='img/email.png') }}" /></a>
</div> </div>
{% endcache %}
</footer> </footer>
<!-- Page-specific javascript here --> <!-- Page-specific javascript here -->
{% block pagescripts %} {% block pagescripts %}
{% endblock %} {% endblock %}
<script type='text/javascript'>
$(document).ready(function() {
$("li.uk-alert > a.uk-alert-close").on('click', function(){
$(this).parent().hide();
});
$(document).ready(function() {
$('.date').each(function (index) {
var d = moment.utc($(this).text()+" UTC");
$(this).html(d.local().format("h:mm A on MMM Do"));
});
});
});
</script>
</body> </body>
</html> </html>

27
templates/profile.html

@ -7,22 +7,31 @@
<div class="uk-width-2-3"> <div class="uk-width-2-3">
<h2 class="uk-float-left"><img class="" src="{{ user.avatar }}" />&nbsp;{{ user.nickname }}</h2> <h2 class="uk-float-left"><img class="" src="{{ user.avatar }}" />&nbsp;{{ user.nickname }}</h2>
</div> </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 id="profile_links" class="uk-width-1-3 uk-text-center">
<div class="uk-panel">
{% if user.public %}
<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>
{% endif %}
</div>
</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">
{% if user.public %}
{% if user.bio_text == None %} {% if user.bio_text == None %}
<em class="uk-text-danger">This user's profile bio is empty!</em> <em class="uk-text-danger">This user's profile bio is empty!</em>
{% else %} {% else %}
<em class="uk-text-bold">{{ user.bio_text }}</em> <em class="uk-text-bold">{{ user.bio_text }}</em>
{% endif %} {% endif %}
{% else %}
<em class="uk-text-danger">This user profile is set to private</em>
{% endif %}
{% if user.id == g.user.id %}&nbsp;<a href="{{ url_for('user_settings')}}"><i class="uk-icon-edit"></i></a>{% 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 uk-panel-box-secondary"> <div class="uk-width-large-1-3 uk-width-medium-1-1 uk-panel uk-panel-box uk-panel-box-secondary">
{% if user.public %}
<div class="uk-container-center uk-text-center"> <div class="uk-container-center uk-text-center">
<span class="uk-text-bold">Current Hero</span><br/> <span class="uk-text-bold">Current Hero</span><br/>
<span>{{ user.random_hero['localized_name'] }}</span><br/> <span>{{ user.random_hero['localized_name'] }}</span><br/>
@ -31,11 +40,13 @@
<span>View A-Z Progress</span> <span>View A-Z Progress</span>
</a> </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 class="uk-list uk-list-space uk-list-striped uk-text-center uk-text-small">
<li>Completed <span id='rands'>{{ user.random_heroes.completed | length }}</span> heroes in A-Z</li>
<li>Has <span id='points_total'>0</span> doobs points</li>
<li>Last seen at <span class='date'> {{ user.last_seen | js_datetime }}</span></li>
<li>Doob since <span class='date'> {{ user.created | js_datetime }}</span></li>
</ul> </ul>
{% endif %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

9
templates/settings.html

@ -5,21 +5,20 @@
{% block content %} {% block content %}
<div class="uk-grid" data-uk-grid-margin> <div class="uk-grid" data-uk-grid-margin>
<div class="uk-width-2-3"> <div class="uk-width-2-3">
<h2 class="uk-float-left"><img class="" src="{{ user.avatar }}" />&nbsp;{{ user.nickname }}</h2>
<h2 class="uk-float-left"><img class="" src="{{ g.user.avatar }}" />&nbsp;{{ g.user.nickname }}'s Settings</h2>
</div> </div>
<div class="uk-width-1-3 uk-text-center"> <div class="uk-width-1-3 uk-text-center">
<a href="{{ url_for('enable_statistics') }}" class="uk-button uk-button-danger">Enable Statistics <i class="uk-icon-star"></i></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-1-1 uk-panel">
<form class="uk-form uk-form-width-large" action="" method="post" name="settings"> <form class="uk-form uk-form-width-large" action="" method="post" name="settings">
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<fieldset data-uk-margin> <fieldset data-uk-margin>
<legend>Settings</legend>
<div class="uk-form-row"> <div class="uk-form-row">
<ul class="uk-list"> <ul class="uk-list">
<li><label class="uk-form-label"> {{ form.public }} Public Profile</label></li> <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>
<li><label class="uk-form-label"> {{ form.logo }} Hide Big Logo</label></li>
</ul> </ul>
</div> </div>
<div class="uk-form-row"> <div class="uk-form-row">

17
templates/sidenav.html

@ -17,15 +17,16 @@
</li> </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://www.youtube.com/user/DotaNoobsVods"><i class="uk-icon-youtube"> YouTube</i></a></li>
<li><a href="http://webchat.oftc.net/?channels=dotanoobs&uio=d4"><i class="uk-icon-comments-alt"> IRC Chat</i></a></li>
<li><a href="http://board.dotanoobs.com"><i class="uk-icon-group"></i> Board</a></li>
<li><a href="http://www.youtube.com/user/DotaNoobsVods"><i class="uk-icon-youtube"></i> YouTube</a></li>
<li><a href="http://webchat.oftc.net/?channels=dotanoobs&uio=d4"><i class="uk-icon-comments"></i> IRC Chat</a></li>
<li class="uk-nav-divider"></li> <li class="uk-nav-divider"></li>
<li class="uk-nav-header">Controls</li>
{% if not g.user%} {% if not g.user%}
<li><a href="{{ url_for('login') }}"><i class="uk-icon-off"> Login</i></a></li>
<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-sign-in"></i> Login</a></li>
<li><a href="{{ url_for('login') }}"><i class="uk-icon-star"></i> Register</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><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 {% if request.endpoint == 'user_profile' %} class='uk-active' {% endif %}><a href="{{ url_for('user_profile', userid=g.user.id) }}"><i class="uk-icon-dashboard"></i> Profile</a></li>
<li {% if request.endpoint == 'user_settings' %} class='uk-active' {% endif %}><a href="{{ url_for('user_settings') }}"><i class="uk-icon-cogs"></i> Settings</a></li>
<li><a href="{{ url_for('logout') }}"><i class="uk-icon-sign-out"></i> Logout</a></li>
{% endif %} {% endif %}

17
templates/teamspeak.html

@ -13,9 +13,9 @@
<h3 class="uk-text-bold uk-text-center">Recent Activity</h3> <h3 class="uk-text-bold uk-text-center">Recent Activity</h3>
<div class="uk-panel"> <div class="uk-panel">
<ul> <ul>
<li>Active users connected: <span id="unique_clients"></span></li>
<li>Total users connected: <span id="current_clients">{{ ts3_current_clients() }}</span></li>
<li>Countries active on server: <span id="country_clients"></span></li>
<li>Users currently connected: <span id="current_clients"></span></li>
<li>Unique users this week: <span id="unique_clients"></span></li>
<li>Countries active this week: <span id="country_clients"></span></li>
</div> </div>
<div class="uk-panel" id="teamspeak_active_users"></div> <div class="uk-panel" id="teamspeak_active_users"></div>
<div class="uk-panel" id="teamspeak_map"></div> <div class="uk-panel" id="teamspeak_map"></div>
@ -38,13 +38,14 @@
{% block pagescripts %} {% block pagescripts %}
{% cache 60*5 %} {% cache 60*5 %}
{% set teamspeak_data = get_teamspeak_window() %} {% set teamspeak_data = get_teamspeak_window() %}
<script src="http://code.highcharts.com/3.0.1/highcharts.js"></script>
<script type="text/javascript" src="http://github.highcharts.com/75c66eb3/modules/map.src.js"></script>
<script type="text/javascript" src="http://github.highcharts.com/75c66eb3/modules/data.src.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/highcharts/3.0.7/highcharts.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/highcharts/3.0.7/modules/map.src.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/highcharts/3.0.7/modules/data.src.js"></script>
<script type="text/javascript" src="{{ url_for('static', filename='js/world-map-shapes.js') }}"></script> <script type="text/javascript" src="{{ url_for('static', filename='js/world-map-shapes.js') }}"></script>
<script> <script>
$(document).ready(function() { $(document).ready(function() {
$('#unique_clients').html("{{ ts3_active_clients(teamspeak_data) }}"); $('#unique_clients').html("{{ ts3_active_clients(teamspeak_data) }}");
$('#current_clients').html("{{ ts3_current_clients() }}");
$('#country_clients').html("{{ ts3_countries_active(teamspeak_data) }}"); $('#country_clients').html("{{ ts3_countries_active(teamspeak_data) }}");
Highcharts.setOptions({ Highcharts.setOptions({
global: { global: {
@ -90,7 +91,7 @@ $(document).ready(function() {
type: 'areaspline', type: 'areaspline',
data: [ data: [
{% for data in teamspeak_data %} {% for data in teamspeak_data %}
[new Date('{{ timestamp_to_js_date(data.time) }}').valueOf(), {{ data.clients | count }}],
[new Date('{{ data.time | js_datetime }}').valueOf(), {{ data.clients | count }}],
{% endfor %} {% endfor %}
], ],
}], }],
@ -155,7 +156,7 @@ $(document).ready(function() {
map_options.series[0].data.push({ map_options.series[0].data.push({
y: num, y: num,
name: country_names[key], name: country_names[key],
path: Highcharts.pathToArray(shapes[key]),
path: shapes[key],
states: { states: {
hover: { hover: {
color: '#FF7F00' color: '#FF7F00'

27
utils.py

@ -7,6 +7,7 @@ from os import path, makedirs
from calendar import timegm from calendar import timegm
from app import app, cache from app import app, cache
from board import latest_news
from teamspeak import create_teamspeak_viewer, getTeamspeakWindow, ISO3166_MAPPING from teamspeak import create_teamspeak_viewer, getTeamspeakWindow, ISO3166_MAPPING
def get_steam_userinfo(steam_id): def get_steam_userinfo(steam_id):
@ -55,6 +56,22 @@ def shorten_filter(s, num_words=40):
output += '...' output += '...'
return output return output
@app.template_filter('js_datetime')
def js_datetime(dt):
return dt.strftime('%m %d %Y %H:%M')
@app.template_filter('event_badge')
def event_badge(t):
if t == 'coaching':
badge = "<div class='uk-badge'>Coaching</div>"
elif t == 'inhouse':
badge = "<div class='uk-badge uk-badge-success'>Inhouse</div>"
elif t == 'tournament':
badge = "<div class='uk-badge uk-badge-danger'>Tournament</div>"
else:
badge = "<div class='uk-badge uk-badge-warning'>Other</div>"
return badge;
@app.context_processor @app.context_processor
def utility_processor(): def utility_processor():
''' For Teamspeak ''' ''' For Teamspeak '''
@ -123,12 +140,10 @@ def utility_processor():
img.write(i) img.write(i)
return img_file return img_file
''' Misc ''' ''' Misc '''
def timestamp_to_js_date(timestamp):
return strftime('%B %d, %Y %H:%M:%S UTC', gmtime(timestamp))
def js_date_to_timestamp(date):
return timegm(strptime(date, '%s, %d %b %Y %H:%M:%S %Z'))
def get_latest_news(num=3):
return latest_news(num)
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, \
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, hero_image_large=hero_image_large, hero_image_small=hero_image_small, \ 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)
heropedia=parse_valve_heropedia, total_hero_pool=total_hero_pool, get_latest_news=get_latest_news)

130
views.py

@ -1,11 +1,14 @@
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 datetime import datetime
from functools import wraps
from app import app, db, oid, cache from app import app, db, oid, cache
from models import User
from models import User, Event
from utils import get_steam_userinfo from utils import get_steam_userinfo
from board import latest_news
from forms import SettingsForm
from board import registerUserForumId
from teamspeak import registerUserTeamspeakId
from forms import SettingsForm, EventForm, EnableStatsForm
@app.before_request @app.before_request
def before_request(): def before_request():
@ -16,9 +19,29 @@ def before_request():
g.user.last_seen = datetime.utcnow() g.user.last_seen = datetime.utcnow()
db.session.commit() db.session.commit()
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if g.user is None:
return redirect(url_for('login', next=request.url))
return f(*args, **kwargs)
return decorated_function
def flash_form_errors(form):
for field, errors in form.errors.items():
for error in errors:
flash(u"Error in the %s field - %s" % (getattr(form,field).label.text, error), 'danger')
#
# Application routes
#
@app.route('/') @app.route('/')
def index(): def index():
return render_template("index.html", latest_news=latest_news())
active = Event.query.filter(Event.start_time <= datetime.utcnow(), Event.end_time > datetime.utcnow()).all()
upcoming = Event.query.filter(Event.start_time > datetime.utcnow()).limit(2).all()
return render_template("index.html", active_events=active, upcoming_events=upcoming)
@app.route('/login') @app.route('/login')
@oid.loginhandler @oid.loginhandler
@ -36,32 +59,20 @@ 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), 'success')
return redirect(oid.get_next_url()) return redirect(oid.get_next_url())
@app.route('/logout') @app.route('/logout')
def logout(): def logout():
session.pop('user_id', None) session.pop('user_id', None)
return redirect(oid.get_next_url())
### TEMPORARY ###
@app.route('/list_events')
def list_events():
return "Events list!"
@app.route('/community')
def community():
return "Community!"
@app.route('/ladder')
def ladder():
return "Ladder!"
### ###
g.user = None
flash("You have been logged out.", 'success')
return redirect(url_for('index'))
# Teamspeak statistics page # Teamspeak statistics page
@app.route('/teamspeak') @app.route('/teamspeak')
def teamspeak(): def teamspeak():
return render_template('teamspeak.html')
return render_template('teamspeak.html')
# Friends of doobs page # Friends of doobs page
@app.route('/friends') @app.route('/friends')
@ -87,15 +98,80 @@ def user_random_hero(userid):
# User settings page # User settings page
@app.route('/settings', methods=['POST', 'GET']) @app.route('/settings', methods=['POST', 'GET'])
@login_required
def user_settings(): def user_settings():
user = User.query.filter_by(id=g.user.id).first_or_404() user = User.query.filter_by(id=g.user.id).first_or_404()
form = SettingsForm(obj=user) form = SettingsForm(obj=user)
if form.validate_on_submit(): if form.validate_on_submit():
g.user.bio_text = form.bio_text.data
g.user.twitch = form.twitch.data
form.populate_obj(user)
if user.bio_text == '':
user.bio_text = None
db.session.commit() db.session.commit()
flash('Settings updated!')
return render_template('profile.html', user=g.user)
flash('Settings updated!', 'success')
return render_template('settings.html', form=form)
# Enable user statistics page
@app.route('/settings/enable_stats', methods=['POST', 'GET'])
@login_required
def enable_statistics():
# TODO: update user_settings to use g.user, avoid extra queries
form = EnableStatsForm(obj=g.user)
if form.validate_on_submit():
forum_data = registerUserForumId(g.user, form.forum_username.data, form.forum_password.data)
if forum_data:
flash('Forum account \''+forum_data['forum_name']+'\' linked!', 'success')
else:
flash('Forum account credentials invalid', 'danger')
if registerUserTeamspeakId(g.user, form.teamspeak_id.data):
flash('Teamspeak account linked successfully!', 'success')
else:
flash('Teamspeak ID not found in client history', 'danger')
return render_template('enable_stats.html', form=form)
# Events list
@app.route('/events', methods=['GET'])
def list_events():
now = datetime.utcnow()
active = Event.query.filter(Event.start_time <= now, Event.end_time > now).all()
upcoming = Event.query.filter(Event.start_time > now).all()
expired = Event.query.filter(Event.end_time < now).all()
return render_template('list_events.html', active=active, upcoming=upcoming, expired=expired)
# Show event info
@app.route('/event/<int:eventid>', methods=['GET'])
def show_event(eventid):
event = Event.query.filter_by(id=eventid).first_or_404()
return render_template('show_event.html', event=event)
# Event creation page
@app.route('/event/edit', methods=['POST', 'GET'])
@login_required
def event_edit():
if not g.user.admin:
flash('Access Denied: You cannot create/edit events.', 'danger')
return redirect(url_for('index'))
eventid = request.args.get('eventid')
event = Event.get_or_create(eventid)
form = EventForm(obj=event)
if form.validate_on_submit():
form.populate_obj(event)
db.session.add(event)
db.session.commit()
flash('New event created!', 'success') if eventid is None else flash('Event updated successfully!', 'success')
return redirect(url_for('show_event', eventid=event.id))
else: else:
form.populate_obj(user)
return render_template('settings.html', user=g.user, form=form)
flash_form_errors(form)
return render_template('edit_event.html', event=event, form=form)
# Event deletion call
@app.route('/event/<int:eventid>/delete', methods=['GET'])
@login_required
def event_delete(eventid):
if g.user.admin:
event = Event.query.filter_by(id=eventid).first_or_404()
flash('Info: Event "{}" deleted successfully.'.format(event.name), 'success')
db.session.delete(event)
db.session.commit()
else:
flash('Access Denied: You cannot delete events.', 'danger')
return redirect(url_for('index'))
Loading…
Cancel
Save