feat(sports) multiple sports

main
Valentin 2024-06-06 16:52:40 +02:00
parent 55d2e5acd2
commit 132758e470
18 changed files with 145 additions and 79 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

198
app.py
View File

@ -3,7 +3,7 @@ from flask_oauthlib.client import OAuth, OAuthRemoteApp
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from datetime import datetime, timedelta from datetime import datetime, timedelta
from icalendar import Calendar, Event from icalendar import Calendar, Event
import os, requests, pytz, hashlib import os, requests, pytz, hashlib, random
from dotenv import load_dotenv from dotenv import load_dotenv
load_dotenv() load_dotenv()
@ -30,37 +30,61 @@ google = oauth.remote_app(
authorize_url='https://accounts.google.com/o/oauth2/auth', authorize_url='https://accounts.google.com/o/oauth2/auth',
) )
teams_dict = { teams_dict = {
1: "Lakers", 1: {
2: "Heat", "name": "NBA",
3: "Warriors", "teams": {
4: "Celtics", 1: "Lakers",
5: "Spurs", 2: "Heat",
6: "Knicks", 3: "Warriors",
7: "Pistons", 4: "Celtics",
8: "Magic", 5: "Spurs",
9: "Suns", 6: "Knicks",
10: "Pacers", 7: "Pistons",
11: "Jazz", 8: "Magic",
12: "Trail Blazers", 9: "Suns",
13: "Raptors", 10: "Pacers",
14: "Mavericks", 11: "Jazz",
15: "Bucks", 12: "Trail Blazers",
16: "Thunder", 13: "Raptors",
17: "Bulls", 14: "Mavericks",
18: "Pelicans", 15: "Bucks",
19: "Rockets", 16: "Thunder",
20: "Kings", 17: "Bulls",
21: "Clippers", 18: "Pelicans",
22: "Cavaliers", 19: "Rockets",
23: "Hawks", 20: "Kings",
24: "Grizzlies", 21: "Clippers",
25: "Nuggets", 22: "Cavaliers",
26: "Hornets", 23: "Hawks",
27: "76ers", 24: "Grizzlies",
28: "Wizards", 25: "Nuggets",
29: "Timberwolves", 26: "Hornets",
30: "Nets" 27: "76ers",
28: "Wizards",
29: "Timberwolves",
30: "Nets"
}
},
2: {
"name": "WNBA",
"teams": {
1: "Aces",
2: "Dream",
3: "Sun",
4: "Wings",
5: "Sky",
6: "Sparks",
7: "Storm",
8: "Lynx",
9: "Mercury",
10: "Mystics",
11: "Fever",
12: "Liberty"
}
}
} }
class User(db.Model): class User(db.Model):
@ -69,44 +93,76 @@ class User(db.Model):
class Team(db.Model): class Team(db.Model):
id = db.Column(db.Integer, primary_key=True) id = db.Column(db.Integer, primary_key=True)
idTeam = db.Column(db.Integer, unique=True) idTeam = db.Column(db.Integer)
idUser = db.Column(db.Integer, db.ForeignKey('user.id')) idUser = db.Column(db.Integer, db.ForeignKey('user.id'))
idSport = db.Column(db.Integer)
def addTeam(idUser, idTeam): def addTeam(idUser, idTeam, idSport):
team = Team(idTeam=idTeam, idUser=idUser) team = Team(idTeam=idTeam, idUser=idUser, idSport=idSport, id=random.randint(10000000, 99999999))
db.session.add(team) db.session.add(team)
db.session.commit() db.session.commit()
def deleteTeam(idTeam): def deleteTeam(idTeam, idSport):
team = Team.query.filter_by(idTeam=idTeam).first() team = Team.query.filter_by(idTeam=idTeam,idSport=idSport).first()
db.session.delete(team) db.session.delete(team)
db.session.commit() db.session.commit()
def getTeamName(idTeam): def getTeamName(idTeam, idSport):
return teams_dict[idTeam] return teams_dict[idSport]["teams"][idTeam]
def getUserTeams(idUser): def getUserTeams(idUser,idSport):
teams = Team.query.filter_by(idUser=idUser).all() teams = Team.query.filter_by(idUser=idUser,idSport=idSport).all()
return teams return teams
def assignTeam(idUser, idTeam): def assignTeam(idUser, idTeam,idSport):
team = Team.query.filter_by(idTeam=idTeam).first() team = Team.query.filter_by(idTeam=idTeam,idSport=idSport).first()
if team is None: if team is None:
addTeam(idUser, idTeam) addTeam(idUser, idTeam, idSport)
else: else:
team.idUser = idUser team.idUser = idUser
db.session.commit() db.session.commit()
def get_team_logo(idTeam):
return f"static/logo/team_nba/team_{idTeam}.png"
def getSchedules(): def getWNBATeamMatches(idTeam):
response = requests.get("https://cdn.nba.com/static/json/staticData/scheduleLeagueV2.json") result = []
matches_info = getWNBASchedules()
for i in matches_info:
if (i['WNBA_hometeamName'] == getTeamName(idTeam) or i['WNBA_awayteamName'] == getTeamName(idTeam)):
result.append(i)
return result
def getTeamLogo(idTeam, idSport):
match idSport:
case 1:
return f"static/logo/team_nba/team_{idTeam}.png"
case 2:
return f"static/logo/team_wnba/team_{idTeam}.png"
case _:
return False
def getOtherTeams(uid, idSport):
result = []
for i in list(teams_dict[idSport]["teams"].keys()):
if (Team.query.filter_by(idUser=uid, idTeam=i).first() is None):
result.append(i)
return result
def getSchedules(idSport):
match idSport:
case 1:
response = requests.get("https://cdn.nba.com/static/json/staticData/scheduleLeagueV2.json")
case 2:
response = requests.get("https://cdn.wnba.com/static/json/staticData/scheduleLeagueV2.json")
matches_info = [] matches_info = []
for game_date in response.json()['leagueSchedule']['gameDates']: for game_date in response.json()['leagueSchedule']['gameDates']:
for game in game_date['games']: for game in game_date['games']:
match_info = { match_info = {
'sportName': teams_dict[idSport]["name"],
'gameDateTimeUTC': game['gameDateTimeUTC'], 'gameDateTimeUTC': game['gameDateTimeUTC'],
'weekNumber': game['weekNumber'], 'weekNumber': game['weekNumber'],
'arenaName': game['arenaName'], 'arenaName': game['arenaName'],
@ -123,21 +179,23 @@ def getSchedules():
matches_info.append(match_info) matches_info.append(match_info)
return matches_info return matches_info
def getTeamMatches(idTeam): def getTeamMatches(idTeam, idSport):
result = [] result = []
matches_info = getSchedules() matches_info = getSchedules(idSport)
for i in matches_info: for i in matches_info:
if (i['hometeamName'] == getTeamName(idTeam) or i['awayteamName'] == getTeamName(idTeam)): if (i['hometeamName'] == getTeamName(idTeam, idSport) or i['awayteamName'] == getTeamName(idTeam, idSport)):
result.append(i) result.append(i)
return result return result
def getUserMatches(idUser): def getUserMatches(idUser):
result = [] result = []
teams = getUserTeams(idUser) for i in range(1,len(teams_dict) + 1):
for i in teams: teams = getUserTeams(idUser, i)
result += getTeamMatches(i.idTeam) for j in teams:
result += getTeamMatches(j.idTeam, i)
return result return result
def convert_to_datetime(date_str): def convert_to_datetime(date_str):
return datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=pytz.UTC) return datetime.strptime(date_str, '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=pytz.UTC)
@ -147,7 +205,7 @@ def generate_ical(events):
event = Event() event = Event()
event.add('summary', f"{event_data['hometeamTricode']} vs {event_data['awayteamTricode']} 🏀") event.add('summary', f"{event_data['hometeamTricode']} vs {event_data['awayteamTricode']} 🏀")
event.add('location', f"🏟 {event_data['arenaName']}, {event_data['arenaCity']}") event.add('location', f"🏟 {event_data['arenaName']}, {event_data['arenaCity']}")
event.add("description", f"🎖Scores: \n{event_data['hometeamName']} {event_data['hometeamScore']} - {event_data['awayteamScore']} {event_data['awayteamName']}") event.add("description", f"Sport: {event_data['sportName']}\n🎖Scores: \n{event_data['hometeamName']} {event_data['hometeamScore']} - {event_data['awayteamScore']} {event_data['awayteamName']}")
event.add("url", event_data['url']) event.add("url", event_data['url'])
event.add('dtstart', datetime.strptime(event_data['gameDateTimeUTC'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=pytz.UTC)) event.add('dtstart', datetime.strptime(event_data['gameDateTimeUTC'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=pytz.UTC))
event.add('dtend', datetime.strptime(event_data['gameDateTimeUTC'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=pytz.UTC) + timedelta(hours=3)) event.add('dtend', datetime.strptime(event_data['gameDateTimeUTC'], '%Y-%m-%dT%H:%M:%SZ').replace(tzinfo=pytz.UTC) + timedelta(hours=3))
@ -163,24 +221,21 @@ def index():
if (not(me.data['email'])): if (not(me.data['email'])):
return redirect("/logout", code=302) return redirect("/logout", code=302)
user = User.query.filter_by(email=me.data['email']).first() user = User.query.filter_by(email=me.data['email']).first()
if user is None: if user is None:
user = User(email=me.data['email']) user = User(email=me.data['email'])
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
sportId = int(request.args.get('sport', 1))
otherTeams = [] user_teams = getUserTeams(user.id, sportId)
other_teams = getOtherTeams(user.id, sportId)
for i in list(teams_dict.keys()): return render_template('dashboard.html', userTeams=user_teams, otherTeams=other_teams, getTeamName=getTeamName, getTeamLogo=getTeamLogo, userId=user.id, sportId=sportId)
if (Team.query.filter_by(idUser=user.id, idTeam=i).first() is None):
otherTeams.append(i)
return render_template('dashboard.html', userTeams=getUserTeams(user.id), otherTeams=otherTeams, getTeamName=getTeamName, getTeamLogo=get_team_logo, userId=user.id)
return render_template('index.html') return render_template('index.html')
@app.route('/add/<int:idTeam>') @app.route('/add/<int:idSport>/<int:idTeam>')
def addTeamRoute(idTeam): def addTeamRoute(idSport,idTeam):
if 'google_token' in session: if 'google_token' in session:
me = google.get('userinfo') me = google.get('userinfo')
user = User.query.filter_by(email=me.data['email']).first() user = User.query.filter_by(email=me.data['email']).first()
@ -189,12 +244,12 @@ def addTeamRoute(idTeam):
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
assignTeam(user.id, idTeam) assignTeam(user.id, idTeam, idSport)
return redirect("/", code=302) return redirect("/?sport=" + str(idSport), code=302)
return redirect("/login", code=302) return redirect("/login", code=302)
@app.route('/del/<int:idTeam>') @app.route('/del/<int:idSport>/<int:idTeam>')
def delTeamRoute(idTeam): def delTeamRoute(idSport, idTeam):
if 'google_token' in session: if 'google_token' in session:
me = google.get('userinfo') me = google.get('userinfo')
user = User.query.filter_by(email=me.data['email']).first() user = User.query.filter_by(email=me.data['email']).first()
@ -203,8 +258,8 @@ def delTeamRoute(idTeam):
db.session.add(user) db.session.add(user)
db.session.commit() db.session.commit()
deleteTeam(idTeam) deleteTeam(idTeam, idSport)
return redirect("/", code=302) return redirect("/?sport=" + str(idSport), code=302)
return redirect("/login", code=302) return redirect("/login", code=302)
@ -235,7 +290,7 @@ def generate_ical_feed(user_id):
def google_redirect(): def google_redirect():
if 'instagram' in request.headers.get('User-Agent').lower() or 'facebook' in request.headers.get('User-Agent').lower(): if 'instagram' in request.headers.get('User-Agent').lower() or 'facebook' in request.headers.get('User-Agent').lower():
return render_template('open_in_browser.html') return render_template('open_in_browser.html')
return google.authorize(callback=url_for('authorized', _external=True, _scheme='https')) return google.authorize(callback=url_for('authorized', _external=True, _scheme='http'))
@app.route('/logout') @app.route('/logout')
def logout(): def logout():
@ -260,5 +315,6 @@ def get_google_oauth_token():
if __name__ == '__main__': if __name__ == '__main__':
with app.app_context(): with app.app_context():
db.create_all() db.create_all()
app.run(debug=True, port=8000, host='127.0.0.1') app.run(debug=True, port=8000, host='127.0.0.1')

BIN
static/.DS_Store vendored

Binary file not shown.

BIN
static/logo/.DS_Store vendored Normal file

Binary file not shown.

BIN
static/logo/team_wnba/.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

View File

@ -38,16 +38,27 @@
</header> </header>
<section> <section>
<div class="container py-4 py-xl-5"> <div class="container py-4 py-xl-5">
<form id="sportForm" method="get" action="/">
<div class="row mb-4">
<div class="col-12">
<label for="sportSelect" class="form-label">Sport:</label>
<select id="sportSelect" name="sport" class="form-select" onchange="document.getElementById('sportForm').submit();">
<option value="1" {% if sportId == 1 %}selected{% endif %}>NBA</option>
<option value="2" {% if sportId == 2 %}selected{% endif %}>WNBA</option>
</select>
</div>
</div>
</form>
<div class="row gy-4 row-cols-1 row-cols-md-2 row-cols-lg-3"> <div class="row gy-4 row-cols-1 row-cols-md-2 row-cols-lg-3">
{% for team in userTeams %} {% for team in userTeams %}
<div class="col"> <div class="col">
<div class="card border-light border-1 d-flex justify-content-center p-4"> <div class="card border-light border-1 d-flex justify-content-center p-4">
<div class="card-body"> <div class="card-body">
<div class="d-flex justify-content-center align-items-center justify-content-md-start"><img src="{{ getTeamLogo(team.idTeam) }}" style="width: 100px;"> <div class="d-flex justify-content-center align-items-center justify-content-md-start"><img src="{{ getTeamLogo(team.idTeam, sportId) }}" style="width: 100px;">
<h5 class="fw-bold mb-0 ms-2">{{ getTeamName(team.idTeam) }}</h5> <h5 class="fw-bold mb-0 ms-2">{{ getTeamName(team.idTeam, sportId) }}</h5>
</div> </div>
<div></div> <div></div>
</div><a class="btn btn-primary" role="button" href="/del/{{ team.idTeam }}">Remove from calendar</a> </div><a class="btn btn-primary" role="button" href="/del/{{ sportId }}/{{ team.idTeam }}">Remove from calendar</a>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
@ -55,11 +66,11 @@
<div class="col"> <div class="col">
<div class="card border-light border-1 d-flex justify-content-center p-4"> <div class="card border-light border-1 d-flex justify-content-center p-4">
<div class="card-body"> <div class="card-body">
<div class="d-flex justify-content-center align-items-center justify-content-md-start"><img src="{{ getTeamLogo(team) }}" style="width: 100px;"> <div class="d-flex justify-content-center align-items-center justify-content-md-start"><img src="{{ getTeamLogo(team, sportId) }}" style="width: 100px;">
<h5 class="fw-bold mb-0 ms-2">{{ getTeamName(team) }}</h5> <h5 class="fw-bold mb-0 ms-2">{{ getTeamName(team, sportId) }}</h5>
</div> </div>
<div></div> <div></div>
</div><a class="btn btn-warning" role="button" href="/add/{{ team }}">Add to calendar</a> </div><a class="btn btn-warning" role="button" href="/add/{{ sportId }}/{{ team }}">Add to calendar</a>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
@ -83,7 +94,6 @@
<p class="mb-0">Copyright © 2024 So My Calendar</p> <p class="mb-0">Copyright © 2024 So My Calendar</p>
<ul class="list-inline mb-0"> <ul class="list-inline mb-0">
<li class="list-inline-item"> <li class="list-inline-item">
<a href="https://twitter.com/SoMy76ers" target="_blank"> <a href="https://twitter.com/SoMy76ers" target="_blank">
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-twitter"> <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="currentColor" viewBox="0 0 16 16" class="bi bi-twitter">
<path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"></path> <path d="M5.026 15c6.038 0 9.341-5.003 9.341-9.334 0-.14 0-.282-.006-.422A6.685 6.685 0 0 0 16 3.542a6.658 6.658 0 0 1-1.889.518 3.301 3.301 0 0 0 1.447-1.817 6.533 6.533 0 0 1-2.087.793A3.286 3.286 0 0 0 7.875 6.03a9.325 9.325 0 0 1-6.767-3.429 3.289 3.289 0 0 0 1.018 4.382A3.323 3.323 0 0 1 .64 6.575v.045a3.288 3.288 0 0 0 2.632 3.218 3.203 3.203 0 0 1-.865.115 3.23 3.23 0 0 1-.614-.057 3.283 3.283 0 0 0 3.067 2.277A6.588 6.588 0 0 1 .78 13.58a6.32 6.32 0 0 1-.78-.045A9.344 9.344 0 0 0 5.026 15z"></path>
@ -105,4 +115,4 @@
<script src="static/js/script.min.js"></script> <script src="static/js/script.min.js"></script>
</body> </body>
</html> </html>