From 90ccfcdf51d83e02a7ab2997eee819e8afd7f5ba Mon Sep 17 00:00:00 2001 From: Yannik Schmidt Date: Fri, 30 Jul 2021 00:42:41 +0200 Subject: [PATCH] Initial (reset) --- .gitignore | 21 +++ README.md | 24 +++ composition_builder.py | 32 ++++ database.py | 55 +++++++ entities/CellContainer.py | 74 +++++++++ jsonConfig.py | 72 +++++++++ pages_api.py | 20 +++ pages_loginmanagement.py | 29 ++++ pages_standard.py | 73 +++++++++ server.py | 46 ++++++ static/contact.js | 48 ++++++ static/defaultFavicon.ico | Bin 0 -> 15406 bytes static/login.css | 64 ++++++++ static/site.css | 105 ++++++++++++ static/table.css | 47 ++++++ static/table.js | 152 ++++++++++++++++++ static/team_champselect.js | 49 ++++++ subpages_dashboard.py | 96 +++++++++++ templates/dashboardSubpages/matchhistory.html | 21 +++ .../dashboardSubpages/self_analysis.html | 36 +++++ .../dashboardSubpages/self_matchups.html | 36 +++++ .../subpage_solo_improvement.html | 85 ++++++++++ .../subpage_team_history.html | 35 ++++ .../subpage_team_improvement.html | 85 ++++++++++ templates/entities/cell.html | 2 + templates/entities/row.html | 4 + templates/entities/table.html | 3 + templates/entities/tableBody.html | 42 +++++ .../partials/champ-selector-dropdown.html | 6 + templates/partials/footer.html | 3 + templates/partials/header.html | 10 ++ .../matchhistory-analysis-team-obj.html | 136 ++++++++++++++++ templates/partials/matchup-single-champ.html | 49 ++++++ templates/partials/navbar.html | 49 ++++++ templates/partials/paypal-button.html | 1 + templates/partials/self-analysis-obj.html | 16 ++ templates/special/team_calendar.html | 20 +++ templates/special/team_champselect.html | 59 +++++++ templates/special/team_composition.html | 50 ++++++ .../special/team_composition_overview.html | 43 +++++ .../special/team_composition_single.html | 52 ++++++ templates/special/team_matchhistory.html | 21 +++ templates/standard/about.html | 38 +++++ templates/standard/contact.html | 85 ++++++++++ templates/standard/impressum.html | 61 +++++++ templates/standard/index.html | 88 ++++++++++ templates/standard/login.html | 56 +++++++ templates/standard/shop.html | 80 +++++++++ templates/standard/thanks.html | 31 ++++ templates/standard/user_dashboard.html | 77 +++++++++ usermanagement.py | 31 ++++ 51 files changed, 2418 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 composition_builder.py create mode 100644 database.py create mode 100644 entities/CellContainer.py create mode 100644 jsonConfig.py create mode 100644 pages_api.py create mode 100644 pages_loginmanagement.py create mode 100644 pages_standard.py create mode 100755 server.py create mode 100644 static/contact.js create mode 100644 static/defaultFavicon.ico create mode 100644 static/login.css create mode 100644 static/site.css create mode 100644 static/table.css create mode 100644 static/table.js create mode 100644 static/team_champselect.js create mode 100644 subpages_dashboard.py create mode 100644 templates/dashboardSubpages/matchhistory.html create mode 100644 templates/dashboardSubpages/self_analysis.html create mode 100644 templates/dashboardSubpages/self_matchups.html create mode 100644 templates/dashboardSubpages/subpage_solo_improvement.html create mode 100644 templates/dashboardSubpages/subpage_team_history.html create mode 100644 templates/dashboardSubpages/subpage_team_improvement.html create mode 100644 templates/entities/cell.html create mode 100644 templates/entities/row.html create mode 100644 templates/entities/table.html create mode 100644 templates/entities/tableBody.html create mode 100644 templates/partials/champ-selector-dropdown.html create mode 100644 templates/partials/footer.html create mode 100644 templates/partials/header.html create mode 100644 templates/partials/matchhistory-analysis-team-obj.html create mode 100644 templates/partials/matchup-single-champ.html create mode 100644 templates/partials/navbar.html create mode 100644 templates/partials/paypal-button.html create mode 100644 templates/partials/self-analysis-obj.html create mode 100644 templates/special/team_calendar.html create mode 100644 templates/special/team_champselect.html create mode 100644 templates/special/team_composition.html create mode 100644 templates/special/team_composition_overview.html create mode 100644 templates/special/team_composition_single.html create mode 100644 templates/special/team_matchhistory.html create mode 100644 templates/standard/about.html create mode 100644 templates/standard/contact.html create mode 100644 templates/standard/impressum.html create mode 100644 templates/standard/index.html create mode 100644 templates/standard/login.html create mode 100644 templates/standard/shop.html create mode 100644 templates/standard/thanks.html create mode 100644 templates/standard/user_dashboard.html create mode 100644 usermanagement.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..40dbb07 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +*.swp +*.txt +*.ncsv +*.out +*.log +*.zip + +pass.secret + +css/ +js/ +data/ +build/ +fontawesome/ +static/pictures/ +config/champions/ + +__pychache__/ +*.pyc + +config/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..d153708 --- /dev/null +++ b/README.md @@ -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"] + } diff --git a/composition_builder.py b/composition_builder.py new file mode 100644 index 0000000..76cd367 --- /dev/null +++ b/composition_builder.py @@ -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]) \ No newline at end of file diff --git a/database.py b/database.py new file mode 100644 index 0000000..2815e3f --- /dev/null +++ b/database.py @@ -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) diff --git a/entities/CellContainer.py b/entities/CellContainer.py new file mode 100644 index 0000000..bd9a0d0 --- /dev/null +++ b/entities/CellContainer.py @@ -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 diff --git a/jsonConfig.py b/jsonConfig.py new file mode 100644 index 0000000..591ed49 --- /dev/null +++ b/jsonConfig.py @@ -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/")] diff --git a/pages_api.py b/pages_api.py new file mode 100644 index 0000000..1b91955 --- /dev/null +++ b/pages_api.py @@ -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") \ No newline at end of file diff --git a/pages_loginmanagement.py b/pages_loginmanagement.py new file mode 100644 index 0000000..b652164 --- /dev/null +++ b/pages_loginmanagement.py @@ -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("/") diff --git a/pages_standard.py b/pages_standard.py new file mode 100644 index 0000000..47d6e75 --- /dev/null +++ b/pages_standard.py @@ -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) diff --git a/server.py b/server.py new file mode 100755 index 0000000..9f546ec --- /dev/null +++ b/server.py @@ -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/') +#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) diff --git a/static/contact.js b/static/contact.js new file mode 100644 index 0000000..7789c59 --- /dev/null +++ b/static/contact.js @@ -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) +} \ No newline at end of file diff --git a/static/defaultFavicon.ico b/static/defaultFavicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..cd1ce4ff1cdfc97ca1f73573358f1b90e8a05762 GIT binary patch literal 15406 zcmeHN2~d?)6#kJNR6s@4R6szHWXCP{T&A)#HFHBn5Jk&{R5BOLU2)$<5nOPlW}3=r zY;SVXw5DZh+MJdt%cXQ$RT3*t=ezIy_x3PVo%_Fg?>XnY=kDhmMe$O+ zmC#Uy^C%_HM^U;diV_uN_#YppD5YGMl47jys3@ny6(yeAFbPu_$~m+CJ4#EHqjZB4 z)iQy`URqU_J#tlAr|14VFHrEtuPJcr59EE|n)t^5MrD7KcgbHAy8L~S>**sV?v_eJtLbd%ynuchREdHh{TaidmKr-zpl+UU`v9q2<=ek9sNW|vUM5i7~3_&3Vv zHjbJtJ*7(vo|1mm$0qubE2-VoQcCJSgBq1yw}XBaDM$XLn6aBEcJw+M^fwInS84E% zWO~LHUho>F-IG-$^gYOR>*Haq79a* zi&>u+Io+xQy+X!CA=4zge&&Fw-}NKE?sTr#zq%N2J=vb}+Mn%rjLrgfoTrHS$IViR z@%(DC?sKzpw8Py%-Md0od`KNKw^P)l-SW&<>uQ?gOulR8GveIC*`MAclY%yVD`fcc zc^1w5QI3?>a}xRPxh(2}H=L!6uH!^`{IF%za{epgzEV@U+IBws|CBOf#}vu_p$*Pc zRep5l4p|p7ZnG?JT=bPJ*Yd6Dw|D6^$tyB@zZtKsilOunjrtQp)#fg!cm6bjh(qeB>KrGMjI3YzpD69ooa_hMUf!5;bG znQ(uBK?l8a9P59HY5hm$l$h}f+VZ`S0bQ&5SNqdt@LW-sIB>QZjapyRJWwM8`>B>L z^y&Ak%J<)ON$!8G=Dc7=qoud??REGce%Gv*GVn%evi38<4>FSOeO%VXjNK^Sf_TGq z=C?^kxAD|=`VoE~{b#p)yfed}e8Vi^%_4B~yDNCpH=?d>Ub#4Da2~YhF$!PsDxU>c z?3QawFP{2Z+e18KVcZ*a=1zUS0Ss@!>hZa9{0#5V`jU_Lr|At`Kksi{!@!+U0Xqqs zXq7BZ)!Iq8UMZtcVck)vv9*A?`D_Fnj9VZ?E!< znP<#AbHEjIfNc?eyMGZjF`wdJ9kz{HJBz&!T%`^Zwo?4?LaV9Sl9OU=Vb_AaLa$xB z+=FKL{B~cam@ymJXFi=$`sPw{|2+P`(J3}YZ9ic1felx+pTV|;l)t)r)=k3PwCw!NIMYPrgzVz(84ov4tpJ}q^OCz1P_(h-9zSyGWZ94_Wdr~YOy%- z-ITGZ*pb5~-fYP`W;z9bZ?^Blm#%FK+pSV=vRlEgk;-;NwGGA~h;2{sqa*$Ru?3D` zwMOf^_llGUxhZ|8vCsZ|6*;i?$MU>Tu_4F!X?eDtUM_Wy`k3)sL=1j zucoU%k=W6{ls;31-^f`w_4wG0pY8S%KhN*z3ERx@cN)G-#JuRqbym5<_z~m6z7N$# zjd=y#H(vjxoLkPqYK@Ph_9=WG11 zLb;BU`%U9@@P*sp$2l93|B{?bSj)IOS?bK0|DCCS;DWC%wRg501Nf(%iPw^s&tA*( zw}$7JcK0WRB6a=Fe|bRv~=tYFqf7<@(j& zZ@%bljg8=77O2GOs2I4Y|veo!THjEjXpHzc4@t`%=(FTh@XJ}7;l5-3*X}S z;5Z2hU0AT4okRi z0d*OYFLaIlC2`Ok{{MuCt&Ye)CeCz6@wt@#@J;a+L_9LiUA)y1Tka}dyRTXShH*Gy YTn`O#O{;75q=-3z-aS8$1{~DD|KO5gQUCw| literal 0 HcmV?d00001 diff --git a/static/login.css b/static/login.css new file mode 100644 index 0000000..27740b3 --- /dev/null +++ b/static/login.css @@ -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; +} \ No newline at end of file diff --git a/static/site.css b/static/site.css new file mode 100644 index 0000000..7ef0a32 --- /dev/null +++ b/static/site.css @@ -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; +} diff --git a/static/table.css b/static/table.css new file mode 100644 index 0000000..cfd1f8a --- /dev/null +++ b/static/table.css @@ -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; +} diff --git a/static/table.js b/static/table.js new file mode 100644 index 0000000..7ad5132 --- /dev/null +++ b/static/table.js @@ -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 = '' + words[i] + '\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 diff --git a/static/team_champselect.js b/static/team_champselect.js new file mode 100644 index 0000000..b39e25b --- /dev/null +++ b/static/team_champselect.js @@ -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()); +} \ No newline at end of file diff --git a/subpages_dashboard.py b/subpages_dashboard.py new file mode 100644 index 0000000..5711d53 --- /dev/null +++ b/subpages_dashboard.py @@ -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) + diff --git a/templates/dashboardSubpages/matchhistory.html b/templates/dashboardSubpages/matchhistory.html new file mode 100644 index 0000000..19661de --- /dev/null +++ b/templates/dashboardSubpages/matchhistory.html @@ -0,0 +1,21 @@ + + + + + + + + Champ Selection + + + {% include 'default-includes.html' %} + + + + {% include 'navbar.html' %} +
+ + +
+ \ No newline at end of file diff --git a/templates/dashboardSubpages/self_analysis.html b/templates/dashboardSubpages/self_analysis.html new file mode 100644 index 0000000..3150976 --- /dev/null +++ b/templates/dashboardSubpages/self_analysis.html @@ -0,0 +1,36 @@ + + + + + + + + Nemesis Coaching + + + {% include 'partials/header.html' %} + + + + + {% include 'partials/navbar.html' %} + +
+
+ +
+
+ {% for m_entry in currentUser.selfAnalysisEntries %} + {% include "partials/self-analysis-obj.html" %} + {% endfor %} + {% include "partials/self-analysis-obj.html" %} +
+
+
+
+ + + {% include 'partials/footer.html' %} + + + diff --git a/templates/dashboardSubpages/self_matchups.html b/templates/dashboardSubpages/self_matchups.html new file mode 100644 index 0000000..0a9bcb8 --- /dev/null +++ b/templates/dashboardSubpages/self_matchups.html @@ -0,0 +1,36 @@ + + + + + + + + Nemesis Coaching + + + {% include 'partials/header.html' %} + + + + + {% include 'partials/navbar.html' %} + +
+
+ +
+
+ {% for m_entry in currentUser.matchupEntris %} + {% include "partials/matchup-single-champ.html" %} + {% endfor %} + {% include "partials/matchup-single-champ.html" %} +
+
+
+
+ +
+ {% include 'partials/footer.html' %} + + + diff --git a/templates/dashboardSubpages/subpage_solo_improvement.html b/templates/dashboardSubpages/subpage_solo_improvement.html new file mode 100644 index 0000000..fec66e1 --- /dev/null +++ b/templates/dashboardSubpages/subpage_solo_improvement.html @@ -0,0 +1,85 @@ + + + + + + + + Nemesis Coaching + + + {% include 'default-includes.html' %} + + + + + {% include 'navbar.html' %} + +
+ +
+
+
+
+

