Initial (reset)

This commit is contained in:
Yannik Schmidt
2021-07-30 00:42:41 +02:00
commit 90ccfcdf51
51 changed files with 2418 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
*.swp
*.txt
*.ncsv
*.out
*.log
*.zip
pass.secret
css/
js/
data/
build/
fontawesome/
static/pictures/
config/champions/
__pychache__/
*.pyc
config/

24
README.md Normal file
View File

@@ -0,0 +1,24 @@
# Setup
## Python
pip install -r req.txt
## CSS/JS/Assets
Download the following into the `static/` directory.
- [bootstrap>=4.3](https://getbootstrap.com/docs/4.3/getting-started/download/)
- [fontawesome-free](https://fontawesome.com) (unpacked directory must be called `fontawesome`)
## Directories
mkdir -p data/users/
## User-Schema
{
name : "name of user",
accounts : [ "list", "of", "account", "names" ],
single : "true/false, depending if user has team",
selectedChampions: ["list of champions", "this user can play"]
allowedFeatures: ["list of allowed features", "or empty list to allow all"]
}

32
composition_builder.py Normal file
View File

@@ -0,0 +1,32 @@
class Composition:
def __init__(self, name):
self.goodAgainst = []
self.badAgainst = []
self.name = name
def isGoodAgainst(self, compositions):
self.goodAgainst = compositions
def isBadAgainst(self, compositions):
self.badAgainst = Composition
def __eq__(self, other):
return self.name == other.name
ATTACK = Composition("ATTACK")
PROTECT = Composition("PROTECT")
CATCH = Composition("CATCH")
SIEGE = Composition("SIEGE")
SPLIT = Composition("SPLIT")
ATTACK.isGoodAgainst([SPLIT, SIEGE])
PROTECT.isGoodAgainst([CATCH, ATTACK])
CATCH.isGoodAgainst([ATTACK, SPLIT])
SIEGE.isGoodAgainst([PROTECT, CATCH])
SPLIT.isGoodAgainst([SIEGE, PROTECT])
ATTACK.isBadAgainst([CATCH, PROTECT])
PROTECT.isBadAgainst([SPLIT, SIEGE])
CATCH.isBadAgainst([SIEGE, PROTECT])
SIEGE.isBadAgainst([ATTACK, SPLIT])
SPLIT.isBadAgainst([CATCH, ATTACK])

55
database.py Normal file
View File

@@ -0,0 +1,55 @@
import json
import os
import usermanagement
import flask_login as fl
userDb = {
"sheppy" : { "password" : "" }
}
DEAULT_DIR = "data/"
def saveTable(tableId, jsonData):
with open(DEAULT_DIR + tableId + ".json" , "w") as f:
print(jsonData)
f.write(json.dumps(jsonData))
def loadTable(tableId):
with open(DEAULT_DIR + tableId + ".json") as f:
return json.loads(f.read())
def teamChampSelectAdd(teamid, champ, role):
path = "config/teams/{}/roles/{}.json".format(teamid, role)
if not os.path.isfile(path):
with open(path, "w") as f:
f.write('{ "champions": [%s]] }' % champ)
else:
data = None
with open(path, "r") as f:
data = json.loads(f.read())
if champ not in data["champions"]:
data["champions"] += [champ]
with open(path, "w") as f:
f.write(json.dumps(data))
def teamChampSelectRemove(teamid, champ, role):
path = "config/teams/{}/roles/{}.json".format(teamid, role)
if not os.path.isfile(path):
raise ValueError("No information about this role exists, so nothing can be removed.")
else:
data = None
with open(path, "r") as f:
data = json.loads(f.read())
data["champions"].remove(champ)
with open(path, "w") as f:
f.write(json.dumps(data))
def getUserByFlaskLoginId(flId):
if not flId.is_active:
return None
return usermanagement.User(flId)
def safeCheckLogin(username, password):
return usermanagement.User(username)
def getUserByName(name):
return usermanagement.User(name)

74
entities/CellContainer.py Normal file
View File

@@ -0,0 +1,74 @@
import flask
class CellContainer:
def __init__(self, tableConfig):
self.columns = tableConfig.get("columns")
self.rows = tableConfig["rows"]
self.headerRow = tableConfig["header-row"]
if not self.columns:
self.columns = len(self.headerRow)
self.headerColumn = tableConfig["header-column"]
self.currentCellId = 0
self.contents = tableConfig.get("contents")
self.colors = tableConfig.get("colors")
self.help = tableConfig.get("help")
self.hasHeaderColumn = bool(tableConfig.get("hasHeaderColumn"))
self.hasHeaderRow = bool(tableConfig.get("hasHeaderRow"))
def setContents(self, contents):
self.contents = contents
def getView(self):
innerHTML = ""
startAtRow = 0
if self.headerRow or self.hasHeaderRow:
innerHTML += flask.Markup(flask.render_template("entities/row.html", cells=self.getHeaderRow()))
startAtRow = 1
for rowNr in range(startAtRow, self.rows):
innerHTML += flask.Markup(flask.render_template("entities/row.html", cells=self.getRowHTML(rowNr)))
return flask.Markup(flask.render_template("entities/table.html", tableContent=innerHTML))
def getHeaderRow(self):
rowHTML = ""
array = None
if self.contents:
array = self.contents[:self.columns]
else:
array = self.headerRow
for rowStr in array:
rowHTML += flask.Markup(flask.render_template("entities/cell.html",
cellId=self.currentCellId, classes="cell header", cellContent=rowStr))
self.currentCellId += 1
return rowHTML
def getCellWidth(self):
return "{:.2f}%".format(100/self.columns)
def getRowHTML(self, curRow):
'''Get HTML for individual rows'''
rowHTML = ""
for col in range(0, self.columns):
# handle header fields in rows #
classes = ["cell"]
cellContent = ""
cellColor = ""
if self.headerColumn and col == 0:
classes += ["header"]
cellContent = self.headerColumn[curRow]
elif self.hasHeaderColumn and col == 0:
classes += ["header"]
if self.contents:
cellContent = self.contents[curRow*self.columns+col]
if self.colors:
cellColor = self.colors[curRow*self.columns+col]
# add single cell #
rowHTML += flask.Markup(flask.render_template("entities/cell.html", cellId=self.currentCellId, cellContent=cellContent, classes=" ".join(classes), enableEdit=True, cellColor=cellColor))
self.currentCellId += 1
# return HTML for row #
return rowHTML

72
jsonConfig.py Normal file
View File

@@ -0,0 +1,72 @@
import re
import os
import json
import types
def readJsonFile(filename):
'''Return a single json file as a dictionary object'''
with open(filename) as f:
return json.load(f)
def readJsonDir(basedir):
'''Return an array of dictionary objects in a directory'''
# important safety check #
udata = basedir.encode("utf-8")
asciidata = udata.decode("ascii", "ignore")
if re.match(r'^[\w-]+$', asciidata) or basedir != asciidata:
raise RuntimeError("Unsafe path")
# load json files from projects/ dir #
jsonDictList = []
for root, dirs, files in os.walk(basedir):
root = dirs
dirs = root
for filename in files:
if filename.endswith(".json"):
jsonDictList += [readJsonFile(os.path.join(basedir, filename))]
return jsonDictList
def priceSection(section):
'''Get a prices sections by it's identifier'''
prices = readJsonDir(os.path.join("config/prices/", section))
allFeatures = []
for p in prices:
allFeatures += p["features"]
for p in prices:
p["disabledFeatures"] = set(allFeatures) - set(p["features"])
return prices
def pricesSections():
return [ priceSection("section-1"), priceSection("section-2")]
def mainConfig():
'''Get main configuration'''
ret = types.SimpleNamespace()
ret.__dict__.update(readJsonFile("config/config.json"))
return ret
def services():
'''Get Services configuration'''
return readJsonDir("config/services/")
def getOfferById(strId):
'''Get an offer in prices sections by it's id'''
d1 = readJsonDir("config/prices/section-1")
d2 = readJsonDir("config/prices/section-1")
for el in d1 + d2:
if el.get(strId):
return el
return None
def champs():
'''Read champ directory and return a list of champions'''
return [ x["name"] for x in readJsonDir("config/champions/")]

20
pages_api.py Normal file
View File

@@ -0,0 +1,20 @@
import flask
import smtplib
import jsonConfig as jc
pagesApi = flask.Blueprint('simple_page', __name__, template_folder='templates')
@pagesApi.route("/contact-api", methods=['POST'])
def contactAPI():
email = flask.request.form["email"]
name = flask.request.form["name"]
subject = "Subject: {} ({})\n\n".format(flask.request.form["subject"], name)
message = subject + flask.request.form["message"]
smtpTarget = smtplib.SMTP(jc.mainConfig().smtp)
smtpTarget.sendmail(email, jc.mainConfig().target_email , message)
smtpTarget.quit()
return flask.redirect("/thanks")

29
pages_loginmanagement.py Normal file
View File

@@ -0,0 +1,29 @@
import flask
import flask_login as fl
import database as db
import jsonConfig as jc
pagesLoginManagement = flask.Blueprint('pagesLoginManagement', __name__, template_folder='templates')
@pagesLoginManagement.route("/login", methods=['GET', 'POST'])
def login():
if flask.request.method == 'POST':
username = flask.request.form['username']
password = flask.request.form['password']
print(username, password)
acceptedUser = db.safeCheckLogin(username, password)
if acceptedUser:
fl.login_user(acceptedUser)
return flask.redirect(flask.url_for("standardPages.dashboard"))
else:
return flask.abort(401)
else:
return flask.render_template("standard/login.html",
config=jc.mainConfig())
@pagesLoginManagement.route("/logout")
@fl.login_required
def logout():
fl.logout_user()
return flask.redirect("/")

73
pages_standard.py Normal file
View File

@@ -0,0 +1,73 @@
import database as db
import jsonConfig as jc
import flask
import flask_login as fl
import os
standardPages = flask.Blueprint('standardPages', __name__, template_folder='templates')
@standardPages.route('/')
def index():
'''Landing page/index page/root page'''
user = db.getUserByFlaskLoginId(fl.current_user)
return flask.render_template("standard/index.html",
services=jc.services(),
pricesSections=jc.pricesSections(),
config=jc.mainConfig(),
currentUser=user)
@standardPages.route("/dashboard")
@fl.login_required
def dashboard():
'''Logged in user dashboard'''
user = db.getUserByFlaskLoginId(fl.current_user)
return flask.render_template("standard/user_dashboard.html",
config=jc.mainConfig(),
currentUser=user)
@standardPages.route("/shop")
def shop():
'''Shop to buy shit'''
user = db.getUserByFlaskLoginId(fl.current_user)
return flask.render_template("standard/shop.html",
config=jc.mainConfig(),
currentUser=user)
@standardPages.route("/about")
def about():
'''About Page'''
user = db.getUserByFlaskLoginId(fl.current_user)
return flask.render_template("standard/about.html",
config=jc.mainConfig(),
currentUser=user)
@standardPages.route("/impressum")
def impressum():
'''About Page'''
user = db.getUserByFlaskLoginId(fl.current_user)
return flask.render_template("standard/impressum.html",
config=jc.mainConfig(),
currentUser=user)
@standardPages.route("/contact")
def contact():
'''Contact Page'''
offer = jc.getOfferById(flask.request.args.get("offerId"))
user = db.getUserByFlaskLoginId(fl.current_user)
return flask.render_template("standard/contact.html",
conf=jc.mainConfig(),
currentUser=user, selectedOffer=offer)
@standardPages.route("/thanks")
def thanks():
'''Post Contact thanks page'''
user = db.getUserByFlaskLoginId(fl.current_user)
return flask.render_template("standard/thanks.html", config=jc.mainConfig(),
currentUser=user)

46
server.py Executable file
View File

@@ -0,0 +1,46 @@
#!/usr/bin/python3
import flask
import flask_login as fl
import argparse
from pages_standard import standardPages
from pages_api import pagesApi
from subpages_dashboard import subpagesDashboard
from pages_loginmanagement import pagesLoginManagement
import database as db
app = flask.Flask("OH-MY-Nemesis")
app.secret_key = 'super secret key'
app.config['SESSION_TYPE'] = 'filesystem'
loginManager = fl.LoginManager()
@loginManager.user_loader
def load_user(userName):
'''Setup the user/login manager'''
return db.getUserByName(userName)
#@app.route('/static/<path:path>')
#def static(path):
# '''Configure sending of static files'''
# return flask.send_from_directory('static', path)
@app.before_first_request
def init():
'''Do nessesary initialization jobs'''
loginManager.init_app(app)
app.register_blueprint(standardPages)
app.register_blueprint(pagesApi)
app.register_blueprint(subpagesDashboard)
app.register_blueprint(pagesLoginManagement)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='None',
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument('--interface', default="localhost",
help='Interface on which flask (this server) will take requests on')
parser.add_argument('--port', default="5000",
help='Port on which flask (this server) will take requests on')
args = parser.parse_args()
app.run(host=args.interface, port=args.port)

48
static/contact.js Normal file
View File

@@ -0,0 +1,48 @@
function submitContactForm(){
/* check input fields */
mailField = document.getElementById("email")
messageField = document.getElementById("message")
if(mailField.value == ""){
alert("Bitte geben Sie einen Kontakt an unter dem wir Sie erreichen können!")
return
}
if(messageField.value == ""){
alert("Nachricht ist leer!")
return
}
/* show the waiting dialog */
dialog = document.getElementById("waiting-dialog")
dialog.style.disply = "block"
setMainBackgroundOpacity(0.5)
/* submit the form */
xhr = new XMLHttpRequest();
xhr.open("POST", "/contact-api");
xhr.onload = formSubmitFinished
formData = new FormData(document.getElementById("contact-form"));
xhr.send(formData);
}
function formSubmitFinished(event){
if(event.target.status < 200 || event.target.status >= 300){
showErrorMessage(event.target); // blocking
setMainBackgroundOpacity(1)
}else{
window.location.href = "/thanks"
}
}
function setMainBackgroundOpacity(opacity){
mainContainer = document.getElementById("main-container")
mainContainer.style.opacity = opacity
}
function showErrorMessage(target){
console.log(target)
alert("Error: " + target.statusText)
}

BIN
static/defaultFavicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

64
static/login.css Normal file
View File

@@ -0,0 +1,64 @@
@import url('https://fonts.googleapis.com/css?family=Numans');
html,body{
/*background-image: url('http://getwallpapers.com/wallpaper/full/a/5/d/544750.jpg');*/
background-color: green !important;
background-size: cover;
background-repeat: no-repeat;
height: 100%;
font-family: 'Numans', sans-serif;
}
.container{
height: 100%;
align-content: center;
}
.card{
height: 370px;
margin-top: auto;
margin-bottom: auto;
width: 400px;
background-color: rgba(0,0,0,0.5) !important;
}
.card-header h3{
color: white;
}
.social_icon{
position: absolute;
right: 20px;
top: -45px;
}
.input-group-prepend span{
width: 50px;
background-color: #FFC312;
color: black;
border:0 !important;
}
input:focus{
outline: 0 0 0 0 !important;
box-shadow: 0 0 0 0 !important;
}
.login_btn{
color: black;
background-color: rgb(255, 195, 18) !important;
width: 100px;
}
.login_btn:hover{
color: black;
background-color: white;
}
.links{
color: white;
}
.links a{
margin-left: 4px;
}

105
static/site.css Normal file
View File

@@ -0,0 +1,105 @@
.eyecatcher {
height: 100vh;
min-height: 500px;
/* background-image: url('/static/pictures/test.png'); */
background: cyan;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
/* pricing */
section.pricing {
background: #007bff;
background: linear-gradient(to right, #0062E6, #33AEFF);
}
.pricing .card {
border: none;
border-radius: 1rem;
transition: all 0.2s;
box-shadow: 0 0.5rem 1rem 0 rgba(0, 0, 0, 0.1);
}
.pricing hr {
margin: 1.5rem 0;
}
.pricing .card-title {
margin: 0.5rem 0;
font-size: 0.9rem;
letter-spacing: .1rem;
font-weight: bold;
}
.pricing .card-price {
font-size: 3rem;
margin: 0;
}
.pricing .card-price .period {
font-size: 0.8rem;
}
.pricing ul li {
margin-bottom: 1rem;
}
.pricing .text-muted {
opacity: 0.7;
}
.pricing .btn {
font-size: 80%;
border-radius: 5rem;
letter-spacing: .1rem;
font-weight: bold;
padding: 1rem;
opacity: 0.7;
transition: all 0.2s;
}
.bg-special{
background-color: #eae9e9;
}
.hover-to-75:hover *{
opacity: 0.75;
display: unset;
}
.footer-el{
float: left;
color: rgba(255,255,255,.5);
}
/* Hover Effects on Card */
@media (min-width: 992px) {
.pricing .card:hover {
margin-top: -.25rem;
margin-bottom: .25rem;
box-shadow: 0 0.5rem 1rem 0 rgba(0, 0, 0, 0.3);
}
.pricing .card:hover .btn {
opacity: 1;
}
}
.special-header{
font-weight: 900 !important;
color: rgba(255,255,255,.9);
font-size: 6vw;
}
.special-sub-header{
font-weight: bold;
color: rgba(255,255,255,.5);
font-size: 4rem;
display: none;
}
.pic-disabled{
opacity: 0.6;
background-color: black;
}

47
static/table.css Normal file
View File

@@ -0,0 +1,47 @@
body{
background: #161618;
}
table{
width: 100%;
border-collapse: collapse;
}
td{
border-style: groove;
border-width: 2px;
border-color: antiquewhite;
background-color: #f5f7f6;
}
.header{
background: lightcyan !important;
font-weight: bold;
}
.hidden{
display: none;
}
.information{
margin-top: 8px;
font-style: italic;
color: #f5f7f6;
}
button{
height: 40px;
width: 30%;
margin: 20px;
border: transparent;
border-radius: 20px;
background: aqua;
font-weight: bold;
transition-duration: 0.3s;
}
button:hover {
background-color: #4CAF50;
color: white;
}
.color-cell{
transition-duration: 0.3s;
}
.color-cell:hover{
opacity: 0.75;
}

152
static/table.js Normal file
View File

@@ -0,0 +1,152 @@
var currentCellSelected = null
var colors = ["rgb(245, 247, 246)", "indianred", "coral", "orange", "yellow", "cyan", "lightgreen", "rgb(75, 140, 72)", "rgb(35, 110, 31)"]
var words = ["", "Awefull", "Worse", "Bad", "Normal", "Praticed", "Decent", "Good", "Perfect"]
function cellOnClickListener(element){
updateSelectedCell(element)
}
function colorCellOnClickListener(element){
if(currentCellSelected){
currentCellSelected.style.background = element.style.background
}
}
function updateSelectedCell(cell){
if(currentCellSelected && currentCellSelected.id != cell.id){
currentCellSelected.style.borderColor = ""
currentCellSelected.blur()
}
currentCellSelected = cell
if(cell){
cell.style.borderColor = "blue"
}
}
function updateBackgroundForCell(cell, indexMod){
if(!cell.style.background){
cell.style.background = colors[0];
}
/* firefox hack */
var bcolor = cell.style.background
if(bcolor.indexOf(") ")>0){
bcolor = bcolor.split(") ")[0] + ")"
}else if(bcolor.indexOf(" ") && bcolor.indexOf("rgb") == -1){
bcolor = bcolor.split(" ")[0]
}
console.log(bcolor)
newColor = colors.indexOf(bcolor) + indexMod + colors.length
cell.style.background = colors[newColor % colors.length]
}
function addWindowListeners(){
var colorContent = ""
var i
for(i = 1; i < colors.length; i++){
var colorCell = '<td class="cell color-cell" onclick="colorCellOnClickListener(this)" '
+ 'style="border-style: none; background: '
+ colors[i] + '">' + words[i] + '</td>\n'
colorContent += colorCell
}
document.getElementById("colorExplanation").innerHTML = colorContent
document.body.addEventListener('keyup', (e) => {
if(!currentCellSelected){
updateSelectedCell(document.getElementById("0"))
}
var cols = document.getElementById('table').rows[0].cells.length
var rows = document.getElementById('table').rows.length
var id = parseInt(currentCellSelected.id)
if(e.code === "ArrowUp" && document.activeElement == document.body){
var targetId = Math.max(id-cols, 0)
updateSelectedCell(document.getElementById(targetId))
e.preventDefault()
}else if(e.code === "ArrowDown" && document.activeElement == document.body){
var targetId = Math.min(id+cols, cols*rows)
updateSelectedCell(document.getElementById(targetId))
e.preventDefault()
}else if(e.code === "ArrowLeft" && document.activeElement == document.body){
var targetId = Math.max(id-1, 0)
updateSelectedCell(document.getElementById(targetId))
e.preventDefault()
}else if(e.code === "ArrowRight" && document.activeElement == document.body){
var targetId = Math.min(id+1, cols*rows)
updateSelectedCell(document.getElementById(targetId))
e.preventDefault()
}else if(e.code === "Escape"){
updateSelectedCell(null)
e.preventDefault()
}else if(e.code === "ShiftLeft"){
updateBackgroundForCell(currentCellSelected, +1)
e.preventDefault()
}else if(e.code === "ControlLeft"){
updateBackgroundForCell(currentCellSelected, -1)
e.preventDefault()
}else if(e.code === "Enter"){
if(document.activeElement != document.body){
// document.activeElement.blur()
}else{
if(currentCellSelected){
currentCellSelected.focus()
}
}
e.preventDefault()
}else if(e.code === "Tab"){
if(document.activeElement != document.body){
document.activeElement.blur()
}
if(currentCellSelected){
var targetId = Math.min(id+1, cols*rows)
updateSelectedCell(document.getElementById(targetId))
}
e.preventDefault()
}
});
}
function saveToServer(){
var contents = []
var colors = []
for(let el of document.getElementsByClassName("cell")){
if(!el.id){
/* filter color cells */
continue
}
contents = contents.concat(el.innerText)
colors = colors.concat(el.style.background)
}
var cols = document.getElementById('table').rows[0].cells.length
var rows = document.getElementById('table').rows.length
var hasHeaderColumn = document.getElementById("hasHeaderColumn").innerText
var hasHeaderRow = document.getElementById("hasHeaderRow").innerText
var dict = { contents:contents, colors:colors, rows:rows, cols:cols,
hasHeaderRow:hasHeaderRow, hasHeaderColumn:hasHeaderColumn };
var json = JSON.stringify(dict);
var tableId = document.getElementById("tableId").innerText
var xhttp = new XMLHttpRequest();
xhttp.onload = function() {
if (xhttp.status != 204) {
alert("Transmission failed!?!")
}else{
window.location.href = "/table?id=" + tableId
}
}
xhttp.open("POST", "/save?id=" + tableId, true);
xhttp.setRequestHeader('Content-Type', 'application/json');
xhttp.send(json)
}
window.onload = addWindowListeners

View File

@@ -0,0 +1,49 @@
function saveToServer(){
var xhttp = new XMLHttpRequest();
champ = "Atrox"
position = "Top"
affinity = "5"
req = "/save?type=teamChampSelect" + "&champ=" + cahmp
+ "&position=" + position
+ "&affinity=" + affinity
xhttp.open("POST", req, true);
//xhttp.setRequestHeader('Content-Type', 'application/json');
xhttp.send(json)
}
function addChamp(selectorId, role){
champ = document.getElementById(selectorId).value
url = window.location.href + "?" + "role=" + role +
"&" + "champ=" + champ +
"&" + "action=" + "add"
fetch(url, {
method: 'POST',
mode: 'cors',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
referrerPolicy: 'strict-origin'
}
).then(window.location.reload());
}
function removeChamp(champ, role){
url = window.location.href + "?" + "role=" + role +
"&" + "champ=" + champ +
"&" + "action=" + "remove"
fetch(url, {
method: 'POST',
mode: 'cors',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
referrerPolicy: 'strict-origin'
}
).then(window.location.reload());
}

96
subpages_dashboard.py Normal file
View File

@@ -0,0 +1,96 @@
import jsonConfig as jc
import flask
import flask_login as fl
import database as db
import collections
import os
subpagesDashboard = flask.Blueprint('subpagesDashboard', __name__, template_folder='templates')
@subpagesDashboard.route("/dashboard/self-analysis")
@fl.login_required
def selfMatchHistory():
user = db.getUserByFlaskLoginId(fl.current_user)
return flask.render_template("dashboardSubpages/self_analysis.html",
config=jc.mainConfig(),
currentUser=user,
champs=jc.champs())
@subpagesDashboard.route("/dashboard/self_matchups")
@fl.login_required
def selfMatchups():
user = db.getUserByFlaskLoginId(fl.current_user)
return flask.render_template("dashboardSubpages/self_matchups.html",
config=jc.mainConfig(), currentUser=user, champs=jc.champs())
@subpagesDashboard.route("/dashboard/team-match-history")
@fl.login_required
def teamMatchHistory():
user = db.getUserByFlaskLoginId(fl.current_user)
return flask.render_template("dashboardSubpages/subpage_team_history.html",
config=jc.mainConfig(), currentUser=user, champs=jc.champs())
@subpagesDashboard.route("/team_champselect", methods=['GET', 'POST'])
@fl.login_required
def teamChampSelect():
teamid = "example"
roles = collections.OrderedDict()
roles["Top"] = None
roles["Jungle"] = None
roles["Mid"] = None
roles["Bottom"] = None
roles["Support"] = None
if flask.request.method == "POST":
role = flask.request.args.get("role")
champ = flask.request.args.get("champ")
action = flask.request.args.get("action")
if action == "remove":
db.teamChampSelectRemove(teamid, champ, role)
elif action == "add":
db.teamChampSelectAdd(teamid, champ, role)
else:
flask.abort(500)
return ("", 204)
for key in roles:
path = "config/teams/{}/roles/{}.json".format(teamid, key)
if not os.path.isfile(path):
with open(path, "w") as f:
f.write('{ "champions": [] }')
roles[key] = jc.readJsonFile(path)
allChampions = jc.readJsonDir("config/champions")
return flask.render_template("team_champselect.html", roles=roles,
allChampions=allChampions, config=jc.readJsonFile("config/config.json"), currentUser=fl.current_user)
@subpagesDashboard.route("/team_composition_overview")
@fl.login_required
def teamCompositionOverview():
username = fl.current_user
user = jc.readJsonFile(os.path.join("data/users/", "{}.json".format(username)))
teamComps = jc.readJsonDir(os.path.join("config/teams/{}/compositions/".format("example")))
return flask.render_template("team_composition_overview.html", config=jc.readJsonFile("config/config.json"), currentUser=fl.current_user,
userInDatabase=user, teamComps=teamComps)
@subpagesDashboard.route("/team_composition_single")
@fl.login_required
def teamCompositionSingle():
roles = collections.OrderedDict()
roles["Top"] = None
roles["Jungle"] = None
roles["Mid"] = None
roles["Bottom"] = None
roles["Support"] = None
allChampions = jc.readJsonDir("config/champions")
return flask.render_template("team_composition_single.html", roles=roles,
teamChamps=allChampions,
config=jc.readJsonFile("config/config.json"),
currentUser=fl.current_user)

View File

@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Select the roles you can play for the team">
<title>Champ Selection</title>
<!-- Bootstrap core CSS -->
{% include 'default-includes.html' %}
</head>
<body>
{% include 'navbar.html' %}
<main role="main">
<!-- day | Game Nr | Role | played | against | side | result
| day time | duoque? | kda | notes -->
</main>
</body>

View File

@@ -0,0 +1,36 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Leauge of Legends Coaching">
<title>Nemesis Coaching</title>
<!-- Bootstrap core CSS -->
{% include 'partials/header.html' %}
</head>
<body>
{% include 'partials/navbar.html' %}
<div role="main" class="p-5">
<div class="row mb-3">
<button id="save">Save All</button>
</div>
<div class="row border border-secondary p-2 mb-2">
{% for m_entry in currentUser.selfAnalysisEntries %}
{% include "partials/self-analysis-obj.html" %}
{% endfor %}
{% include "partials/self-analysis-obj.html" %}
</div>
<div class="row mt-3">
<button class="btn-primary">Add Match</div>
</div>
</div>
{% include 'partials/footer.html' %}
</body>
</html>

View File

@@ -0,0 +1,36 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Leauge of Legends Coaching">
<title>Nemesis Coaching</title>
<!-- Bootstrap core CSS -->
{% include 'partials/header.html' %}
</head>
<body>
{% include 'partials/navbar.html' %}
<div role="main" class="p-5">
<div class="row mb-3">
<button id="save">Save All</button>
</div>
<div class="border border-secondary p-2 mb-2">
{% for m_entry in currentUser.matchupEntris %}
{% include "partials/matchup-single-champ.html" %}
{% endfor %}
{% include "partials/matchup-single-champ.html" %}
</div>
<div class="row mt-3">
<button class="btn-primary">Add new Champ you play</div>
</div>
</div>
<div class="mb-5"></div>
{% include 'partials/footer.html' %}
</body>
</html>

View File

@@ -0,0 +1,85 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Leauge of Legends Coaching">
<title>Nemesis Coaching</title>
<!-- Bootstrap core CSS -->
{% include 'default-includes.html' %}
</head>
<body>
{% include 'navbar.html' %}
<main role="main">
<header class="eyecatcher">
<div class="container h-100">
<div class="row h-100 align-items-center">
<div class="col-12 text-center">
<h1 class="font-weight-light">{{ config["main-title"] }}</h1>
<p class="lead">{{ config["subtitle"] }}</p>
</div>
</div>
</div>
</header>
<!-- Services -->
<div class="container pt-3">
<div class="row">
{% for card in services %}
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" src="static/pictures/{{ card["picture"] }}">
<div class="card-body">
<h2>{{ card["headline"] }}</h2>
<p>{{ card["description"] }}</p>
<p><a class="btn btn-secondary" href="{{ card["primary-link"] }}" role="button">Read More &raquo;</a></p>
</p>
</div>
</div>
</div>
{% endfor %}
<hr>
</div>
</div>
<!-- Prices -->
{% for prices in pricesSections %}
<section class="pricing py-5 mb-5"" style="background-color: chartreuse;">
<div class="container">
<div class="row">
{% for card in prices %}
<div class="col-lg-4">
<div class="card mb-5 mb-lg-0">
<div class="card-body">
<h5 class="card-title text-muted text-uppercase text-center">{{ card["header"] }}</h5>
<h6 class="card-price text-center">{{ card["price"] }}&euro;<span class="period">{{ card["period"] }}</span></h6>
<hr>
<ul class="fa-ul">
{% for feature in card["features"] %}
<li><span class="fa-li"><i class="fas fa-check"></i></span>{{ feature }}</li>
{% endfor %}
{% for feature in card["disabledFeatures"] %}
<li class="text-muted"><span class="fa-li"><i class="fas fa-times"></i></span>{{ feature }}</li>
{% endfor %}
</ul>
<a href="/contact?offerId={{ card["config-id"] }}" class="btn btn-block btn-primary text-uppercase">Contact Me</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</section>
{% endfor %}
{% include 'footer.html' %}
</main>
</body>
</html>

View File

@@ -0,0 +1,35 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Leauge of Legends Coaching">
<title>Nemesis Coaching</title>
<!-- Bootstrap core CSS -->
{% include 'partials/header.html' %}
</head>
<body>
{% include 'partials/navbar.html' %}
<div role="main" class="p-5">
<div class="row mb-3">
<button id="save">Save All</button>
</div>
{% for m_entry in currentUser.matchhistoryEntries %}
{% include "partials/matchhistory-analysis-team-obj.html" %}
{% endfor %}
{% include "partials/matchhistory-analysis-team-obj.html" %}
<div class="row mt-3">
<button class="btn-primary">Add Match</div>
</div>
<div class="pb-3"></div>
</div>
{% include 'partials/footer.html' %}
</body>
</html>

View File

@@ -0,0 +1,85 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Leauge of Legends Coaching">
<title>Nemesis Coaching</title>
<!-- Bootstrap core CSS -->
{% include 'default-includes.html' %}
</head>
<body>
{% include 'navbar.html' %}
<main role="main">
<header class="eyecatcher">
<div class="container h-100">
<div class="row h-100 align-items-center">
<div class="col-12 text-center">
<h1 class="font-weight-light">{{ config["main-title"] }}</h1>
<p class="lead">{{ config["subtitle"] }}</p>
</div>
</div>
</div>
</header>
<!-- Services -->
<div class="container pt-3">
<div class="row">
{% for card in services %}
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" src="static/pictures/{{ card["picture"] }}">
<div class="card-body">
<h2>{{ card["headline"] }}</h2>
<p>{{ card["description"] }}</p>
<p><a class="btn btn-secondary" href="{{ card["primary-link"] }}" role="button">Read More &raquo;</a></p>
</p>
</div>
</div>
</div>
{% endfor %}
<hr>
</div>
</div>
<!-- Prices -->
{% for prices in pricesSections %}
<section class="pricing py-5 mb-5"" style="background-color: chartreuse;">
<div class="container">
<div class="row">
{% for card in prices %}
<div class="col-lg-4">
<div class="card mb-5 mb-lg-0">
<div class="card-body">
<h5 class="card-title text-muted text-uppercase text-center">{{ card["header"] }}</h5>
<h6 class="card-price text-center">{{ card["price"] }}&euro;<span class="period">{{ card["period"] }}</span></h6>
<hr>
<ul class="fa-ul">
{% for feature in card["features"] %}
<li><span class="fa-li"><i class="fas fa-check"></i></span>{{ feature }}</li>
{% endfor %}
{% for feature in card["disabledFeatures"] %}
<li class="text-muted"><span class="fa-li"><i class="fas fa-times"></i></span>{{ feature }}</li>
{% endfor %}
</ul>
<a href="/contact?offerId={{ card["config-id"] }}" class="btn btn-block btn-primary text-uppercase">Contact Me</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</section>
{% endfor %}
{% include 'footer.html' %}
</main>
</body>
</html>

View File

@@ -0,0 +1,2 @@
<td onclick="cellOnClickListener(this)" id="{{ cellId }}" {% if enableEdit %} contenteditable='true' {% endif %} class="{{ classes }}" {% if cellColor %}style="background: {{ cellColor }};"{% endif %}>{{ cellContent }}</td>

View File

@@ -0,0 +1,4 @@
<tr>
{{ cells }}
</tr>

View File

@@ -0,0 +1,3 @@
<table id="table">
{{ tableContent }}
</table>

View File

@@ -0,0 +1,42 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Basic Table</title>
<link rel="shortcut icon" href="/static/defaultFavicon.ico">
<link rel=stylesheet href="/static/table.css">
<script defer src="/static/table.js"></script>
<style>
{% if cellWidth %}
.cell{
width: {{ cellWidth }};
}
{% endif %}
</style>
<!-- Bootstrap core CSS -->
{% include 'default-includes.html' %}
</head>
<body class="pt-5 mt-5">
{% include 'navbar.html' %}
<div class="hidden" id="tableId">{{ tableId }}</div>
<div class="hidden" id="hasHeaderColumn">{{ hasHeaderColumn }}</div>
<div class="hidden" id="hasHeaderRow">{{ hasHeaderRow }}</div>
{% if helpRow %}
{{ helpRow }} {% if champion %} {{ champion}} {% endif %}
{% else %}
<table>
<tr id="colorExplanation">
</tr>
</table>
{% endif %}
<div class="information">
Left CTRL or SHIFT to change color of selected cell (or press the desired color with the cell selected)
</div>
<br>
{{ body }}
<div style="text-align: center">
<button onclick=saveToServer()>Save</button>
</div>
</body>
</html>

View File

@@ -0,0 +1,6 @@
<select class="browser-default custom-select">
<option selected>...</option>
{% for c in champs %}
<option value="{{ c }}">{{ c }}</option>
{% endfor %}
</select>

View File

@@ -0,0 +1,3 @@
<div class="footer-copyright text-center py-3 fixed-bottom bg-dark">
<a class="col-sm footer-el" href="/impressum">Impressum/Datenschutz</a>
</div>

View File

@@ -0,0 +1,10 @@
<link rel="shortcut icon" href="/static/defaultFavicon.ico">
<meta name="author" content="Yannik Schmidt">
<link href="/static/css/bootstrap.min.css" rel="stylesheet">
<link href="/static/site.css" rel="stylesheet">
<link href="/static/fontawesome/css/all.css" rel="stylesheet">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="/static/js/bootstrap.js"></script>

View File

@@ -0,0 +1,136 @@
<div class="p-3 row border border-secondary">
{% set positions = [ "Top", "Jgl", "Mid", "Bot", "Sup" ] %}
{% set gameTypes = [ "Leauge", "Tournament", "Scrim", "Normal", "Flex" ] %}
<div class="col">
<div class="row">
<div class="col">
<h2>Game Type</h2>
</div>
<div class="col">
<select class="browser-default custom-select">
<option selected>...</option>
{% for gt in gameTypes %}
<option value="{{ gt }}">{{ gt }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="row">
<div class="col">
<div class="row">
<div class="col">
<h3>Our Team</h3>
</div>
</div>
<div class="row">
{% for p in positions %}
<div id="{{ p }}" class="col">
<h4>{{ p }}</h4>
{% include "partials/champ-selector-dropdown.html" %}
</div>
{% endfor %}
</div>
</div>
</div>
<div class="row pt-5 pb-5">
<div class="col">
<div class="row">
<div class="col">
<h3>Enemy Team</h3>
</div>
</div>
<div class="row">
{% for p in positions %}
<div id="{{ p }}" class="col">
<h4>{{ p }}</h4>
<select class="browser-default custom-select">
<option selected>...</option>
{% for champs in champs %}
<option value="{{ r }}">{{ r }}</option>
{% endfor %}
</select>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="row pt-5 pb-5">
<div class="col">
<div class="row">
<div class="col">
<h3>My Side</h3>
</div>
</div>
<div class="row">
<div class="col">
<select class="browser-default custom-select">
<option selected>...</option>
<option value="blue">Blue</option>
<option value="red">Red</option>
</select>
</div>
</div>
</div>
<div class="col">
<div class="row">
<h3>Result</h3>
</div>
<div class="row">
<select class="browser-default custom-select">
<option selected>...</option>
<option value="w">Win</option>
<option value="l">Lose</option>
</select>
</div>
</div>
<div class="col">
<div class="row">
<div class="col">
<h3>KDA</h3>
</div>
</div>
<div class="row">
<div class="col">
<select class="browser-default custom-select">
<option selected>...</option>
<option value="0">2</option>
<option value="1">3</option>
</select>
</div>
<div class="col">
<select class="browser-default custom-select">
<option selected>...</option>
<option value="0">2</option>
<option value="1">3</option>
</select>
</div>
<div class="col">
<select class="browser-default custom-select">
<option selected>...</option>
<option value="0">2</option>
<option value="1">3</option>
</select>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col">
<h3>Feedback</h3>
<textarea class="w-100"></textarea>
</div>
<div class="col">
<h3>Feedback to Matchup</h3>
<textarea class="w-100"></textarea>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,49 @@
<div class="row">
<div class="col">
<div class="row mb-5">
<div class="col">
<h4>Champion</h4>
</div>
<div class="col">
{% include "partials/champ-selector-dropdown.html" %}
</div>
</div>
<div class="row">
<div class="col">
<h4>Matchups</h4>
</div>
</div>
<div class="row">
<div class="col">
{% include "partials/champ-selector-dropdown.html" %}
</div>
<div class="col">
<select class="browser-default custom-select">
<option selected>Difficulty<option>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
<div class="col">
<textarea class="w-100"></textarea>
</div>
</div>
<div class="row">
<div class="col">
<button class="btn-primary">+</button>
</div>
</div>
<div class="row">
<div class="col">
<div class="border border-secondary my-2"></div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,49 @@
<nav class="navbar navbar-expand-md navbar-dark bg-dark">
<li class="nav-item">
<a class="nav-link" href="#">PH</a>
</li>
</nav>
<nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark">
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarMain">
<ul class="navbar-nav mr-auto">
<li class="nav-item">
<a class="nav-link" href="/">Home</a>
</li>
<li class="nav-item">
<a class="nav-link text-nowrap" href="/about">About Me</a>
</li>
<!--<li class="nav-item">
<a class="nav-link" href="/vod-reviews">Reviews</a>
</li>-->
<li class="nav-item">
<a class="nav-link" href="/shop">Shop</a>
</li>
{% if currentUser %}
<li class="nav-item" style="width: max-content !important;">
<a class="nav-link" href="/dashboard">Dashboard</a>
</li>
{% endif %}
</ul>
<ul class="navbar-nav mr-auto w-100 ml-auto justify-content-end">
{% if currentUser %}
<li class="nav-item navbar-brand">
{{ currentUser }}
</li>
<li class="nav-item">
<a class="nav-link" href="/logout">Logout</a>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="/login">Login</a>
</li>
{% endif %}
</ul>
</div>
</nav>

View File

@@ -0,0 +1 @@
<form target="paypal" action="https://www.paypal.com/cgi-bin/webscr" method="post"><input type="hidden" name="cmd" value="_s-xclick"><input type="hidden" name="hosted_button_id" value="7EB72EVQ74634"><table><tr><td><input type="hidden" name="on0" value="Type">Type</td></tr><tr><td><select name="os0"><option value="VoD Review">VoD Review €10,00 EUR</option><option value="Coaching">Coaching €15,00 EUR</option><option value="Coaching x10">Coaching x10 €100,00 EUR</option></select> </td></tr></table><input type="hidden" name="currency_code" value="EUR"><input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_cart_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!"><img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1"></form>

View File

@@ -0,0 +1,16 @@
<div class="row border-secondary">
{% set types = [ "Pre Game", "Early Game", "Ganks", "Counterjungling", "Mid Game", "Late Game", "All Game" ] %}
{% set ratings = [ "Good", "Decent", "Practiced", "Normal", "Bad", "Worse" ] %}
{% for t in types %}
<div id="{{ t }}" class="col">
<h3>{{ t }}</h3>
<textarea style="width: 100%;"></textarea>
<select class="browser-default custom-select">
<option selected>...</option>
{% for r in ratings %}
<option value="{{ r }}">{{ r }}</option>
{% endfor %}
</select>
</div>
{% endfor %}
</div>

View File

@@ -0,0 +1,20 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Select the roles you can play for the team">
<title>Champ Selection</title>
<!-- Bootstrap core CSS -->
{% include 'default-includes.html' %}
</head>
<body>
{% include 'navbar.html' %}
<main role="main">
<!-- Selector für Teammitglieder wenn Teamcpt -->
<!-- TODO when team has time -->
</main>
</body>

View File

@@ -0,0 +1,59 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Select the roles you can play for the team">
<title>Champ Selection</title>
<!-- Bootstrap core CSS -->
{% include 'default-includes.html' %}
<script src="/static/team_champselect.js"></script>
</head>
<body>
{% include 'navbar.html' %}
<main role="main" class="m-5">
<!-- Selector für Teammitglieder wenn Teamcpt -->
<!-- SELECTION -->
<div style="margin-top: 100px !important;"></div>
<div class="row" id="selection-row">
{% for key in roles.keys() %}
<div class="col-sm {% if loop.index % 2 == 0 %} bg-light {% else %} bg-secondary {% endif %}" id="{{ key }}-selection">
<div class="text-sm-left role-info m1">{{ key }}</div></br>
<select id="{{ key }}-affinity-selector" class="selectpicker" data-live-search="true">
{% for x in range(0,6) %}
<option>{{ x }}</option>
{% endfor %}
</select>
<select id="{{ key }}-selector" class="selectpicker" data-live-search="true">
{% for champ in allChampions %}
<option>{{ champ["name"] }}</option>
{% endfor %}
</select>
<button id="{{ key }}-selector-button" type="button" onClick="addChamp('{{ key }}-selector', '{{ key }}')" class="btn btn-primary add-btn">Add</button>
</div>
{% endfor %}
</div>
<!-- SLECTED CHAMPIONS -->
<div class="row" id="selection-row" style="min-height: 400px;">
{% for key in roles.keys() %}
<div class="col-sm {% if loop.index % 2 == 0 %} bg-light {% else %} bg-secondary {% endif %}" id="{{ key }}-champs">
{% for champ in roles[key].champions %}
<div class="mt-3 mb-3" style="background-color: green; width: 100%; clear: both;">
<div class="mr-2" style="float: left;">AF_PLACEHDR</div>
<div style="float: left;">{{ champ }}</div>
<button style="float: right;" type="button" onClick="removeChamp('{{ champ }}', '{{ key }}')" class="ml-3 btn btn-primary">Remove</button>
</div></br>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
</select>
</main>
</body>

View File

@@ -0,0 +1,50 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Select the roles you can play for the team">
<title>Champ Selection</title>
<!-- Bootstrap core CSS -->
{% include 'default-includes.html' %}
</head>
<body>
{% include 'navbar.html' %}
{% for x in range(1, 10) %}
<main role="main" class="m-5">
<!-- Selector für Teammitglieder wenn Teamcpt -->
<!-- SELECTION -->
<div style="margin-top: 100px !important;"></div>
<div class="row" id="selection-row">
{% for key in roles.keys() %}
<div class="col-sm {% if loop.index % 2 == 0 %} bg-light {% else %} bg-secondary {% endif %}" id="{{ key }}-selection">
<select id="{{ key }}-selector" class="selectpicker" data-live-search="true">
{% for champ in teamChamps %}
<option>{{ champ["name"] }}</option>
{% endfor %}
</select>
<button id="{{ key }}-selector-button" type="button" class="btn btn-primary">Add</button>
</div>
{% endfor %}
</div>
<!-- SLECTED CHAMPIONS -->
<div class="row" id="selection-row" style="min-height: 400px;">
{% for key in roles.keys() %}
<div class="col-sm {% if loop.index % 2 == 0 %} bg-light {% else %} bg-secondary {% endif %}" id="{{ key }}-champs">
{% for champ in roles[key].champions %}
<p>{{ champ }}</p> <button type="button" class="btn">Remove</button>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
</select>
</main>
{% endfor %}
</body>

View File

@@ -0,0 +1,43 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Leauge of Legends Coaching">
<title>Nemesis Coaching</title>
<!-- Bootstrap core CSS -->
{% include 'default-includes.html' %}
</head>
<body>
{% include 'navbar.html' %}
<main role="main">
<!-- Services -->
<div class="container pt-5 my-5">
<div class="row">
{% for comp in teamComps %}
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<div class="card-body">
<h2>{{ comp["name"] }}</h2>
<p>{{ comp["Description"] }}</p>
<p><a class="btn btn-secondary" href="/team_composition_single?id={{ comp['config-id'] }}" role="button">Open &raquo;</a></p>
</p>
</div>
</div>
</div>
{% endfor %}
<hr>
</div>
</div>
{% include 'footer.html' %}
<button type="button" onClick="createNewTeam()" class="ml-3 btn btn-primary">Create New Team</button>
</main>
</body>
</html>

View File

@@ -0,0 +1,52 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Select the roles you can play for the team">
<title>Champ Selection</title>
<!-- Bootstrap core CSS -->
{% include 'default-includes.html' %}
<script src="/static/team_champselect.js"></script>
</head>
<body>
{% include 'navbar.html' %}
<main role="main" class="m-5">
<!-- Selector für Teammitglieder wenn Teamcpt -->
<!-- SELECTION -->
<div style="margin-top: 100px !important;"></div>
<div class="row" id="selection-row">
{% for key in roles.keys() %}
<div class="col-sm {% if loop.index % 2 == 0 %} bg-light {% else %} bg-secondary {% endif %}" id="{{ key }}-selection">
<div class="text-sm-left role-info m1">{{ key }}</div></br>
<select id="{{ key }}-selector" class="selectpicker" data-live-search="true">
{% for champ in teamChamps %}
<option>{{ champ["name"] }}</option>
{% endfor %}
</select>
<button id="{{ key }}-selector-button" type="button" onClick="addChamp('{{ key }}-selector', '{{ key }}')" class="btn btn-primary">Add</button>
</div>
{% endfor %}
</div>
<!-- SLECTED CHAMPIONS -->
<div class="row" id="selection-row" style="min-height: 400px;">
{% for key in roles.keys() %}
<div class="col-sm {% if loop.index % 2 == 0 %} bg-light {% else %} bg-secondary {% endif %}" id="{{ key }}-champs">
{% for champ in roles[key].champions %}
<p>{{ champ }}</p> <button type="button" onClick="removeChamp('{{ champ }}', '{{ key }}')" class="btn">Remove</button>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
</select>
</main>
</body>

View File

@@ -0,0 +1,21 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Select the roles you can play for the team">
<title>Champ Selection</title>
<!-- Bootstrap core CSS -->
{% include 'default-includes.html' %}
</head>
<body>
{% include 'navbar.html' %}
<main role="main">
<!-- day | Game Nr | Role | played | against | side | result
| day time | duoque? | kda | notes -->
</main>
</body>

View File

@@ -0,0 +1,38 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap core CSS -->
{% include 'partials/header.html' %}
<script src="/static/contact.js"></script>
</head>
<body class="bg-secondary">
{% include 'partials/navbar.html' %}
<div class="container mt-5 mb-5">
<div class="row impressum mt-5">
<div class="col text-min-dimensions">
<h2>Nemesis</h2>
<h4>Coach</h4>
<p>
Nemesis is bad Nemesis is bad
Nemesis is bad Nemesis is bad
Nemesis is bad Nemesis is bad
Nemesis is bad Nemesis is bad
Nemesis is bad Nemesis is bad
Nemesis is bad Nemesis is bad
</p>
</div>
<div class="col image-min-dimensions">
<img class="img-responsive w-100 image-max-dimensions" src="https://esports-erlangen.de/picture/nemesis_euw.jpg?scalex=1280&scaley=960"></img>
</div>
</div>
</div>
{% include "partials/footer.html" %}
</body>
</html>

View File

@@ -0,0 +1,85 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap core CSS -->
{% include 'partials/header.html' %}
<script src="/static/contact.js"></script>
</head>
<body class="bg-secondary">
{% include 'partials/navbar.html' %}
<div id="main-container" class="container" style="margin-top: 4vw;">
<section class="mb-4">
<h2 class="text-color-special h1-responsive font-weight-bold my-4">
{{ conf['CONTACT_HEADLINE'] }}
</h2>
<p class="text-center w-responsive mx-auto mb-5"> </p>
<div class="row">
<div class="col-md-9 mb-md-0 mb-5">
<form id="contact-form" name="contact-form">
<!-- action="contact-api" method="POST" enctype='application/json'> -->
<div class="row">
<div class="col-md-6 mt-2">
<div class="md-form mb-0">
<input placeholder="{{ conf['CONTACT_PLACEHOLDER_NAME'] }}"
type="text" id="name" name="name" class="form-control">
</div>
</div>
<div class="col-md-6 mt-2">
<div class="md-form mb-0">
<input type="text" id="email" name="email" class="form-control"
placeholder="{{ conf['CONTACT_PLACEHOLDER_EMAIL'] }}">
</div>
</div>
</div>
<div class="row mt-2">
<div class="col-md-12">
<div class="md-form mb-0">
<input type="text" id="subject" name="subject" class="form-control"
placeholder="{{ conf['CONTACT_PLACEHOLDER_SUBJECT'] }}">
</div>
</div>
</div>
<div class="row mt-2">
<div class="col-md-12">
<div class="md-form">
<textarea type="text" id="message" name="message" rows="10"
placeholder="{{ conf['CONTACT_PLACEHOLDER_TEXTAREA'] }}"
class="form-control md-textarea"></textarea>
</div>
</div>
</div>
</form>
<div class="text-center text-md-left mt-4">
<a class="btn btn-light w-50" onclick="submitContactForm()">Absenden</a>
</div>
<div class="status"></div>
</div>
<div class="border p-3 col-md-3 text-center bg-special">
<ul class="list-unstyled mb-0">
<li><i class="fas fa-envelope mt-4 fa-2x"></i>
<p>Wir freuen uns auf Ihre Nachricht!</p>
{% if conf['CONTACT_EMAIL'] %}
<hr>
<p>Natürlich können Sie uns auch direkt per Mail kontaktieren.</p></br>
<a type="button" class="btn btn-light p-3 w-75"
href="mailto:{{ conf['CONTACT_EMAIL'] }}">{{ conf['CONTACT_EMAIL'] }}</a>
{% endif %}
</li>
</ul>
</div>
</div>
</section>
</div>
{% include "partials/footer.html" %}
</body>
</html>

View File

@@ -0,0 +1,61 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Leauge of Legends Coaching">
<title>Nemesis Coaching</title>
{% include 'partials/header.html' %}
</head>
<body>
{% include 'partials/navbar.html' %}
<div class="navbar navbar-default"></div>
<div class="container mt-5">
<div class="row impressum">
<div class="col-lg-12">
<h1>Impressum</h1>
<h4><b>{{ config["company-name"] }}</b></h4>
</div>
<div class="col-lg-6">
<p>Adresse<br/>{{ config["company-street"] }}<br/>{{ config["company-postal"] }}</p>
<p>Telefon<br/>{{ config["company-phone"] }}</p>
</div>
<div class="col-lg-6">
<p>E-Mail<br/><a href="mailto:{{ config["company-mail"] }}">{{ config["company-mail"] }}</a></p>
<p>Verantwortlicher<br/>{{ config["company-responsible"] }}</p>
{{ config["company-freetext"] }}
</div>
<div class="col-lg-12">
Other Legal BS
</div>
</div>
<div class=mt-5></div>
<h1>Haftung für Inhalte</h1>
<p>
Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte können wir jedoch keine Gewähr übernehmen. Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.
</p>
<h1>Haftung für Links</h1>
<p>
Unser Angebot enthält Links zu externen Webseiten Dritter, auf deren Inhalte wir keinen Einfluss haben. Deshalb können wir für diese fremden Inhalte auch keine Gewähr übernehmen. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder Betreiber der Seiten verantwortlich. Die verlinkten Seiten wurden zum Zeitpunkt der Verlinkung auf mögliche Rechtsverstöße überprüft. Rechtswidrige Inhalte waren zum Zeitpunkt der Verlinkung nicht erkennbar. Eine permanente inhaltliche Kontrolle der verlinkten Seiten ist jedoch ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar. Bei Bekanntwerden von Rechtsverletzungen werden wir derartige Links umgehend entfernen.
</p>
<h1>Datenschutz</h1>
<p>
Die Nutzung unserer Webseite ist in der Regel ohne Angabe personenbezogener Daten möglich. Soweit auf unseren Seiten personenbezogene Daten (beispielsweise Name, Anschrift oder eMail-Adressen) erhoben werden, erfolgt dies, soweit möglich, stets auf freiwilliger Basis. Diese Daten werden ohne Ihre ausdrückliche Zustimmung nicht an Dritte weitergegeben. Wir weisen darauf hin, dass die Datenübertragung im Internet (z.B. bei der Kommunikation per E-Mail) Sicherheitslücken aufweisen kann. Ein lückenloser Schutz der Daten vor dem Zugriff durch Dritte ist nicht möglich. Der Nutzung von im Rahmen der Impressumspflicht veröffentlichten Kontaktdaten durch Dritte zur Übersendung von nicht ausdrücklich angeforderter Werbung und Informationsmaterialien wird hiermit ausdrücklich widersprochen. Die Betreiber der Seiten behalten sich ausdrücklich rechtliche Schritte im Falle der unverlangten Zusendung von Werbeinformationen, etwa durch Spam-Mails, vor.
</p>
<h1>Externe Inhalte</h1>
<p>
Externe Inhalte erforden eine Zustimmung des Nutzers, sollte der Nutzer der Einbindung
dieser Inhalte zustimmen, ist er sich bewusst, dass für diese Inhalte die
Datenschutzbestimmungen des jeweiligen Drittanbieters gelten.
</p>
</div>
<div class=pb-5></div>
<div class=pb-5></div>
{% include 'partials/footer.html' %}
</html>

View File

@@ -0,0 +1,88 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="League of Legends Coaching">
<title>Nemesis Coaching</title>
<!-- Bootstrap core CSS -->
{% include 'partials/header.html' %}
</head>
<body>
{% include 'partials/navbar.html' %}
<main role="main" class="bg-special">
<header class="eyecatcher" style="background-image: url(https://blog.atlantishq.de/lolproplay_by_Clement_Grandjean_CCSA_licensed.jpg);">
<div class="container h-100">
<div class="row h-100 align-items-center">
<div class="col-12 col-sm-12 text-center">
<h1 class="special-header">{{ config["main-title"] }}</h1>
<p class="lead special-sub-header">{{ config["subtitle"] }}</p>
</div>
</div>
</div>
</header>
<!-- Services -->
<div class="container pt-3">
<div class="row">
{% for card in services %}
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top" src="static/pictures/{{ card["picture"] }}">
<div class="card-body">
<h3>{{ card["headline"] }}</h3>
<p>{{ card["description"] }}</p>
<!--
<p><a class="btn btn-secondary" href="{{ card["primary-link"] }}" role="button">Read More &raquo;</a></p>
-->
</p>
</div>
</div>
</div>
{% endfor %}
<hr>
</div>
</div>
<!-- Prices -->
{% for prices in pricesSections %}
<section class="pricing py-5">
<div class="container">
<div class="row">
{% for card in prices %}
<div class="col-lg-4 pb-3">
<div class="card mb-lg-0">
<div class="card-body">
<h5 class="card-title text-muted text-uppercase text-center">{{ card["header"] }}</h5>
<h6 class="card-price text-center">{{ card["price"] }}&euro;<span class="period">{{ card["period"] }}</span></h6>
<hr>
<ul class="fa-ul">
{% for feature in card["features"] %}
<li><span class="fa-li"><i class="fas fa-check"></i></span>{{ feature }}</li>
{% endfor %}
{% for feature in card["disabledFeatures"] %}
<li class="text-muted"><span class="fa-li"><i class="fas fa-times"></i></span>{{ feature }}</li>
{% endfor %}
</ul>
<a href="/contact?offerId={{ card["config-id"] }}" class="btn btn-block btn-primary text-uppercase">Contact Me</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</section>
{% endfor %}
<div class="pb-5"></div>
</main>
{% include 'partials/footer.html' %}
</body>
</html>

View File

@@ -0,0 +1,56 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Leauge of Legends Coaching">
<link rel=stylesheet href="/static/login.css">
<title>Nemesis Coaching</title>
{% include 'partials/header.html' %}
</head>
<body style="background-image: url(https://blog.atlantishq.de/lolproplay_by_Clement_Grandjean_CCSA_licensed.jpg);">
{% include 'partials/navbar.html' %}
<div class="container">
<div class="d-flex justify-content-center h-100">
<div class="card">
<div class="card-header">
<h3>Sign In</h3>
</div>
<div class="card-body">
<form method="post">
<div class="input-group form-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-user"></i></span>
</div>
<input type="text" class="form-control" name="username" placeholder="username">
</div>
<div class="input-group form-group">
<div class="input-group-prepend">
<span class="input-group-text"><i class="fas fa-key"></i></span>
</div>
<input type="password" class="form-control" name="password" placeholder="password">
</div>
<div class="form-group">
<input type="submit" value="Login" class="btn float-right login_btn">
</div>
</form>
</div>
<div class="card-footer">
<div class="d-flex justify-content-center links mb-2">
{{ config["login-help"] }}
</div>
<div class="d-flex justify-content-center links">
<a class="btn btn-block btn-primary text-uppercase" href="/contact">{{ config["login-help-action"] }}</a>
</div>
</div>
</div>
</div>
</div>
{% include "partials/footer.html" %}
</body>
</html>

View File

@@ -0,0 +1,80 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<!-- Bootstrap core CSS -->
{% include 'partials/header.html' %}
<script src="/static/contact.js"></script>
</head>
<body class="bg-secondary">
{% include 'partials/navbar.html' %}
<div class="container mt-5 mb-5">
<div class="row mt-5">
<div class="col">
<div class="col image-min-dimensions">
<img class="img-responsive w-100 image-max-dimensions"
src="https://esports-erlangen.de/picture/nemesis_euw.jpg"></img>
</div>
</div>
<div class="p-5 col text-min-dimensions">
<div class="row">
<div class="col">
<h2>VoD Review</h2>
<h4>Review eines deiner Spiele als VoD</h4>
<p>
Nemesis is bad Nemesis is bad
Nemesis is bad Nemesis is bad
Nemesis is bad Nemesis is bad
</p>
</div>
</div>
<div class="row">
<div class="col">
{% include "partials/paypal-button.html" %}
</div>
</div>
</div>
</div>
<!-- example ##################### example -->
<div class="row mt-5">
<div class="col">
<div class="col image-min-dimensions">
<img class="img-responsive w-100 image-max-dimensions"
src="https://esports-erlangen.de/picture/nemesis_euw.jpg"></img>
</div>
</div>
<div class="p-5 col text-min-dimensions">
<div class="row">
<div class="col">
<h2>Live Coaching</h2>
<h4>Live im discord blablabla</h4>
<p>
Nemesis is bad Nemesis is bad
Nemesis is bad Nemesis is bad
Nemesis is bad Nemesis is bad
</p>
</div>
</div>
<div class="row">
<div class="col">
{% include "partials/paypal-button.html" %}
</div>
</div>
</div>
</div>
<!-- example ##################### example -->
</div>
<div class=pb-4></div>
{% include "partials/footer.html" %}
</body>
</html>

View File

@@ -0,0 +1,31 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Leauge of Legends Coaching">
<title>Nemesis Coaching</title>
<!-- Bootstrap core CSS -->
{% include 'partials/header.html' %}
</head>
<body>
{% include 'partials/navbar.html' %}
<div class="jumbotron text-center">
<h1 class="display-3">Thank You!</h1>
<p class="lead"><strong>Your contact request has been send!</strong> We will answer you as soon as possible.</p>
<hr>
<p>
Still having trouble? <a href="/contact">Contact us</a>
</p>
<p class="lead">
<a class="btn btn-primary btn-sm" href="/" role="button">Return to Homepage</a>
</p>
</div>
{% include 'partials/footer.html' %}
</body>
</html>

View File

@@ -0,0 +1,77 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Leauge of Legends Coaching">
<title>Nemesis Coaching</title>
<!-- Bootstrap core CSS -->
{% include 'partials/header.html' %}
</head>
<body>
{% include 'partials/navbar.html' %}
<main role="main">
<!-- Services -->
<div class="container pt-1 my-5">
{% if currentUser.single %}
<div class="row p-2 border border-secondary">
<div id="myaccounts" class="w-50">
<h3>Accounts:</h3>
{% for acc in currentUser.accounts %}
{{ acc }}<br>
{% endfor %}
</div>
<div id=mychamps class="w-50 float-left">
<h3>Champions:</h3>
{% for c in currentUser.selectedChampions %}
<div id="championName">{{ c }}</div>
{% endfor %}
<div id="championSelector"></div>
<div id="roleSelector"></div>
</div>
</div>
<div class="row mb-3 p-2 border border-secondary">
<div class="col-sm">
<button class="btn-primary" id="addAccount">Add Account</button>
</div>
<div class="col-sm">
<button class="btn-primary" id="addChampion">Add Champion</button>
</div>
</div>
{% endif %}
<div class="row">
{% for table in currentUser.allowedFeatures() %}
<div class="col-md-4">
<div class="card mb-4 box-shadow">
<img class="card-img-top {% if table['disabled'] %}pic-disabled{% endif %}
" src="static/pictures/{{ table["picture"] }}">
<div class="card-body">
<h2>{{ table["title"] }}</h2>
<p>{{ table["description"] }}</p>
{% if table.get("href") %}
<p><a class="btn btn-secondary" href="{{ table["href"] }}" role="button">Open &raquo;</a></p>
{% else %}
<p><a class="btn btn-secondary {% if table['disabled'] %}disabled{% endif %}"
href="/table?table={{ table['config-identifier'] }}"
role="button">Open &raquo;</a></p>
{% endif %}
</p>
</div>
</div>
</div>
{% endfor %}
<hr>
</div>
</div>
{% include 'partials/footer.html' %}
</main>
</body>
</html>

31
usermanagement.py Normal file
View File

@@ -0,0 +1,31 @@
import flask_login as fl
import jsonConfig as jc
class User(fl.UserMixin):
def __init__(self, name):
self.id = name
self.name = "user" + str(id)
self.password = self.name + "_secret"
self.single = True
self.accounts = ["Sheppy", "TheArmCommander"]
self.selectedChampions = ["Lulu", "Aatrox", "Brand"]
self.matchhistoryEntries = []
def __repr__(self):
return self.id
def __str__(self):
return str(self.id)
def allowedFeatures(self):
'''Dynamicly return the features this user is allowed to use'''
allFeatures = jc.readJsonDir("config/tables/")
allowedFeatures = []
if not allowedFeatures:
allowedFeatures = allFeatures
else:
allowedFeatures = filter(lambda x: x["title"] in allowedFeatures,
allFeatures)
return sorted(allowedFeatures, key=lambda x: x["title"])