Fix up voting and start adding club logos
This commit is contained in:
parent
7f0d171e21
commit
131f17947c
60
motm_app/POSTGRESQL_SETUP.md
Normal file
60
motm_app/POSTGRESQL_SETUP.md
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
# PostgreSQL Database Setup
|
||||||
|
|
||||||
|
This application is now configured to use PostgreSQL as the default database instead of SQLite.
|
||||||
|
|
||||||
|
## Database Configuration
|
||||||
|
|
||||||
|
The application connects to a PostgreSQL database with the following settings:
|
||||||
|
- **Host**: icarus.ipa.champion
|
||||||
|
- **Port**: 5432
|
||||||
|
- **Database**: motm
|
||||||
|
- **Username**: motm_user
|
||||||
|
- **Password**: q7y7f7Lv*sODJZ2wGiv0Wq5a
|
||||||
|
|
||||||
|
## Running the Application
|
||||||
|
|
||||||
|
### Linux/macOS
|
||||||
|
```bash
|
||||||
|
./run_motm.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Windows
|
||||||
|
```cmd
|
||||||
|
run_motm.bat
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual Setup
|
||||||
|
If you need to run the application manually, set these environment variables:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
export DATABASE_TYPE=postgresql
|
||||||
|
export POSTGRES_HOST=icarus.ipa.champion
|
||||||
|
export POSTGRES_PORT=5432
|
||||||
|
export POSTGRES_DATABASE=motm
|
||||||
|
export POSTGRES_USER=motm_user
|
||||||
|
export POSTGRES_PASSWORD='q7y7f7Lv*sODJZ2wGiv0Wq5a'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Database Status
|
||||||
|
|
||||||
|
The PostgreSQL database contains the following data:
|
||||||
|
- **Players**: 3 players in the `_hkfc_players` table
|
||||||
|
- **Match Squad**: 3 players currently selected for the match squad
|
||||||
|
- **Admin Settings**: Configured with next match details
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
If you encounter issues:
|
||||||
|
|
||||||
|
1. **Connection Failed**: Check if the PostgreSQL server is accessible
|
||||||
|
2. **Empty Data**: The database contains sample data - if you see empty tables, check the connection
|
||||||
|
3. **Permission Errors**: Ensure the `motm_user` has proper database permissions
|
||||||
|
|
||||||
|
## Testing Database Connection
|
||||||
|
|
||||||
|
Run the test script to verify everything is working:
|
||||||
|
```bash
|
||||||
|
python3 test_match_squad.py
|
||||||
|
```
|
||||||
|
|
||||||
|
This will test the database connection and display current data.
|
||||||
@ -1,6 +1,6 @@
|
|||||||
# encoding=utf-8
|
# encoding=utf-8
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
from wtforms import BooleanField, StringField, PasswordField, IntegerField, TextAreaField, SubmitField, RadioField, SelectField, DateField
|
from wtforms import BooleanField, StringField, PasswordField, IntegerField, TextAreaField, SubmitField, RadioField, SelectField, DateField, FieldList
|
||||||
from wtforms_components import read_only
|
from wtforms_components import read_only
|
||||||
from wtforms import validators, ValidationError
|
from wtforms import validators, ValidationError
|
||||||
from wtforms.validators import InputRequired, Email, Length
|
from wtforms.validators import InputRequired, Email, Length
|
||||||
@ -106,10 +106,19 @@ class ClubForm(FlaskForm):
|
|||||||
class TeamForm(FlaskForm):
|
class TeamForm(FlaskForm):
|
||||||
"""Form for adding/editing teams."""
|
"""Form for adding/editing teams."""
|
||||||
|
|
||||||
club = StringField('Club', validators=[InputRequired()])
|
club = SelectField('Club', validators=[InputRequired()], choices=[], coerce=str)
|
||||||
team = StringField('Team', validators=[InputRequired()])
|
team = StringField('Team', validators=[InputRequired()])
|
||||||
display_name = StringField('Display Name', validators=[InputRequired()])
|
display_name = StringField('Display Name', validators=[InputRequired()])
|
||||||
league = StringField('League', validators=[InputRequired()])
|
league = SelectField('League', validators=[InputRequired()], choices=[
|
||||||
|
('', 'Select League'),
|
||||||
|
('Premier Division', 'Premier Division'),
|
||||||
|
('1st Division', '1st Division'),
|
||||||
|
('2nd Division', '2nd Division'),
|
||||||
|
('3rd Division', '3rd Division'),
|
||||||
|
('4th Division', '4th Division'),
|
||||||
|
('5th Division', '5th Division'),
|
||||||
|
('6th Division', '6th Division')
|
||||||
|
], coerce=str)
|
||||||
|
|
||||||
# Action buttons
|
# Action buttons
|
||||||
save_team = SubmitField('Save Team')
|
save_team = SubmitField('Save Team')
|
||||||
@ -126,3 +135,16 @@ class DataImportForm(FlaskForm):
|
|||||||
# Action buttons
|
# Action buttons
|
||||||
import_data = SubmitField('Import Data')
|
import_data = SubmitField('Import Data')
|
||||||
cancel = SubmitField('Cancel')
|
cancel = SubmitField('Cancel')
|
||||||
|
|
||||||
|
|
||||||
|
class ClubSelectionForm(FlaskForm):
|
||||||
|
"""Form for selecting which clubs to import."""
|
||||||
|
|
||||||
|
# This will be populated dynamically with club checkboxes
|
||||||
|
selected_clubs = FieldList(BooleanField('Select Club'), min_entries=0)
|
||||||
|
|
||||||
|
# Action buttons
|
||||||
|
import_selected = SubmitField('Import Selected Clubs')
|
||||||
|
select_all = SubmitField('Select All')
|
||||||
|
select_none = SubmitField('Select None')
|
||||||
|
cancel = SubmitField('Cancel')
|
||||||
|
|||||||
136
motm_app/main.py
136
motm_app/main.py
@ -27,7 +27,7 @@ from flask_basicauth import BasicAuth
|
|||||||
from wtforms import StringField, PasswordField, BooleanField
|
from wtforms import StringField, PasswordField, BooleanField
|
||||||
from wtforms import DateField
|
from wtforms import DateField
|
||||||
from wtforms.validators import InputRequired, Email, Length
|
from wtforms.validators import InputRequired, Email, Length
|
||||||
from forms import motmForm, adminSettingsForm2, goalsAssistsForm, DatabaseSetupForm, PlayerForm, ClubForm, TeamForm, DataImportForm
|
from forms import motmForm, adminSettingsForm2, goalsAssistsForm, DatabaseSetupForm, PlayerForm, ClubForm, TeamForm, DataImportForm, ClubSelectionForm
|
||||||
from db_config import sql_write, sql_write_static, sql_read, sql_read_static
|
from db_config import sql_write, sql_write_static, sql_read, sql_read_static
|
||||||
from sqlalchemy import text
|
from sqlalchemy import text
|
||||||
from tables import matchSquadTable
|
from tables import matchSquadTable
|
||||||
@ -75,6 +75,13 @@ def motm_vote(randomUrlSuffix):
|
|||||||
currMotM = nextInfo[0]['currmotm']
|
currMotM = nextInfo[0]['currmotm']
|
||||||
currDotD = nextInfo[0]['currdotd']
|
currDotD = nextInfo[0]['currdotd']
|
||||||
oppo = nextTeam
|
oppo = nextTeam
|
||||||
|
|
||||||
|
# Get opponent club logo from clubs table
|
||||||
|
if nextClub:
|
||||||
|
sql_club_logo = text("SELECT logo_url FROM clubs WHERE hockey_club = :club_name")
|
||||||
|
club_logo_result = sql_read(sql_club_logo, {'club_name': nextClub})
|
||||||
|
if club_logo_result and club_logo_result[0]['logo_url']:
|
||||||
|
oppoLogo = club_logo_result[0]['logo_url']
|
||||||
# Get match date from admin settings
|
# Get match date from admin settings
|
||||||
if nextInfo and nextInfo[0]['nextdate']:
|
if nextInfo and nextInfo[0]['nextdate']:
|
||||||
nextDate = nextInfo[0]['nextdate']
|
nextDate = nextInfo[0]['nextdate']
|
||||||
@ -123,14 +130,14 @@ def motm_vote(randomUrlSuffix):
|
|||||||
@app.route('/motm/comments', methods=['GET', 'POST'])
|
@app.route('/motm/comments', methods=['GET', 'POST'])
|
||||||
def match_comments():
|
def match_comments():
|
||||||
"""Display and allow adding match comments"""
|
"""Display and allow adding match comments"""
|
||||||
sql = text("SELECT nextClub, nextTeam, nextDate, oppoLogo, hkfcLogo FROM motmadminsettings")
|
sql = text("SELECT nextClub, nextTeam, nextdate, oppoLogo, hkfcLogo FROM motmadminsettings")
|
||||||
row = sql_read_static(sql)
|
row = sql_read_static(sql)
|
||||||
if not row:
|
if not row:
|
||||||
return render_template('error.html', message="Database not initialized. Please go to Database Setup to initialize the database.")
|
return render_template('error.html', message="Database not initialized. Please go to Database Setup to initialize the database.")
|
||||||
|
|
||||||
_oppo = row[0]['nextClub']
|
_oppo = row[0]['nextClub']
|
||||||
commentDate = row[0]['nextDate'].strftime('%Y-%m-%d')
|
commentDate = row[0]['nextdate'].strftime('%Y-%m-%d')
|
||||||
_matchDate = row[0]['nextDate'].strftime('%Y_%m_%d')
|
_matchDate = row[0]['nextdate'].strftime('%Y_%m_%d')
|
||||||
hkfcLogo = row[0]['hkfcLogo']
|
hkfcLogo = row[0]['hkfcLogo']
|
||||||
oppoLogo = row[0]['oppoLogo']
|
oppoLogo = row[0]['oppoLogo']
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@ -257,7 +264,7 @@ def motm_admin():
|
|||||||
|
|
||||||
# Only update currMotM and currDotD if they were provided
|
# Only update currMotM and currDotD if they were provided
|
||||||
if _currMotM and _currMotM != '0' and _currDotD and _currDotD != '0':
|
if _currMotM and _currMotM != '0' and _currDotD and _currDotD != '0':
|
||||||
sql = text("UPDATE motmadminsettings SET nextDate = :next_date, nextClub = :next_club, nextTeam = :next_team, currMotM = :curr_motm, currDotD = :curr_dotd")
|
sql = text("UPDATE motmadminsettings SET nextdate = :next_date, nextClub = :next_club, nextTeam = :next_team, currMotM = :curr_motm, currDotD = :curr_dotd")
|
||||||
sql_write_static(sql, {
|
sql_write_static(sql, {
|
||||||
'next_date': _nextMatchDate,
|
'next_date': _nextMatchDate,
|
||||||
'next_club': _nextClub,
|
'next_club': _nextClub,
|
||||||
@ -267,7 +274,7 @@ def motm_admin():
|
|||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
# Don't update currMotM and currDotD if not provided
|
# Don't update currMotM and currDotD if not provided
|
||||||
sql = text("UPDATE motmadminsettings SET nextDate = :next_date, nextClub = :next_club, nextTeam = :next_team")
|
sql = text("UPDATE motmadminsettings SET nextdate = :next_date, nextClub = :next_club, nextTeam = :next_team")
|
||||||
sql_write_static(sql, {
|
sql_write_static(sql, {
|
||||||
'next_date': _nextMatchDate,
|
'next_date': _nextMatchDate,
|
||||||
'next_club': _nextClub,
|
'next_club': _nextClub,
|
||||||
@ -290,7 +297,7 @@ def motm_admin():
|
|||||||
print(urlSuffix)
|
print(urlSuffix)
|
||||||
sql3 = text("UPDATE motmadminsettings SET motmurlsuffix = :url_suffix WHERE userid = 'admin'")
|
sql3 = text("UPDATE motmadminsettings SET motmurlsuffix = :url_suffix WHERE userid = 'admin'")
|
||||||
sql_write_static(sql3, {'url_suffix': urlSuffix})
|
sql_write_static(sql3, {'url_suffix': urlSuffix})
|
||||||
flash('MotM URL https://hockey.ervine.cloud/motm/'+urlSuffix)
|
flash('MotM URL https://motm.ervine.cloud/motm/'+urlSuffix)
|
||||||
elif form.activateButton.data:
|
elif form.activateButton.data:
|
||||||
# Generate a fixture number based on the date
|
# Generate a fixture number based on the date
|
||||||
_nextFixture = _nextMatchDate.replace('-', '')
|
_nextFixture = _nextMatchDate.replace('-', '')
|
||||||
@ -309,7 +316,7 @@ def motm_admin():
|
|||||||
currSuffix = tempSuffix[0]['motmurlsuffix']
|
currSuffix = tempSuffix[0]['motmurlsuffix']
|
||||||
print(currSuffix)
|
print(currSuffix)
|
||||||
flash('Man of the Match vote is now activated')
|
flash('Man of the Match vote is now activated')
|
||||||
flash('MotM URL https://hockey.ervine.cloud/motm/'+currSuffix)
|
flash('MotM URL https://motm.ervine.cloud/motm/'+currSuffix)
|
||||||
else:
|
else:
|
||||||
flash('Something went wrong - check with Smithers')
|
flash('Something went wrong - check with Smithers')
|
||||||
|
|
||||||
@ -554,6 +561,11 @@ def add_team():
|
|||||||
"""Add a new team"""
|
"""Add a new team"""
|
||||||
form = TeamForm()
|
form = TeamForm()
|
||||||
|
|
||||||
|
# Populate club choices
|
||||||
|
sql_clubs = text("SELECT hockey_club FROM clubs ORDER BY hockey_club")
|
||||||
|
clubs = sql_read(sql_clubs)
|
||||||
|
form.club.choices = [(club['hockey_club'], club['hockey_club']) for club in clubs]
|
||||||
|
|
||||||
if form.validate_on_submit():
|
if form.validate_on_submit():
|
||||||
if form.save_team.data:
|
if form.save_team.data:
|
||||||
# Check if team already exists
|
# Check if team already exists
|
||||||
@ -586,6 +598,11 @@ def edit_team(team_id):
|
|||||||
"""Edit an existing team"""
|
"""Edit an existing team"""
|
||||||
form = TeamForm()
|
form = TeamForm()
|
||||||
|
|
||||||
|
# Populate club choices
|
||||||
|
sql_clubs = text("SELECT hockey_club FROM clubs ORDER BY hockey_club")
|
||||||
|
clubs = sql_read(sql_clubs)
|
||||||
|
form.club.choices = [(club['hockey_club'], club['hockey_club']) for club in clubs]
|
||||||
|
|
||||||
if request.method == 'GET':
|
if request.method == 'GET':
|
||||||
# Load team data
|
# Load team data
|
||||||
sql = text("SELECT id, club, team, display_name, league FROM teams WHERE id = :team_id")
|
sql = text("SELECT id, club, team, display_name, league FROM teams WHERE id = :team_id")
|
||||||
@ -801,6 +818,107 @@ def data_import():
|
|||||||
return render_template('data_import.html', form=form)
|
return render_template('data_import.html', form=form)
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/admin/import/clubs/select', methods=['GET', 'POST'])
|
||||||
|
@basic_auth.required
|
||||||
|
def club_selection():
|
||||||
|
"""Club selection page for importing specific clubs"""
|
||||||
|
form = ClubSelectionForm()
|
||||||
|
|
||||||
|
# Fetch clubs from the website
|
||||||
|
try:
|
||||||
|
clubs = get_hk_hockey_clubs()
|
||||||
|
if not clubs:
|
||||||
|
# Fallback to predefined clubs if scraping fails
|
||||||
|
clubs = [
|
||||||
|
{'name': 'Hong Kong Football Club', 'abbreviation': 'HKFC', 'teams': ['A', 'B', 'C', 'F', 'G', 'H'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Kowloon Cricket Club', 'abbreviation': 'KCC', 'teams': ['A', 'B', 'C', 'D', 'E'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'United Services Recreation Club', 'abbreviation': 'USRC', 'teams': ['A', 'B', 'C'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Valley Fort Sports Club', 'abbreviation': 'Valley', 'teams': ['A', 'B', 'C', 'D', 'E', 'F'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'South China Sports Club', 'abbreviation': 'SSSC', 'teams': ['C'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Dragons Hockey Club', 'abbreviation': 'Dragons', 'teams': ['A', 'B'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Kai Tak Sports Club', 'abbreviation': 'Kai Tak', 'teams': ['B', 'C'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Royal Hong Kong Regiment Officers and Businessmen Association', 'abbreviation': 'RHOBA', 'teams': ['A', 'B', 'C'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Elite Hockey Club', 'abbreviation': 'Elite', 'teams': ['B', 'C'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Aquila Hockey Club', 'abbreviation': 'Aquila', 'teams': ['A', 'B'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Hong Kong Jockey Club', 'abbreviation': 'HKJ', 'teams': ['B', 'C'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Sirius Hockey Club', 'abbreviation': 'Sirius', 'teams': ['A', 'B'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Shaheen Hockey Club', 'abbreviation': 'Shaheen', 'teams': ['B'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Diocesan Boys School', 'abbreviation': 'Diocesan', 'teams': ['B'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Rhino Hockey Club', 'abbreviation': 'Rhino', 'teams': ['B'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Khalsa Hockey Club', 'abbreviation': 'Khalsa', 'teams': ['C'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Hong Kong Cricket Club', 'abbreviation': 'HKCC', 'teams': ['C', 'D'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Hong Kong Police Force', 'abbreviation': 'Police', 'teams': ['A'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Recreio Hockey Club', 'abbreviation': 'Recreio', 'teams': ['A', 'B'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Correctional Services Department', 'abbreviation': 'CSD', 'teams': ['A'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Dutch Hockey Club', 'abbreviation': 'Dutch', 'teams': ['B'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Hong Kong University Hockey Club', 'abbreviation': 'HKUHC', 'teams': ['A'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Kaitiaki Hockey Club', 'abbreviation': 'Kaitiaki', 'teams': ['A'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Antlers Hockey Club', 'abbreviation': 'Antlers', 'teams': ['C'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Marcellin Hockey Club', 'abbreviation': 'Marcellin', 'teams': ['A'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Skyers Hockey Club', 'abbreviation': 'Skyers', 'teams': ['A'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'JR Hockey Club', 'abbreviation': 'JR', 'teams': ['A'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'International University of Hong Kong', 'abbreviation': 'IUHK', 'teams': ['A'], 'convenor': None, 'email': None},
|
||||||
|
{'name': '144 United Hockey Club', 'abbreviation': '144U', 'teams': ['A'], 'convenor': None, 'email': None},
|
||||||
|
{'name': 'Hong Kong University', 'abbreviation': 'HKU', 'teams': ['A'], 'convenor': None, 'email': None},
|
||||||
|
]
|
||||||
|
except Exception as e:
|
||||||
|
flash(f'Error fetching clubs: {str(e)}', 'error')
|
||||||
|
clubs = []
|
||||||
|
|
||||||
|
if request.method == 'POST':
|
||||||
|
if form.select_all.data:
|
||||||
|
# Select all clubs
|
||||||
|
selected_clubs = [club['name'] for club in clubs]
|
||||||
|
return render_template('club_selection.html', form=form, clubs=clubs, selected_clubs=selected_clubs)
|
||||||
|
|
||||||
|
elif form.select_none.data:
|
||||||
|
# Select no clubs
|
||||||
|
return render_template('club_selection.html', form=form, clubs=clubs, selected_clubs=[])
|
||||||
|
|
||||||
|
elif form.import_selected.data:
|
||||||
|
# Import selected clubs
|
||||||
|
selected_clubs = request.form.getlist('selected_clubs')
|
||||||
|
|
||||||
|
if not selected_clubs:
|
||||||
|
flash('No clubs selected for import!', 'error')
|
||||||
|
return render_template('club_selection.html', form=form, clubs=clubs, selected_clubs=[])
|
||||||
|
|
||||||
|
imported_count = 0
|
||||||
|
skipped_count = 0
|
||||||
|
|
||||||
|
for club_name in selected_clubs:
|
||||||
|
# Find the club data
|
||||||
|
club_data = next((club for club in clubs if club['name'] == club_name), None)
|
||||||
|
if not club_data:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if club already exists
|
||||||
|
sql_check = text("SELECT hockey_club FROM clubs WHERE hockey_club = :club_name")
|
||||||
|
existing = sql_read(sql_check, {'club_name': club_name})
|
||||||
|
|
||||||
|
if existing:
|
||||||
|
skipped_count += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Import the club
|
||||||
|
logo_url = f"/static/images/clubs/{club_name.lower().replace(' ', '_').replace('.', '').replace(',', '')}_logo.png"
|
||||||
|
sql = text("INSERT INTO clubs (hockey_club, logo_url) VALUES (:club_name, :logo_url)")
|
||||||
|
sql_write(sql, {'club_name': club_name, 'logo_url': logo_url})
|
||||||
|
imported_count += 1
|
||||||
|
|
||||||
|
if imported_count > 0:
|
||||||
|
flash(f'Successfully imported {imported_count} clubs!', 'success')
|
||||||
|
if skipped_count > 0:
|
||||||
|
flash(f'Skipped {skipped_count} clubs that already exist.', 'info')
|
||||||
|
|
||||||
|
return redirect(url_for('club_management'))
|
||||||
|
|
||||||
|
elif form.cancel.data:
|
||||||
|
return redirect(url_for('data_import'))
|
||||||
|
|
||||||
|
return render_template('club_selection.html', form=form, clubs=clubs, selected_clubs=[])
|
||||||
|
|
||||||
|
|
||||||
@app.route('/admin/squad/submit', methods=['POST'])
|
@app.route('/admin/squad/submit', methods=['POST'])
|
||||||
@basic_auth.required
|
@basic_auth.required
|
||||||
def match_squad_submit():
|
def match_squad_submit():
|
||||||
@ -1162,7 +1280,7 @@ def voting_chart():
|
|||||||
date_result = sql_read_static(sql_date)
|
date_result = sql_read_static(sql_date)
|
||||||
|
|
||||||
if date_result:
|
if date_result:
|
||||||
matchDate = date_result[0]['nextDate'].replace('-', '')
|
matchDate = str(date_result[0]['nextdate']).replace('-', '')
|
||||||
else:
|
else:
|
||||||
matchDate = '20251012' # Default fallback
|
matchDate = '20251012' # Default fallback
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,16 @@
|
|||||||
@echo off
|
@echo off
|
||||||
echo 🐍 Starting MOTM Application...
|
echo 🐍 Starting MOTM Application...
|
||||||
|
|
||||||
|
REM Set PostgreSQL environment variables
|
||||||
|
set DATABASE_TYPE=postgresql
|
||||||
|
set POSTGRES_HOST=icarus.ipa.champion
|
||||||
|
set POSTGRES_PORT=5432
|
||||||
|
set POSTGRES_DATABASE=motm
|
||||||
|
set POSTGRES_USER=motm_user
|
||||||
|
set POSTGRES_PASSWORD=q7y7f7Lv*sODJZ2wGiv0Wq5a
|
||||||
|
|
||||||
|
echo 📊 Using PostgreSQL database: %POSTGRES_DATABASE% on %POSTGRES_HOST%
|
||||||
|
|
||||||
call venv\Scripts\activate.bat
|
call venv\Scripts\activate.bat
|
||||||
python.exe main.py
|
python.exe main.py
|
||||||
pause
|
pause
|
||||||
|
|||||||
11
motm_app/run_motm.sh
Normal file → Executable file
11
motm_app/run_motm.sh
Normal file → Executable file
@ -1,5 +1,16 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
echo "🐍 Starting MOTM Application..."
|
echo "🐍 Starting MOTM Application..."
|
||||||
|
|
||||||
|
# Set PostgreSQL environment variables
|
||||||
|
export DATABASE_TYPE=postgresql
|
||||||
|
export POSTGRES_HOST=icarus.ipa.champion
|
||||||
|
export POSTGRES_PORT=5432
|
||||||
|
export POSTGRES_DATABASE=motm
|
||||||
|
export POSTGRES_USER=motm_user
|
||||||
|
export POSTGRES_PASSWORD='q7y7f7Lv*sODJZ2wGiv0Wq5a'
|
||||||
|
|
||||||
|
echo "📊 Using PostgreSQL database: $POSTGRES_DATABASE on $POSTGRES_HOST"
|
||||||
|
|
||||||
source venv/bin/activate
|
source venv/bin/activate
|
||||||
python main.py
|
python main.py
|
||||||
|
|
||||||
|
|||||||
4
motm_app/static/images/clubs/placeholder_logo.svg
Normal file
4
motm_app/static/images/clubs/placeholder_logo.svg
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<svg width="120" height="80" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<rect width="120" height="80" fill="#f8f9fa" stroke="#dee2e6" stroke-width="2"/>
|
||||||
|
<text x="60" y="45" font-family="Arial, sans-serif" font-size="12" text-anchor="middle" fill="#6c757d">Club Logo</text>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 277 B |
1
motm_app/static/images/default_player.png
Normal file
1
motm_app/static/images/default_player.png
Normal file
@ -0,0 +1 @@
|
|||||||
|
iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==
|
||||||
@ -30,11 +30,11 @@ class matchSquadTable:
|
|||||||
html += ' <tbody>\n'
|
html += ' <tbody>\n'
|
||||||
for item in self.items:
|
for item in self.items:
|
||||||
html += ' <tr>\n'
|
html += ' <tr>\n'
|
||||||
html += f' <td>{item.get("playerNumber", "")}</td>\n'
|
html += f' <td>{item.get("playernumber", "")}</td>\n'
|
||||||
html += f' <td>{item.get("playerNickname", "")}</td>\n'
|
html += f' <td>{item.get("playernickname", "")}</td>\n'
|
||||||
html += f' <td>{item.get("playerSurname", "")}</td>\n'
|
html += f' <td>{item.get("playersurname", "")}</td>\n'
|
||||||
html += f' <td>{item.get("playerForenames", "")}</td>\n'
|
html += f' <td>{item.get("playerforenames", "")}</td>\n'
|
||||||
html += f' <td><form method="post" action="/admin/squad/remove?playerNumber={item.get("playerNumber", "")}"><button type="submit" class="btn btn-danger">Delete</button></form></td>\n'
|
html += f' <td><form method="post" action="/admin/squad/remove?playerNumber={item.get("playernumber", "")}"><button type="submit" class="btn btn-danger">Delete</button></form></td>\n'
|
||||||
html += ' </tr>\n'
|
html += ' </tr>\n'
|
||||||
html += ' </tbody>\n'
|
html += ' </tbody>\n'
|
||||||
|
|
||||||
|
|||||||
@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{{ form.logo_url.label(class="form-label") }}
|
{{ form.logo_url.label(class="form-label") }}
|
||||||
{{ form.logo_url(class="form-control") }}
|
{{ form.logo_url(class="form-control", id="logoUrl", onchange="previewLogo()") }}
|
||||||
{% if form.logo_url.errors %}
|
{% if form.logo_url.errors %}
|
||||||
<div class="text-danger">
|
<div class="text-danger">
|
||||||
{% for error in form.logo_url.errors %}
|
{% for error in form.logo_url.errors %}
|
||||||
@ -52,6 +52,15 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<small class="form-text text-muted">Enter the full URL to the club's logo image</small>
|
<small class="form-text text-muted">Enter the full URL to the club's logo image</small>
|
||||||
|
|
||||||
|
<!-- Logo Preview -->
|
||||||
|
<div id="logoPreview" class="mt-2" style="display: none;">
|
||||||
|
<label class="form-label small">Preview:</label>
|
||||||
|
<div class="border rounded p-2 bg-light">
|
||||||
|
<img id="previewImage" src="" alt="Logo preview" style="max-height: 80px; max-width: 120px;" onerror="this.style.display='none'; document.getElementById('previewError').style.display='block';" onload="document.getElementById('previewError').style.display='none';">
|
||||||
|
<div id="previewError" class="text-muted small" style="display: none;">Unable to load image</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||||
@ -70,5 +79,25 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function previewLogo() {
|
||||||
|
const urlInput = document.getElementById('logoUrl');
|
||||||
|
const previewDiv = document.getElementById('logoPreview');
|
||||||
|
const previewImg = document.getElementById('previewImage');
|
||||||
|
|
||||||
|
if (urlInput.value.trim()) {
|
||||||
|
previewImg.src = urlInput.value;
|
||||||
|
previewDiv.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
previewDiv.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preview logo on page load if URL is already filled
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
previewLogo();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{{ form.club.label(class="form-label") }}
|
{{ form.club.label(class="form-label") }}
|
||||||
{{ form.club(class="form-control") }}
|
{{ form.club(class="form-select") }}
|
||||||
{% if form.club.errors %}
|
{% if form.club.errors %}
|
||||||
<div class="text-danger">
|
<div class="text-danger">
|
||||||
{% for error in form.club.errors %}
|
{% for error in form.club.errors %}
|
||||||
@ -39,7 +39,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<small class="form-text text-muted">Enter the club name (e.g., HKFC, KCC, USRC)</small>
|
<small class="form-text text-muted">Select the club from the list of available clubs</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -70,7 +70,7 @@
|
|||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{{ form.league.label(class="form-label") }}
|
{{ form.league.label(class="form-label") }}
|
||||||
{{ form.league(class="form-control") }}
|
{{ form.league(class="form-select") }}
|
||||||
{% if form.league.errors %}
|
{% if form.league.errors %}
|
||||||
<div class="text-danger">
|
<div class="text-danger">
|
||||||
{% for error in form.league.errors %}
|
{% for error in form.league.errors %}
|
||||||
@ -78,7 +78,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<small class="form-text text-muted">Enter the league/division (e.g., Premier Division, Division 1)</small>
|
<small class="form-text text-muted">Select the league/division from the list</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||||
|
|||||||
@ -77,6 +77,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>ID</th>
|
<th>ID</th>
|
||||||
<th>Club Name</th>
|
<th>Club Name</th>
|
||||||
|
<th>Logo</th>
|
||||||
<th>Logo URL</th>
|
<th>Logo URL</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -87,9 +88,20 @@
|
|||||||
<td>{{ club.id }}</td>
|
<td>{{ club.id }}</td>
|
||||||
<td>{{ club.hockey_club }}</td>
|
<td>{{ club.hockey_club }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ club.logo_url }}" target="_blank" class="text-decoration-none">
|
{% if club.logo_url %}
|
||||||
{{ club.logo_url }}
|
<img src="{{ club.logo_url }}" alt="{{ club.hockey_club }} logo" style="max-height: 40px; max-width: 60px;" onerror="this.style.display='none'">
|
||||||
</a>
|
{% else %}
|
||||||
|
<span class="text-muted">No logo</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{% if club.logo_url %}
|
||||||
|
<a href="{{ club.logo_url }}" target="_blank" class="text-decoration-none small">
|
||||||
|
{{ club.logo_url[:50] }}{% if club.logo_url|length > 50 %}...{% endif %}
|
||||||
|
</a>
|
||||||
|
{% else %}
|
||||||
|
<span class="text-muted">No URL</span>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="/admin/clubs/edit/{{ club.id }}" class="btn btn-sm btn-outline-primary">Edit</a>
|
<a href="/admin/clubs/edit/{{ club.id }}" class="btn btn-sm btn-outline-primary">Edit</a>
|
||||||
|
|||||||
174
motm_app/templates/club_selection.html
Normal file
174
motm_app/templates/club_selection.html
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Select Clubs to Import - HKFC Men's C Team</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container mt-4">
|
||||||
|
<div class="row justify-content-center">
|
||||||
|
<div class="col-md-10">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
<h3>Select Clubs to Import</h3>
|
||||||
|
<p class="mb-0 text-muted">Choose which clubs you want to import from the Hong Kong Hockey Association</p>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% if messages %}
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<div class="alert alert-{{ 'danger' if category == 'error' else 'success' }} alert-dismissible fade show" role="alert">
|
||||||
|
{{ message }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
<div class="alert alert-info">
|
||||||
|
<h5>Club Selection</h5>
|
||||||
|
<p>Select the clubs you want to import. You can choose all clubs or select specific ones based on your needs.</p>
|
||||||
|
<p><strong>Note:</strong> Only new clubs will be imported. Existing clubs will be skipped to prevent duplicates.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% if clubs %}
|
||||||
|
<form method="POST" id="clubSelectionForm">
|
||||||
|
{{ form.hidden_tag() }}
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<div class="d-flex gap-2 mb-3">
|
||||||
|
<button type="submit" name="select_all" class="btn btn-outline-primary btn-sm" formnovalidate>
|
||||||
|
Select All
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="select_none" class="btn btn-outline-secondary btn-sm" formnovalidate>
|
||||||
|
Select None
|
||||||
|
</button>
|
||||||
|
<span class="text-muted align-self-center">
|
||||||
|
<span id="selectedCount">0</span> of {{ clubs|length }} clubs selected
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
{% for club in clubs %}
|
||||||
|
<div class="col-md-6 col-lg-4 mb-3">
|
||||||
|
<div class="card h-100">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="form-check">
|
||||||
|
<input class="form-check-input club-checkbox"
|
||||||
|
type="checkbox"
|
||||||
|
name="selected_clubs"
|
||||||
|
value="{{ club.name }}"
|
||||||
|
id="club_{{ loop.index }}"
|
||||||
|
{% if club.name in selected_clubs %}checked{% endif %}>
|
||||||
|
<label class="form-check-label w-100" for="club_{{ loop.index }}">
|
||||||
|
<div class="d-flex justify-content-between align-items-start">
|
||||||
|
<div>
|
||||||
|
<h6 class="mb-1">{{ club.name }}</h6>
|
||||||
|
{% if club.abbreviation %}
|
||||||
|
<small class="text-muted">{{ club.abbreviation }}</small>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
<div class="text-end">
|
||||||
|
{% if club.teams %}
|
||||||
|
<small class="badge bg-info">{{ club.teams|length }} teams</small>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% if club.convenor %}
|
||||||
|
<div class="mt-2">
|
||||||
|
<small class="text-muted">
|
||||||
|
<i class="bi bi-person"></i> {{ club.convenor }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% if club.email %}
|
||||||
|
<div>
|
||||||
|
<small class="text-muted">
|
||||||
|
<i class="bi bi-envelope"></i> {{ club.email }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-grid gap-2 d-md-flex justify-content-md-end mt-4">
|
||||||
|
<button type="submit" name="cancel" class="btn btn-secondary me-md-2" formnovalidate>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
<button type="submit" name="import_selected" class="btn btn-primary" id="importButton" disabled>
|
||||||
|
Import Selected Clubs
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<div class="alert alert-warning">
|
||||||
|
<h5>No clubs found</h5>
|
||||||
|
<p>Unable to fetch clubs from the Hong Kong Hockey Association website. This might be due to:</p>
|
||||||
|
<ul>
|
||||||
|
<li>Network connectivity issues</li>
|
||||||
|
<li>Website structure changes</li>
|
||||||
|
<li>Server maintenance</li>
|
||||||
|
</ul>
|
||||||
|
<p>Please try again later or contact the administrator.</p>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-3">
|
||||||
|
<a href="/admin/import" class="btn btn-outline-secondary">Back to Data Import</a>
|
||||||
|
<a href="/admin" class="btn btn-outline-secondary">Back to Admin</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script>
|
||||||
|
// Update selected count and enable/disable import button
|
||||||
|
function updateSelection() {
|
||||||
|
const checkboxes = document.querySelectorAll('.club-checkbox');
|
||||||
|
const selectedCount = document.querySelectorAll('.club-checkbox:checked').length;
|
||||||
|
const importButton = document.getElementById('importButton');
|
||||||
|
const countSpan = document.getElementById('selectedCount');
|
||||||
|
|
||||||
|
countSpan.textContent = selectedCount;
|
||||||
|
importButton.disabled = selectedCount === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add event listeners to checkboxes
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const checkboxes = document.querySelectorAll('.club-checkbox');
|
||||||
|
checkboxes.forEach(checkbox => {
|
||||||
|
checkbox.addEventListener('change', updateSelection);
|
||||||
|
});
|
||||||
|
updateSelection(); // Initial update
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle select all/none buttons
|
||||||
|
document.getElementById('clubSelectionForm').addEventListener('submit', function(e) {
|
||||||
|
if (e.submitter.name === 'select_all') {
|
||||||
|
e.preventDefault();
|
||||||
|
document.querySelectorAll('.club-checkbox').forEach(checkbox => {
|
||||||
|
checkbox.checked = true;
|
||||||
|
});
|
||||||
|
updateSelection();
|
||||||
|
} else if (e.submitter.name === 'select_none') {
|
||||||
|
e.preventDefault();
|
||||||
|
document.querySelectorAll('.club-checkbox').forEach(checkbox => {
|
||||||
|
checkbox.checked = false;
|
||||||
|
});
|
||||||
|
updateSelection();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -44,6 +44,14 @@
|
|||||||
<small class="form-text text-muted d-block">
|
<small class="form-text text-muted d-block">
|
||||||
Import 30+ hockey clubs including HKFC, KCC, USRC, Valley, SSSC, Dragons, etc.
|
Import 30+ hockey clubs including HKFC, KCC, USRC, Valley, SSSC, Dragons, etc.
|
||||||
</small>
|
</small>
|
||||||
|
<div class="mt-2">
|
||||||
|
<a href="/admin/import/clubs/select" class="btn btn-outline-primary btn-sm">
|
||||||
|
Select Specific Clubs
|
||||||
|
</a>
|
||||||
|
<small class="text-muted d-block mt-1">
|
||||||
|
Choose which clubs to import instead of importing all
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-check mb-3">
|
<div class="form-check mb-3">
|
||||||
|
|||||||
@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{{ form.logo_url.label(class="form-label") }}
|
{{ form.logo_url.label(class="form-label") }}
|
||||||
{{ form.logo_url(class="form-control") }}
|
{{ form.logo_url(class="form-control", id="logoUrl", onchange="previewLogo()") }}
|
||||||
{% if form.logo_url.errors %}
|
{% if form.logo_url.errors %}
|
||||||
<div class="text-danger">
|
<div class="text-danger">
|
||||||
{% for error in form.logo_url.errors %}
|
{% for error in form.logo_url.errors %}
|
||||||
@ -52,6 +52,15 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<small class="form-text text-muted">Enter the full URL to the club's logo image</small>
|
<small class="form-text text-muted">Enter the full URL to the club's logo image</small>
|
||||||
|
|
||||||
|
<!-- Logo Preview -->
|
||||||
|
<div id="logoPreview" class="mt-2" style="display: none;">
|
||||||
|
<label class="form-label small">Preview:</label>
|
||||||
|
<div class="border rounded p-2 bg-light">
|
||||||
|
<img id="previewImage" src="" alt="Logo preview" style="max-height: 80px; max-width: 120px;" onerror="this.style.display='none'; document.getElementById('previewError').style.display='block';" onload="document.getElementById('previewError').style.display='none';">
|
||||||
|
<div id="previewError" class="text-muted small" style="display: none;">Unable to load image</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||||
@ -70,5 +79,25 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function previewLogo() {
|
||||||
|
const urlInput = document.getElementById('logoUrl');
|
||||||
|
const previewDiv = document.getElementById('logoPreview');
|
||||||
|
const previewImg = document.getElementById('previewImage');
|
||||||
|
|
||||||
|
if (urlInput.value.trim()) {
|
||||||
|
previewImg.src = urlInput.value;
|
||||||
|
previewDiv.style.display = 'block';
|
||||||
|
} else {
|
||||||
|
previewDiv.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preview logo on page load if URL is already filled
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
previewLogo();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{{ form.club.label(class="form-label") }}
|
{{ form.club.label(class="form-label") }}
|
||||||
{{ form.club(class="form-control") }}
|
{{ form.club(class="form-select") }}
|
||||||
{% if form.club.errors %}
|
{% if form.club.errors %}
|
||||||
<div class="text-danger">
|
<div class="text-danger">
|
||||||
{% for error in form.club.errors %}
|
{% for error in form.club.errors %}
|
||||||
@ -39,7 +39,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<small class="form-text text-muted">Enter the club name (e.g., HKFC, KCC, USRC)</small>
|
<small class="form-text text-muted">Select the club from the list of available clubs</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@ -70,7 +70,7 @@
|
|||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
{{ form.league.label(class="form-label") }}
|
{{ form.league.label(class="form-label") }}
|
||||||
{{ form.league(class="form-control") }}
|
{{ form.league(class="form-select") }}
|
||||||
{% if form.league.errors %}
|
{% if form.league.errors %}
|
||||||
<div class="text-danger">
|
<div class="text-danger">
|
||||||
{% for error in form.league.errors %}
|
{% for error in form.league.errors %}
|
||||||
@ -78,7 +78,7 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<small class="form-text text-muted">Enter the league/division (e.g., Premier Division, Division 1)</small>
|
<small class="form-text text-muted">Select the league/division from the list</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
<div class="d-grid gap-2 d-md-flex justify-content-md-end">
|
||||||
|
|||||||
@ -30,10 +30,10 @@
|
|||||||
<span class="input-group-addon" id="basic-addon1">Man of the Match</span>
|
<span class="input-group-addon" id="basic-addon1">Man of the Match</span>
|
||||||
<select class="form-control" name="motmVote" required>
|
<select class="form-control" name="motmVote" required>
|
||||||
{% for item in data %}
|
{% for item in data %}
|
||||||
{% if item.playerNickname != "" %}
|
{% if item.playernickname != "" %}
|
||||||
<option value={{ item.playerNumber }}>{{ item.playerNickname }}</option>
|
<option value={{ item.playernumber }}>{{ item.playernickname }}</option>
|
||||||
{% else %}
|
{% else %}
|
||||||
<option value={{ item.playerNumber }}>{{ item.playerSurname }}, {{ item.playerForenames }}</option>
|
<option value={{ item.playernumber }}>{{ item.playersurname }}, {{ item.playerforenames }}</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
@ -44,10 +44,10 @@
|
|||||||
<span class="input-group-addon" id="basic-addon1">Dick of the Day</span>
|
<span class="input-group-addon" id="basic-addon1">Dick of the Day</span>
|
||||||
<select class="form-control" name="dotdVote" required>
|
<select class="form-control" name="dotdVote" required>
|
||||||
{% for item in data %}
|
{% for item in data %}
|
||||||
{% if item.playerNickname != "" %}
|
{% if item.playernickname != "" %}
|
||||||
<option value={{ item.playerNumber }}>{{ item.playerNickname }}</option>
|
<option value={{ item.playernumber }}>{{ item.playernickname }}</option>
|
||||||
{% else %}
|
{% else %}
|
||||||
<option value={{ item.playerNumber }}>{{ item.playerSurname }}, {{ item.playerForenames }}</option>
|
<option value={{ item.playernumber }}>{{ item.playersurname }}, {{ item.playerforenames }}</option>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
<body>
|
<body>
|
||||||
Smithers' army of Internet monkeys will now go about adding up the votes ...
|
Smithers' army of Internet monkeys will now go about adding up the votes ...
|
||||||
<p>
|
<p>
|
||||||
<img src="https://storage.googleapis.com/hk-hockey-data/images/monkey-typing-simpsons.jpg"></img>
|
<img src="http://icarus.ipa.champion:9000/hockey-app/assets/simpsons-monkeys.jpg"></img>
|
||||||
</p>
|
</p>
|
||||||
<a class="btn btn-primary" href="/" role="button">Home</a>
|
<a class="btn btn-primary" href="/" role="button">Home</a>
|
||||||
<a class="btn btn-info" href="/motm/comments" role="button">Comments</a>
|
<a class="btn btn-info" href="/motm/comments" role="button">Comments</a>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user