{{ config["main-title"] }}

+

{{ config["subtitle"] }}

+
+
+
+
+ + +
+
+ {% for card in services %} +
+
+ +
+

{{ card["headline"] }}

+

{{ card["description"] }}

+

Read More »

+

+
+
+
+ {% endfor %} +
+
+
+ + + {% for prices in pricesSections %} +
+
+
+ {% for card in prices %} +
+
+
+
{{ card["header"] }}
+
{{ card["price"] }}€{{ card["period"] }}
+
+
    + {% for feature in card["features"] %} +
  • {{ feature }}
  • + {% endfor %} + {% for feature in card["disabledFeatures"] %} +
  • {{ feature }}
  • + {% endfor %} +
+ Contact Me +
+
+
+ {% endfor %} +
+
+
+ {% endfor %} + + {% include 'footer.html' %} +
+ + + \ No newline at end of file diff --git a/templates/dashboardSubpages/subpage_team_history.html b/templates/dashboardSubpages/subpage_team_history.html new file mode 100644 index 0000000..f0cc52f --- /dev/null +++ b/templates/dashboardSubpages/subpage_team_history.html @@ -0,0 +1,35 @@ + + + + + + + + Nemesis Coaching + + + {% include 'partials/header.html' %} + + + + + {% include 'partials/navbar.html' %} + +
+
+ +
+ {% for m_entry in currentUser.matchhistoryEntries %} + {% include "partials/matchhistory-analysis-team-obj.html" %} + {% endfor %} + {% include "partials/matchhistory-analysis-team-obj.html" %} +
+
+
+
+ + + {% include 'partials/footer.html' %} + + + diff --git a/templates/dashboardSubpages/subpage_team_improvement.html b/templates/dashboardSubpages/subpage_team_improvement.html new file mode 100644 index 0000000..fec66e1 --- /dev/null +++ b/templates/dashboardSubpages/subpage_team_improvement.html @@ -0,0 +1,85 @@ + + + + + + + + Nemesis Coaching + + + {% include 'default-includes.html' %} + + + + + {% include 'navbar.html' %} + +
+ +
+
+
+
+

