Update before moving GIT heirarchy
This commit is contained in:
parent
23e04edafd
commit
3160967fa8
220
__init__.py
220
__init__.py
@ -9,222 +9,4 @@ db = SQLAlchemy(app)
|
||||
oid = OpenID(app)
|
||||
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
24
board.py
@ -1,17 +1,31 @@
|
||||
import os
|
||||
import re
|
||||
from time import strftime, gmtime
|
||||
from hashlib import sha256
|
||||
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']})
|
||||
|
||||
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)
|
||||
def latest_news(num=3):
|
||||
latest_news = []
|
||||
try:
|
||||
db.connect()
|
||||
board_db.connect()
|
||||
news_forum = Forums.get(fn.Lower(Forums.title) % '%news%')
|
||||
for thread in Threads.select().where(
|
||||
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:
|
||||
latest_news.append({'title':'Error with forum db', 'text':e, 'url':''})
|
||||
finally:
|
||||
db.close()
|
||||
board_db.close()
|
||||
return latest_news
|
||||
|
||||
class UnknownFieldType(object):
|
||||
@ -40,7 +54,7 @@ class UnknownFieldType(object):
|
||||
|
||||
class BaseModel(Model):
|
||||
class Meta:
|
||||
database = db
|
||||
database = board_db
|
||||
|
||||
class Badges(BaseModel):
|
||||
color = IntegerField()
|
||||
|
20
forms.py
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):
|
||||
public = BooleanField('public', default=True)
|
||||
logo = BooleanField('biglogo', default=True)
|
||||
twitch = TextField('twitch')
|
||||
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')
|
||||
|
221
models.py
221
models.py
@ -1,11 +1,23 @@
|
||||
import simplejson as json
|
||||
from datetime import datetime
|
||||
from random import choice
|
||||
from flask.ext.sqlalchemy import SQLAlchemy
|
||||
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
|
||||
|
||||
# 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
|
||||
class Json(db.TypeDecorator):
|
||||
impl = db.Text
|
||||
@ -20,7 +32,7 @@ class Json(db.TypeDecorator):
|
||||
value = json.loads(value)
|
||||
return value
|
||||
|
||||
|
||||
# Mongoness factor - phase 2
|
||||
class MutableDict(Mutable, dict):
|
||||
@classmethod
|
||||
def coerce(cls, key, value):
|
||||
@ -46,27 +58,51 @@ class MutableDict(Mutable, dict):
|
||||
self.update(self)
|
||||
|
||||
|
||||
|
||||
class User(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=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))
|
||||
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))
|
||||
created = db.Column(db.DateTime)
|
||||
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
|
||||
rv.random_heroes = {'current':None, 'completed':[]}
|
||||
bio_text = ''
|
||||
db.session.add(rv)
|
||||
return rv
|
||||
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)
|
||||
|
||||
|
||||
@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
|
||||
def random_hero(self):
|
||||
@ -103,14 +139,161 @@ class User(db.Model):
|
||||
db.session.commit()
|
||||
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):
|
||||
return '<User {}>'.format(self.nickname)
|
||||
return '<User {}>'.format(self.id)
|
||||
|
||||
class TeamspeakData(db.Model):
|
||||
id = db.Column(db.Integer, primary_key=True)
|
||||
time = db.Column(db.Float())
|
||||
time = db.Column(db.DateTime())
|
||||
clients = db.Column(Json())
|
||||
|
||||
def __init__(self, clientlist):
|
||||
self.time = time()
|
||||
self.time = datetime.utcnow()
|
||||
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)
|
||||
|
@ -50,9 +50,6 @@ footer {
|
||||
padding-bottom:80px;
|
||||
}
|
||||
|
||||
#streams-online > .uk-article > a > h4 {
|
||||
margin:0px;
|
||||
}
|
||||
#tsviewer > .uk-modal-dialog {
|
||||
max-height: 80%;
|
||||
overflow-y: scroll;
|
||||
@ -63,16 +60,21 @@ footer {
|
||||
}
|
||||
.dn-streamer, .dn-streamer-offline {
|
||||
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 {
|
||||
height:50px;
|
||||
}
|
||||
|
||||
.dn-streamer-hover {
|
||||
background:red;
|
||||
background:#CACACA;
|
||||
}
|
||||
|
||||
.dn-hero-icon {
|
||||
@ -80,3 +82,19 @@ footer {
|
||||
padding:4px;
|
||||
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
26
teamspeak.py
@ -1,19 +1,35 @@
|
||||
from flask import url_for
|
||||
|
||||
import operator
|
||||
import os
|
||||
import ts3
|
||||
import time
|
||||
import requests
|
||||
from datetime import datetime, timedelta
|
||||
from xml.etree import ElementTree
|
||||
from flask import url_for
|
||||
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
|
||||
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():
|
||||
try:
|
||||
server = ts3.TS3Server(app.config['TS3_HOST'], app.config['TS3_PORT'])
|
||||
|
@ -16,7 +16,7 @@
|
||||
</div>
|
||||
<div class="uk-width-large-1-2 uk-width-medium-1-1 uk-text-center uk-panel botpad">
|
||||
<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>
|
||||
<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>
|
||||
@ -26,10 +26,7 @@
|
||||
</div>
|
||||
<div class="uk-width-large-1-2 uk-width-medium-1-1 uk-text-center uk-panel">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
@ -38,34 +35,49 @@
|
||||
{% block pagescripts %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
{% cache 60*5 %}
|
||||
// Add the 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) {
|
||||
$.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>
|
||||
{% endblock %}
|
||||
|
@ -5,32 +5,16 @@
|
||||
{% block title %}Dota Noobs{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{#{% cache 60*5 %}#}
|
||||
{% cache 60*5 %}
|
||||
{% set teamspeak_data = get_teamspeak_window() %}
|
||||
{% set latest_news = get_latest_news() %}
|
||||
<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 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>
|
||||
<h1 class="uk-panel-title">Teamspeak</h1>
|
||||
<div class="uk-grid uk-margin-bottom">
|
||||
@ -57,11 +41,15 @@
|
||||
</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 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 %}
|
||||
<article class="uk-article dn-news-article">
|
||||
<h4 class="uk-article-title" title="{{ news['title'] }}"><a href="{{ news['url'] }}">{{ news['title'] }}</a></h4>
|
||||
@ -70,15 +58,22 @@
|
||||
</article>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{#{% endcache %}#}
|
||||
{% endcache %}
|
||||
{% endblock %}
|
||||
|
||||
{% block pagescripts %}
|
||||
<script>
|
||||
{% cache 60*5 %}
|
||||
$(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
|
||||
var stream_url = "https://api.twitch.tv/kraken/streams/";
|
||||
var channels = ["dotanoobs", "bearhugdota", "kreejaffakree", "prettypenguins", "shaneomad"];
|
||||
@ -113,7 +108,7 @@ $(document).ready(function() {
|
||||
{% endcache %}
|
||||
|
||||
|
||||
$(".dn-streamer, .dn-streamer-offline").on({
|
||||
$("#streams").on({
|
||||
mouseover: function() {
|
||||
$(this).addClass('dn-streamer-hover');
|
||||
},
|
||||
@ -121,12 +116,6 @@ $(document).ready(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() );
|
||||
});
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
@ -10,13 +10,14 @@
|
||||
<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="//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') }}" />
|
||||
|
||||
<!-- Javascript includes -->
|
||||
<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>
|
||||
<body>
|
||||
<!-- 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_random_hero', userid=g.user.id) }}">A-Z Challenge</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><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>
|
||||
</ul>
|
||||
</div>
|
||||
</ul>
|
||||
{% 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 %}
|
||||
<div class="uk-navbar-content uk-navbar-center uk-hidden-small uk-text-bold">DotaNoobs</div>
|
||||
</nav>
|
||||
|
||||
<!-- Flash Error Messages -->
|
||||
{% with messages = get_flashed_messages() %}
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% 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 %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
@ -60,7 +63,9 @@
|
||||
<!-- Start Main Container -->
|
||||
<div id="container" class="uk-grid">
|
||||
<!-- 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>
|
||||
{% endif %}
|
||||
<!-- Side navigation -->
|
||||
<div class="uk-visible-large uk-width-1-5 uk-panel">
|
||||
{% if g.doob%}
|
||||
@ -78,9 +83,10 @@
|
||||
{% endif %}
|
||||
<ul class="uk-nav uk-nav-offcanvas" data-uk-nav>
|
||||
{% include 'sidenav.html' %}
|
||||
</ul>
|
||||
</div>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main content area -->
|
||||
<div class="uk-width-large-4-5 uk-width-medium-5-5">
|
||||
{% block content %}
|
||||
@ -91,6 +97,7 @@
|
||||
|
||||
<!-- Footer section -->
|
||||
<footer class="uk-clearfix uk-align-center">
|
||||
{% cache 60*5 %}
|
||||
<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> &
|
||||
<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="emailto:admin@dotanoobs.com"><img src="{{ url_for('static', filename='img/email.png') }}" /></a>
|
||||
</div>
|
||||
{% endcache %}
|
||||
</footer>
|
||||
<!-- Page-specific javascript here -->
|
||||
{% block pagescripts %}
|
||||
{% 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>
|
||||
</html>
|
||||
|
||||
|
@ -7,22 +7,31 @@
|
||||
<div class="uk-width-2-3">
|
||||
<h2 class="uk-float-left"><img class="" src="{{ user.avatar }}" /> {{ 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 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>
|
||||
<!--Main content area -->
|
||||
<div class="uk-width-large-2-3 uk-width-medium-1-1 uk-panel">
|
||||
{% if user.public %}
|
||||
{% 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 %}
|
||||
{% else %}
|
||||
<em class="uk-text-danger">This user profile is set to private</em>
|
||||
{% endif %}
|
||||
{% if user.id == g.user.id %} <a href="{{ url_for('user_settings')}}"><i class="uk-icon-edit"></i></a>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<!-- Side bar -->
|
||||
<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">
|
||||
<span class="uk-text-bold">Current Hero</span><br/>
|
||||
<span>{{ user.random_hero['localized_name'] }}</span><br/>
|
||||
@ -31,11 +40,13 @@
|
||||
<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 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>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
@ -5,21 +5,20 @@
|
||||
{% 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 }}" /> {{ user.nickname }}</h2>
|
||||
<h2 class="uk-float-left"><img class="" src="{{ g.user.avatar }}" /> {{ g.user.nickname }}'s Settings</h2>
|
||||
</div>
|
||||
<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>
|
||||
<!--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.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>
|
||||
<li><label class="uk-form-label"> {{ form.logo }} Hide Big Logo</label></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="uk-form-row">
|
||||
|
@ -17,15 +17,16 @@
|
||||
</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>
|
||||
<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-header">Controls</li>
|
||||
{% 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 %}
|
||||
<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 %}
|
||||
|
@ -13,9 +13,9 @@
|
||||
<h3 class="uk-text-bold uk-text-center">Recent Activity</h3>
|
||||
<div class="uk-panel">
|
||||
<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 class="uk-panel" id="teamspeak_active_users"></div>
|
||||
<div class="uk-panel" id="teamspeak_map"></div>
|
||||
@ -38,13 +38,14 @@
|
||||
{% block pagescripts %}
|
||||
{% cache 60*5 %}
|
||||
{% 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>
|
||||
$(document).ready(function() {
|
||||
$('#unique_clients').html("{{ ts3_active_clients(teamspeak_data) }}");
|
||||
$('#current_clients').html("{{ ts3_current_clients() }}");
|
||||
$('#country_clients').html("{{ ts3_countries_active(teamspeak_data) }}");
|
||||
Highcharts.setOptions({
|
||||
global: {
|
||||
@ -90,7 +91,7 @@ $(document).ready(function() {
|
||||
type: 'areaspline',
|
||||
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 %}
|
||||
],
|
||||
}],
|
||||
@ -155,7 +156,7 @@ $(document).ready(function() {
|
||||
map_options.series[0].data.push({
|
||||
y: num,
|
||||
name: country_names[key],
|
||||
path: Highcharts.pathToArray(shapes[key]),
|
||||
path: shapes[key],
|
||||
states: {
|
||||
hover: {
|
||||
color: '#FF7F00'
|
||||
|
27
utils.py
27
utils.py
@ -7,6 +7,7 @@ from os import path, makedirs
|
||||
|
||||
from calendar import timegm
|
||||
from app import app, cache
|
||||
from board import latest_news
|
||||
from teamspeak import create_teamspeak_viewer, getTeamspeakWindow, ISO3166_MAPPING
|
||||
|
||||
def get_steam_userinfo(steam_id):
|
||||
@ -55,6 +56,22 @@ def shorten_filter(s, num_words=40):
|
||||
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
|
||||
def utility_processor():
|
||||
''' For Teamspeak '''
|
||||
@ -123,12 +140,10 @@ def utility_processor():
|
||||
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):
|
||||
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, \
|
||||
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, \
|
||||
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)
|
||||
|
132
views.py
132
views.py
@ -1,11 +1,14 @@
|
||||
from flask import render_template, flash, redirect, g, request, url_for, session
|
||||
|
||||
from datetime import datetime
|
||||
from functools import wraps
|
||||
|
||||
from app import app, db, oid, cache
|
||||
from models import User
|
||||
from models import User, Event
|
||||
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
|
||||
def before_request():
|
||||
@ -16,9 +19,29 @@ def before_request():
|
||||
g.user.last_seen = datetime.utcnow()
|
||||
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('/')
|
||||
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')
|
||||
@oid.loginhandler
|
||||
@ -36,32 +59,20 @@ 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), 'success')
|
||||
return redirect(oid.get_next_url())
|
||||
|
||||
@app.route('/logout')
|
||||
def logout():
|
||||
session.pop('user_id', None)
|
||||
return redirect(oid.get_next_url())
|
||||
g.user = None
|
||||
flash("You have been logged out.", 'success')
|
||||
return redirect(url_for('index'))
|
||||
|
||||
|
||||
### 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!"
|
||||
### ###
|
||||
|
||||
|
||||
# Teamspeak statistics page
|
||||
@app.route('/teamspeak')
|
||||
def teamspeak():
|
||||
return render_template('teamspeak.html')
|
||||
return render_template('teamspeak.html')
|
||||
|
||||
# Friends of doobs page
|
||||
@app.route('/friends')
|
||||
@ -87,15 +98,80 @@ def user_random_hero(userid):
|
||||
|
||||
# User settings page
|
||||
@app.route('/settings', methods=['POST', 'GET'])
|
||||
@login_required
|
||||
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)
|
||||
if user.bio_text == '':
|
||||
user.bio_text = None
|
||||
db.session.commit()
|
||||
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:
|
||||
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…
x
Reference in New Issue
Block a user