diff --git a/.github/workflows/pylint.yml b/.github/workflows/pylint.yml index 02a7454..5f5df48 100644 --- a/.github/workflows/pylint.yml +++ b/.github/workflows/pylint.yml @@ -21,4 +21,4 @@ jobs: pip install pylint - name: Analysing the code with pylint run: | - pylint --disable=line-too-long,too-many-locals,trailing-whitespace,too-many-statements $(git ls-files '*.py') + pylint --rcfile=.pylintrc $(git ls-files '*.py') diff --git a/.pylintrc b/.pylintrc new file mode 100644 index 0000000..cb022dc --- /dev/null +++ b/.pylintrc @@ -0,0 +1,13 @@ +[MESSAGES CONTROL] + +# Disable the message, report, category or checker with the given id(s). +disable=invalid-name, + line-too-long, + missing-class-docstring, + missing-module-docstring, + redefined-outer-name, + too-few-public-methods, + too-many-locals, + too-many-statements, + trailing-whitespace, + diff --git a/app.py b/app.py index 7c77edd..ae6b58b 100644 --- a/app.py +++ b/app.py @@ -1,13 +1,12 @@ +"""main app.py""" from dotenv import load_dotenv -# Load environment variables from .env file -load_dotenv() - -from config import DevelopmentConfig, ProductionConfig +from config import DevelopmentConfig from usport_flask_app import create_app from usport_flask_app.models import db from data_pipeline import update_db - +# Load environment variables from .env file +load_dotenv() app = create_app(config_name=DevelopmentConfig) db.init_app(app) @@ -19,5 +18,3 @@ def scheduled(): if __name__ == "__main__": app.run() - -#add cron job when deploying \ No newline at end of file diff --git a/config.py b/config.py index dd400d8..160fb09 100644 --- a/config.py +++ b/config.py @@ -1,14 +1,18 @@ +"""Configurations file""" import os mysqldatabase = f"mysql+pymysql://usportsballwebapp:{os.environ.get('USPORT_BBALL_PASSWORD')}.@localhost/usports_bball" class Config: + """Default Configuration""" DEBUG = False SQLALCHEMY_TRACK_MODIFICATIONS = False class DevelopmentConfig(Config): + """Development Configuration""" DEBUG = True SQLALCHEMY_DATABASE_URI = mysqldatabase class ProductionConfig(Config): + """Production Configuration""" DEBUG = False - SQLALCHEMY_DATABASE_URI = mysqldatabase \ No newline at end of file + SQLALCHEMY_DATABASE_URI = mysqldatabase diff --git a/data_pipeline/database_update.py b/data_pipeline/database_update.py index 57a4b93..67db2f3 100644 --- a/data_pipeline/database_update.py +++ b/data_pipeline/database_update.py @@ -70,7 +70,7 @@ def update_usports_bball_db(mysql_password: str): men_df.to_sql(name='men_team', con=conn, if_exists='replace', index=False,dtype=team_dtypes) logging.info("Men's team data written to 'men_team' table successfully!") - except Exception as e: + except ValueError as e: logging.error("Error writing men's team data to database: %s", e) try: @@ -78,7 +78,7 @@ def update_usports_bball_db(mysql_password: str): women_df.to_sql(name='women_team', con=conn, if_exists='replace', index=False,dtype=team_dtypes) logging.info("Women's team data written to 'women_team' table successfully!") - except Exception as e: + except ValueError as e: logging.error("Error writing women's team data to database: %s", e) try: @@ -86,7 +86,7 @@ def update_usports_bball_db(mysql_password: str): men_players_df.to_sql(name='men_players', con=conn,if_exists='replace', index=False,dtype=player_dtypes) logging.info("Men's players data written to 'men_players' table successfully!") - except Exception as e: + except ValueError as e: logging.error("Error writing men's players data to database: %s", e) try: @@ -94,7 +94,7 @@ def update_usports_bball_db(mysql_password: str): women_players_df.to_sql(name='women_players', con=conn, if_exists='replace', index=False, dtype=player_dtypes) logging.info("Women's players data written to 'women_players' table successfully!") - except Exception as e: + except ValueError as e: logging.error("Error writing women's players data to database: %s", e) sql_queries = [ @@ -111,7 +111,7 @@ def update_usports_bball_db(mysql_password: str): try: # Execute SQL queries for idx, query in enumerate(sql_queries, start=1): - logging.info(f"Executing SQL Query {idx}:") + logging.info("Executing SQL Query %s",idx) conn.execute(text(query)) logging.info("Executed query: %s\n", query.strip()) logging.info("All SQL queries executed successfully. \n") diff --git a/requirements.txt b/requirements.txt index 676c9e4..28d8d86 100644 --- a/requirements.txt +++ b/requirements.txt @@ -29,6 +29,6 @@ SQLAlchemy==2.0.29 typing_extensions==4.11.0 tzdata==2024.1 urllib3==2.2.1 -usports-basketball==1.0.0 +usports-basketball==1.0.1 Werkzeug==3.0.2 WTForms==3.1.2 diff --git a/usport_flask_app/__init__.py b/usport_flask_app/__init__.py index 8d5f730..e462a29 100644 --- a/usport_flask_app/__init__.py +++ b/usport_flask_app/__init__.py @@ -1,9 +1,12 @@ +"""U Sport web app with routes""" from flask import Flask, render_template, request, redirect, url_for from sqlalchemy import func, cast, Numeric +from sqlalchemy.exc import SQLAlchemyError +from config import DevelopmentConfig from .models import db, Feedback, MenTeam, WomenTeam, MenPlayers, WomenPlayers from .radar_data_calculator import calculate_radar_data, find_min_max_values from .team_stats_calulator import calculate_categories -from config import DevelopmentConfig + def create_app(config_name=DevelopmentConfig): @@ -26,15 +29,14 @@ def index(): try: # Commit the changes to the database db.session.commit() - except Exception as e: + except SQLAlchemyError as e: # Rollback the transaction in case of error db.session.rollback() print("Error adding feedback entry:", str(e)) # Redirect to the index page after form submission return redirect("/") - else: - # Render the home.html template for GET requests - return render_template("index.html") + # Render the home.html template for GET requests + return render_template("index.html") # Define the route for the about page diff --git a/usport_flask_app/models.py b/usport_flask_app/models.py index 8714daa..56d234c 100644 --- a/usport_flask_app/models.py +++ b/usport_flask_app/models.py @@ -15,7 +15,6 @@ - MenPlayers: Represents men's basketball player data. - WomenPlayers: Represents women's basketball player data. """ - from datetime import datetime, timezone from flask_sqlalchemy import SQLAlchemy from sqlalchemy.orm import Mapped, mapped_column diff --git a/usport_flask_app/radar_data_calculator.py b/usport_flask_app/radar_data_calculator.py index 1f25881..e026205 100644 --- a/usport_flask_app/radar_data_calculator.py +++ b/usport_flask_app/radar_data_calculator.py @@ -1,16 +1,16 @@ """ -This module contains functions for calculating and normalizing basketball statistics used in the USPORTS BASKETBALL WEB APP. +This module contains functions for calculating and normalizing basketball statistics. Functions: - normalize: Normalize a value within a specified range. - find_min_max_values: Find the minimum and maximum values for various statistics across teams. - calculate_radar_data: Calculate radar chart data for teams based on their statistics. """ +from math import floor +from typing import Union from sqlalchemy.engine.row import Row from sqlalchemy import func from .models import db, MenTeam, WomenTeam -from math import floor -from typing import Union def normalize(value: Union[int, float], min_value: Union[int, float], max_value: Union[int, float], upper_bound: int = 99) -> int: """ @@ -102,12 +102,12 @@ def calculate_radar_data(specific_team_table: Union[MenTeam, WomenTeam], min_max defensive_efficiency = normalize(1/query_def_efficiency(team), 1/max_defensive_efficiency, 1/min_defensive_efficiency) playmaking = normalize(query_playmaking(team), min_playmaking, max_playmaking) rebound_margin = normalize(query_rebound_margin(team),min_rebound_margin, max_rebound_margin) - EFG_percentage = normalize(query_effective_fg_percentage(team), min_effective_fg_percentage, max_effective_fg_percentage) + efg_percentage = normalize(query_effective_fg_percentage(team), min_effective_fg_percentage, max_effective_fg_percentage) three_point_rating = normalize(query_3pt_shooting_efficiency(team), min_3pt_rating, max_3pt_rating, 98) #order of array should match labels in javascript charjs label - radar_data[team.team_name] = [overall_efficiency, defensive_efficiency, playmaking, rebound_margin, three_point_rating, EFG_percentage, offensive_efficiency] + radar_data[team.team_name] = [overall_efficiency, defensive_efficiency, playmaking, rebound_margin, three_point_rating, efg_percentage, offensive_efficiency] return radar_data @@ -210,4 +210,4 @@ def query_3pt_shooting_efficiency(team: Union[MenTeam, WomenTeam]): # Calculate the team's 3-point shooting efficiency three_point_efficiency = (0.9 * team.three_point_percentage) + (0.1 * three_point_attempts_per_game) - return three_point_efficiency \ No newline at end of file + return three_point_efficiency diff --git a/usport_flask_app/team_stats_calulator.py b/usport_flask_app/team_stats_calulator.py index 9be6950..aae46cc 100644 --- a/usport_flask_app/team_stats_calulator.py +++ b/usport_flask_app/team_stats_calulator.py @@ -1,5 +1,23 @@ -from .models import MenTeam, WomenTeam +"""Calculate team and opponent per game statistics. + +Given a MenTeam or WomenTeam object, this function calculates various per game statistics: +- Points +- Field Goal Percentage +- Three-Pointers Made +- Three-Point Percentage +- Total Rebounds +- Offensive Rebounds +- Assists +- Steals +- Blocks +- Turnovers +- Fouls. + +Returns: + Tuple containing team per game statistics and opponent per game statistics. +""" from typing import Union +from .models import MenTeam, WomenTeam def calculate_categories(team: Union[MenTeam, WomenTeam]): """ @@ -46,4 +64,4 @@ def calculate_categories(team: Union[MenTeam, WomenTeam]): steals_per_game_against, blocks_per_game_against, turnovers_per_game_against, fouls_per_game_against,ppp_against) - return team_stats, team_opponent_stats \ No newline at end of file + return team_stats, team_opponent_stats diff --git a/usport_flask_app/templates/league.html b/usport_flask_app/templates/league.html index 9d51373..d67548b 100644 --- a/usport_flask_app/templates/league.html +++ b/usport_flask_app/templates/league.html @@ -25,7 +25,6 @@
Team 1
+2
+Team 2
+1
+Team 3
+2
+Team 4
+1
+Team 5
+2
+Team 6
+1
+Team 7
+2
+Team 8
+1
+Team 1
+2
+Team 3
+1
+Team 5
+2
+Team 7
+0
+Team 1
+0
+Team 5
+2
+Team 1
+2
+Team 3
+1
+Team 5
+2
+Team 7
+0
+Team 1
+0
+Team 5
+2
+