{{ config["main-title"] }}

+

{{ config["subtitle"] }}

+
+
+
+
+ + +
+
+ {% for card in services %} +
+
+ +
+

{{ card["headline"] }}

+

{{ card["description"] }}

+

Read More »

+

+
+
+
+ {% endfor %} +
+
+
+ + + {% for prices in pricesSections %} +
+
+
+ {% for card in prices %} +
+
+
+
{{ card["header"] }}
+
{{ card["price"] }}€{{ card["period"] }}
+
+
    + {% for feature in card["features"] %} +
  • {{ feature }}
  • + {% endfor %} + {% for feature in card["disabledFeatures"] %} +
  • {{ feature }}
  • + {% endfor %} +
+ Contact Me +
+
+
+ {% endfor %} +
+
+
+ {% endfor %} + + {% include 'footer.html' %} +
+ + + \ No newline at end of file diff --git a/templates/entities/cell.html b/templates/entities/cell.html new file mode 100644 index 0000000..782a524 --- /dev/null +++ b/templates/entities/cell.html @@ -0,0 +1,2 @@ +{{ cellContent }} + diff --git a/templates/entities/row.html b/templates/entities/row.html new file mode 100644 index 0000000..eb2223f --- /dev/null +++ b/templates/entities/row.html @@ -0,0 +1,4 @@ + + {{ cells }} + + diff --git a/templates/entities/table.html b/templates/entities/table.html new file mode 100644 index 0000000..61acf45 --- /dev/null +++ b/templates/entities/table.html @@ -0,0 +1,3 @@ + + {{ tableContent }} +
diff --git a/templates/entities/tableBody.html b/templates/entities/tableBody.html new file mode 100644 index 0000000..f0c37a6 --- /dev/null +++ b/templates/entities/tableBody.html @@ -0,0 +1,42 @@ + + + + Basic Table + + + + + + + {% include 'default-includes.html' %} + + + {% include 'navbar.html' %} + + + + + {% if helpRow %} + {{ helpRow }} {% if champion %} {{ champion}} {% endif %} + {% else %} + + + +
+ {% endif %} +
+ Left CTRL or SHIFT to change color of selected cell (or press the desired color with the cell selected) +
+
+ {{ body }} +
+ +
+ + diff --git a/templates/partials/champ-selector-dropdown.html b/templates/partials/champ-selector-dropdown.html new file mode 100644 index 0000000..6fdb4f0 --- /dev/null +++ b/templates/partials/champ-selector-dropdown.html @@ -0,0 +1,6 @@ + diff --git a/templates/partials/footer.html b/templates/partials/footer.html new file mode 100644 index 0000000..60ed948 --- /dev/null +++ b/templates/partials/footer.html @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/templates/partials/header.html b/templates/partials/header.html new file mode 100644 index 0000000..1df478b --- /dev/null +++ b/templates/partials/header.html @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/templates/partials/matchhistory-analysis-team-obj.html b/templates/partials/matchhistory-analysis-team-obj.html new file mode 100644 index 0000000..b5149c9 --- /dev/null +++ b/templates/partials/matchhistory-analysis-team-obj.html @@ -0,0 +1,136 @@ +
+ {% set positions = [ "Top", "Jgl", "Mid", "Bot", "Sup" ] %} + {% set gameTypes = [ "Leauge", "Tournament", "Scrim", "Normal", "Flex" ] %} +
+
+
+

Game Type

+
+
+ +
+
+ +
+
+
+
+

Our Team

+
+
+
+ {% for p in positions %} +
+

{{ p }}

+ {% include "partials/champ-selector-dropdown.html" %} +
+ {% endfor %} +
+
+
+ +
+
+
+
+

Enemy Team

+
+
+
+ {% for p in positions %} +
+

{{ p }}

+ +
+ {% endfor %} +
+
+
+ +
+
+
+
+

My Side

+
+
+
+
+ +
+
+
+ +
+
+

Result

+
+
+ +
+
+ +
+
+
+

KDA

+
+
+
+
+ +
+
+ +
+
+ +
+
+
+ +
+ +
+
+

Feedback

+ +
+ +
+

Feedback to Matchup

+ +
+
+
+
diff --git a/templates/partials/matchup-single-champ.html b/templates/partials/matchup-single-champ.html new file mode 100644 index 0000000..553279d --- /dev/null +++ b/templates/partials/matchup-single-champ.html @@ -0,0 +1,49 @@ +
+
+ +
+
+

Champion

+
+
+ {% include "partials/champ-selector-dropdown.html" %} +
+
+ +
+
+

Matchups

+
+
+ +
+
+ {% include "partials/champ-selector-dropdown.html" %} +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
+ +
+
diff --git a/templates/partials/navbar.html b/templates/partials/navbar.html new file mode 100644 index 0000000..15e32b5 --- /dev/null +++ b/templates/partials/navbar.html @@ -0,0 +1,49 @@ + + + diff --git a/templates/partials/paypal-button.html b/templates/partials/paypal-button.html new file mode 100644 index 0000000..51f6e25 --- /dev/null +++ b/templates/partials/paypal-button.html @@ -0,0 +1 @@ +
Type
diff --git a/templates/partials/self-analysis-obj.html b/templates/partials/self-analysis-obj.html new file mode 100644 index 0000000..37cc2aa --- /dev/null +++ b/templates/partials/self-analysis-obj.html @@ -0,0 +1,16 @@ +
+ {% 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 %} +
+

{{ t }}

+ + +
+ {% endfor %} +
diff --git a/templates/special/team_calendar.html b/templates/special/team_calendar.html new file mode 100644 index 0000000..b919fb6 --- /dev/null +++ b/templates/special/team_calendar.html @@ -0,0 +1,20 @@ + + + + + + + + Champ Selection + + + {% include 'default-includes.html' %} + + + + {% include 'navbar.html' %} +
+ + +
+ \ No newline at end of file diff --git a/templates/special/team_champselect.html b/templates/special/team_champselect.html new file mode 100644 index 0000000..af0a29f --- /dev/null +++ b/templates/special/team_champselect.html @@ -0,0 +1,59 @@ + + + + + + + + Champ Selection + + + {% include 'default-includes.html' %} + + + + + + {% include 'navbar.html' %} +
+ + +
+
+ {% for key in roles.keys() %} +
+
{{ key }}

+ + + +
+ {% endfor %} +
+ + +
+ {% for key in roles.keys() %} +
+ {% for champ in roles[key].champions %} +
+
AF_PLACEHDR
+
{{ champ }}
+ +

+ {% endfor %} +
+ {% endfor %} +
+ + + +
+ \ No newline at end of file diff --git a/templates/special/team_composition.html b/templates/special/team_composition.html new file mode 100644 index 0000000..5562438 --- /dev/null +++ b/templates/special/team_composition.html @@ -0,0 +1,50 @@ + + + + + + + + Champ Selection + + + {% include 'default-includes.html' %} + + + + {% include 'navbar.html' %} + + {% for x in range(1, 10) %} +
+ + +
+
+ {% for key in roles.keys() %} +
+ + +
+ {% endfor %} +
+ + +
+ {% for key in roles.keys() %} +
+ {% for champ in roles[key].champions %} +

{{ champ }}

+ {% endfor %} +
+ {% endfor %} +
+ + + +
+ {% endfor %} + \ No newline at end of file diff --git a/templates/special/team_composition_overview.html b/templates/special/team_composition_overview.html new file mode 100644 index 0000000..8d4adcc --- /dev/null +++ b/templates/special/team_composition_overview.html @@ -0,0 +1,43 @@ + + + + + + + + Nemesis Coaching + + + {% include 'default-includes.html' %} + + + + + {% include 'navbar.html' %} +
+ + +
+
+ {% for comp in teamComps %} +
+
+
+

{{ comp["name"] }}

+

{{ comp["Description"] }}

+

Open »

+

+
+
+
+ {% endfor %} +
+
+
+ + {% include 'footer.html' %} + +
+ + + \ No newline at end of file diff --git a/templates/special/team_composition_single.html b/templates/special/team_composition_single.html new file mode 100644 index 0000000..09ce61e --- /dev/null +++ b/templates/special/team_composition_single.html @@ -0,0 +1,52 @@ + + + + + + + + Champ Selection + + + {% include 'default-includes.html' %} + + + + + + + {% include 'navbar.html' %} + +
+ + +
+
+ {% for key in roles.keys() %} +
+
{{ key }}

+ + +
+ {% endfor %} +
+ + +
+ {% for key in roles.keys() %} +
+ {% for champ in roles[key].champions %} +

{{ champ }}

+ {% endfor %} +
+ {% endfor %} +
+ + + +
+ \ No newline at end of file diff --git a/templates/special/team_matchhistory.html b/templates/special/team_matchhistory.html new file mode 100644 index 0000000..19661de --- /dev/null +++ b/templates/special/team_matchhistory.html @@ -0,0 +1,21 @@ + + + + + + + + Champ Selection + + + {% include 'default-includes.html' %} + + + + {% include 'navbar.html' %} +
+ + +
+ \ No newline at end of file diff --git a/templates/standard/about.html b/templates/standard/about.html new file mode 100644 index 0000000..76476d8 --- /dev/null +++ b/templates/standard/about.html @@ -0,0 +1,38 @@ + + + + + + + + + {% include 'partials/header.html' %} + + + + + + + {% include 'partials/navbar.html' %} +
+
+
+

Nemesis

+

Coach

+

+ 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 +

+
+
+ +
+
+
+ {% include "partials/footer.html" %} + + diff --git a/templates/standard/contact.html b/templates/standard/contact.html new file mode 100644 index 0000000..5b62d9d --- /dev/null +++ b/templates/standard/contact.html @@ -0,0 +1,85 @@ + + + + + + + + + {% include 'partials/header.html' %} + + + + + + + {% include 'partials/navbar.html' %} +
+
+

+ {{ conf['CONTACT_HEADLINE'] }} +

+

+
+
+
+ +
+
+
+ +
+
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+ +
+
+
+
+
+ Absenden +
+
+
+ +
+
    +
  • +

    Wir freuen uns auf Ihre Nachricht!

    + {% if conf['CONTACT_EMAIL'] %} +
    +

    Natürlich können Sie uns auch direkt per Mail kontaktieren.


    + {{ conf['CONTACT_EMAIL'] }} + {% endif %} +
  • +
+
+
+
+
+ {% include "partials/footer.html" %} + + \ No newline at end of file diff --git a/templates/standard/impressum.html b/templates/standard/impressum.html new file mode 100644 index 0000000..9096164 --- /dev/null +++ b/templates/standard/impressum.html @@ -0,0 +1,61 @@ + + + + + + + + Nemesis Coaching + {% include 'partials/header.html' %} + + + + + {% include 'partials/navbar.html' %} + +
+
+
+

Impressum

+

{{ config["company-name"] }}

+
+
+

Adresse
{{ config["company-street"] }}
{{ config["company-postal"] }}

+

Telefon
{{ config["company-phone"] }}

+
+
+

E-Mail
{{ config["company-mail"] }}

+

Verantwortlicher
{{ config["company-responsible"] }}

+ {{ config["company-freetext"] }} +
+
+ Other Legal BS +
+
+
+

Haftung für Inhalte

+

+ 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. +

+

Haftung für Links

+

+ 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. +

+ +

Datenschutz

+

+ 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. +

+

Externe Inhalte

+

+ 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. +

+
+ +
+
+ + {% include 'partials/footer.html' %} + \ No newline at end of file diff --git a/templates/standard/index.html b/templates/standard/index.html new file mode 100644 index 0000000..b839657 --- /dev/null +++ b/templates/standard/index.html @@ -0,0 +1,88 @@ + + + + + + + + Nemesis Coaching + + + {% include 'partials/header.html' %} + + + + + {% include 'partials/navbar.html' %} + +
+ +
+
+
+
+

{{ config["main-title"] }}

+

{{ config["subtitle"] }}

+
+
+
+
+ + +
+
+ {% for card in services %} +
+
+ +
+

{{ card["headline"] }}

+

{{ card["description"] }}

+ +

+
+
+
+ {% endfor %} +
+
+
+ + + {% for prices in pricesSections %} +
+
+
+ {% for card in prices %} +
+
+
+
{{ card["header"] }}
+
{{ card["price"] }}€{{ card["period"] }}
+
+
    + {% for feature in card["features"] %} +
  • {{ feature }}
  • + {% endfor %} + {% for feature in card["disabledFeatures"] %} +
  • {{ feature }}
  • + {% endfor %} +
+ Contact Me +
+
+
+ {% endfor %} +
+
+
+ {% endfor %} +
+
+ + {% include 'partials/footer.html' %} + + + diff --git a/templates/standard/login.html b/templates/standard/login.html new file mode 100644 index 0000000..08b344d --- /dev/null +++ b/templates/standard/login.html @@ -0,0 +1,56 @@ + + + + + + + + + Nemesis Coaching + {% include 'partials/header.html' %} + + + + + {% include 'partials/navbar.html' %} +
+
+
+
+

Sign In

+
+
+
+
+
+ +
+ + +
+
+
+ +
+ +
+
+ +
+
+
+ +
+
+
+ {% include "partials/footer.html" %} + + + \ No newline at end of file diff --git a/templates/standard/shop.html b/templates/standard/shop.html new file mode 100644 index 0000000..4a5b6a7 --- /dev/null +++ b/templates/standard/shop.html @@ -0,0 +1,80 @@ + + + + + + + + + {% include 'partials/header.html' %} + + + + + + + {% include 'partials/navbar.html' %} +
+
+
+
+ +
+
+
+
+
+

VoD Review

+

Review eines deiner Spiele als VoD

+

+ Nemesis is bad Nemesis is bad + Nemesis is bad Nemesis is bad + Nemesis is bad Nemesis is bad +

+
+
+
+
+ {% include "partials/paypal-button.html" %} +
+
+
+
+ + + +
+
+
+ +
+
+
+
+
+

Live Coaching

+

Live im discord blablabla

+

+ Nemesis is bad Nemesis is bad + Nemesis is bad Nemesis is bad + Nemesis is bad Nemesis is bad +

+
+
+
+
+ {% include "partials/paypal-button.html" %} +
+
+
+
+ + + +
+
+ {% include "partials/footer.html" %} + + diff --git a/templates/standard/thanks.html b/templates/standard/thanks.html new file mode 100644 index 0000000..b148841 --- /dev/null +++ b/templates/standard/thanks.html @@ -0,0 +1,31 @@ + + + + + + + + Nemesis Coaching + + + {% include 'partials/header.html' %} + + + + + {% include 'partials/navbar.html' %} +
+

Thank You!

+

Your contact request has been send! We will answer you as soon as possible.

+
+

+ Still having trouble? Contact us +

+

+ Return to Homepage +

+
+ {% include 'partials/footer.html' %} + + + \ No newline at end of file diff --git a/templates/standard/user_dashboard.html b/templates/standard/user_dashboard.html new file mode 100644 index 0000000..175b1b2 --- /dev/null +++ b/templates/standard/user_dashboard.html @@ -0,0 +1,77 @@ + + + + + + + + Nemesis Coaching + + + {% include 'partials/header.html' %} + + + + + {% include 'partials/navbar.html' %} +
+ + +
+ {% if currentUser.single %} +
+
+

Accounts:

+ {% for acc in currentUser.accounts %} + {{ acc }}
+ {% endfor %} +
+
+

Champions:

+ {% for c in currentUser.selectedChampions %} +
{{ c }}
+ {% endfor %} +
+
+
+
+
+
+ +
+
+ +
+
+ {% endif %} + +
+ {% for table in currentUser.allowedFeatures() %} +
+
+ +
+

{{ table["title"] }}

+

{{ table["description"] }}

+ {% if table.get("href") %} +

Open »

+ {% else %} +

Open »

+ {% endif %} +

+
+
+
+ {% endfor %} +
+
+
+ + {% include 'partials/footer.html' %} +
+ + + diff --git a/usermanagement.py b/usermanagement.py new file mode 100644 index 0000000..ad332c1 --- /dev/null +++ b/usermanagement.py @@ -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"])