Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • lokeshsat01/capstone-project
  • ashutoshpocham/capstone-project
  • ggali14/capstone-project
3 results
Show changes
Showing
with 247809 additions and 0 deletions
import torch
import torchvision.transforms as transforms
import cv2
from torchvision import models
import numpy as np
class CourtLineDetector:
def __init__(self, model_path):
self.model = models.resnet50(pretrained=True)
self.model.fc = torch.nn.Linear(self.model.fc.in_features, 14*2)
self.model.load_state_dict(torch.load(model_path, map_location='cpu'))
self.transform = transforms.Compose([
transforms.ToPILImage(),
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
def predict(self, image):
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
image_tensor = self.transform(image_rgb).unsqueeze(0)
with torch.no_grad():
outputs = self.model(image_tensor)
keypoints = outputs.squeeze().cpu().numpy()
original_h, original_w = image.shape[:2]
keypoints[::2] *= original_w / 224.0
keypoints[1::2] *= original_h / 224.0
return keypoints
def draw_keypoints(self, image, keypoints):
# Plot keypoints on the image
for i in range(0, len(keypoints), 2):
x = int(keypoints[i])
y = int(keypoints[i+1])
cv2.putText(image, str(i//2), (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
cv2.circle(image, (x, y), 5, (0, 0, 255), -1)
return image
def draw_keypoints_on_video(self, video_frames, keypoints):
output_video_frames = []
for frame in video_frames:
frame = self.draw_keypoints(frame, keypoints)
output_video_frames.append(frame)
return output_video_frames
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
from utils import (
read_video, save_video, measure_distance, draw_player_stats, convert_pixel_distance_to_meters
)
import constants
from trackers import PlayerTracker, BallTracker, PoseDetector
from court_line_detector import CourtLineDetector
from mini_court import MiniCourt
import cv2
import pandas as pd
from copy import deepcopy
import os
def main():
# Ensure input and output directories exist
input_video_path = "static/input_videos/input_video.mp4"
output_video_path = "static/output_videos/input_video1.avi"
os.makedirs(os.path.dirname(output_video_path), exist_ok=True)
# Read Video
print("Reading video...")
video_frames = read_video(input_video_path)
# Detect Players and Ball
player_tracker = PlayerTracker(model_path='yolov8x')
ball_tracker = BallTracker(model_path='models/yolov5l6u.pt')
pose_detector = PoseDetector(model_path='models/final_pose_model.pth')
print("Detecting players and ball...")
player_detections = player_tracker.detect_frames(
video_frames, read_from_stub=True, stub_path="tracker_stubs/player_detections.pkl"
)
ball_detections = ball_tracker.detect_frames(
video_frames, read_from_stub=True, stub_path="tracker_stubs/ball_detections.pkl"
)
ball_detections = ball_tracker.interpolate_ball_positions(ball_detections)
# Court Line Detection
print("Detecting court lines...")
court_model_path = "models/keypoints_model.pth"
court_line_detector = CourtLineDetector(court_model_path)
court_keypoints = court_line_detector.predict(video_frames[0])
# Filter players
print("Filtering player detections...")
player_detections = player_tracker.choose_and_filter_players(court_keypoints, player_detections)
# Initialize Mini Court
mini_court = MiniCourt(video_frames[0])
# Detect ball shots
ball_shot_frames = ball_tracker.get_ball_shot_frames(ball_detections)
# Convert positions to mini court coordinates
print("Converting positions to mini court coordinates...")
player_mini_court_detections, ball_mini_court_detections = mini_court.convert_bounding_boxes_to_mini_court_coordinates(
player_detections, ball_detections, court_keypoints
)
# Initialize player statistics data
player_stats_data = [{
'frame_num': 0,
'player_1_number_of_shots': 0, 'player_1_total_shot_speed': 0, 'player_1_last_shot_speed': 0,
'player_1_total_player_speed': 0, 'player_1_last_player_speed': 0,
'player_2_number_of_shots': 0, 'player_2_total_shot_speed': 0, 'player_2_last_shot_speed': 0,
'player_2_total_player_speed': 0, 'player_2_last_player_speed': 0,
'player_1_pose_backhand': 0, 'player_1_pose_forehand': 0, 'player_1_pose_ready': 0, 'player_1_pose_serve': 0,
'player_2_pose_backhand': 0, 'player_2_pose_forehand': 0, 'player_2_pose_ready': 0, 'player_2_pose_serve': 0
}]
print("Calculating player statistics...")
for ball_shot_ind in range(len(ball_shot_frames) - 1):
start_frame = ball_shot_frames[ball_shot_ind]
end_frame = ball_shot_frames[ball_shot_ind + 1]
ball_shot_time_in_seconds = (end_frame - start_frame) / 24 # Assuming 24fps
# Measure ball distance and speed
distance_covered_pixels = measure_distance(
ball_mini_court_detections[start_frame][1], ball_mini_court_detections[end_frame][1]
)
distance_covered_meters = convert_pixel_distance_to_meters(
distance_covered_pixels, constants.DOUBLE_LINE_WIDTH, mini_court.get_width_of_mini_court()
)
speed_of_ball_shot = (distance_covered_meters / ball_shot_time_in_seconds) * 3.6 # km/h
# Identify the player who hit the ball
player_positions = player_mini_court_detections[start_frame]
player_shot_ball = min(
player_positions.keys(),
key=lambda pid: measure_distance(player_positions[pid], ball_mini_court_detections[start_frame][1])
)
# Copy the previous stats to keep a running total.
current_stats = deepcopy(player_stats_data[-1])
current_stats['frame_num'] = start_frame
player_frame = video_frames[start_frame].copy()
for player_id, bbox in player_detections[start_frame].items():
# Predict keypoints and pose class
keypoints, pose_class = pose_detector.crop_and_predict_pose(player_frame, bbox)
player_frame = pose_detector.draw_pose(player_frame, bbox, keypoints, pose_class)
# Print for debugging purposes
pose_label = ["Backhand", "Forehand", "Ready", "Serve"][pose_class]
print(f"Player {player_id} detected with pose: {pose_label}")
# Ensure stats are updated correctly
stat_key = f'player_{player_id}_pose_{pose_label.lower()}'
if stat_key in current_stats:
current_stats[stat_key] += 1
else:
print(f"Invalid stat key: {stat_key}") # Debugging message
# Ensure the modified frame is saved back to the video frames
video_frames[start_frame] = player_frame
# Update statistics for this frame
player_stats_data.append(current_stats)
# Calculate opponent speed
opponent_id = 1 if player_shot_ball == 2 else 2
opponent_distance_pixels = measure_distance(
player_mini_court_detections[start_frame][opponent_id],
player_mini_court_detections[end_frame][opponent_id]
)
opponent_distance_meters = convert_pixel_distance_to_meters(
opponent_distance_pixels, constants.DOUBLE_LINE_WIDTH, mini_court.get_width_of_mini_court()
)
opponent_speed = (opponent_distance_meters / ball_shot_time_in_seconds) * 3.6 # km/h
# Update player stats
current_stats = deepcopy(player_stats_data[-1])
current_stats['frame_num'] = start_frame
current_stats[f'player_{player_shot_ball}_number_of_shots'] += 1
current_stats[f'player_{player_shot_ball}_total_shot_speed'] += speed_of_ball_shot
current_stats[f'player_{player_shot_ball}_last_shot_speed'] = speed_of_ball_shot
current_stats[f'player_{opponent_id}_total_player_speed'] += opponent_speed
current_stats[f'player_{opponent_id}_last_player_speed'] = opponent_speed
player_stats_data.append(current_stats)
# Create a DataFrame for player stats
stats_df = pd.DataFrame(player_stats_data)
frames_df = pd.DataFrame({'frame_num': range(len(video_frames))})
stats_df = pd.merge(frames_df, stats_df, on='frame_num', how='left').ffill()
# Calculate average speeds
stats_df['player_1_average_shot_speed'] = stats_df['player_1_total_shot_speed'] / stats_df['player_1_number_of_shots']
stats_df['player_2_average_shot_speed'] = stats_df['player_2_total_shot_speed'] / stats_df['player_2_number_of_shots']
stats_df['player_1_average_player_speed'] = stats_df['player_1_total_player_speed'] / stats_df['player_2_number_of_shots']
stats_df['player_2_average_player_speed'] = stats_df['player_2_total_player_speed'] / stats_df['player_1_number_of_shots']
print(stats_df[['frame_num',
'player_1_pose_backhand', 'player_1_pose_forehand',
'player_1_pose_ready', 'player_1_pose_serve']].head())
# Draw bounding boxes, keypoints, and stats on the video frames
print("Generating output video...")
output_frames = pose_detector.draw_poses(video_frames, player_detections, {})
output_frames = player_tracker.draw_bboxes(video_frames, player_detections)
output_frames = ball_tracker.draw_bboxes(output_frames, ball_detections)
output_frames = court_line_detector.draw_keypoints_on_video(output_frames, court_keypoints)
output_frames = mini_court.draw_mini_court(output_frames)
output_frames = mini_court.draw_points_on_mini_court(output_frames, player_mini_court_detections)
output_frames = mini_court.draw_points_on_mini_court(output_frames, ball_mini_court_detections, color=(0, 255, 255))
output_frames = draw_player_stats(output_frames, stats_df)
# Add frame numbers to the video
for i, frame in enumerate(output_frames):
cv2.putText(frame, f"Frame: {i}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# Save the output video
save_video(output_frames, output_video_path)
print(f"Video saved at {output_video_path}")
if __name__ == "__main__":
main()
from .mini_court import MiniCourt
\ No newline at end of file
File added
File added
import cv2
import numpy as np
import sys
sys.path.append('../')
import constants
from utils import (
convert_meters_to_pixel_distance,
convert_pixel_distance_to_meters,
get_foot_position,
get_closest_keypoint_index,
get_height_of_bbox,
measure_xy_distance,
get_center_of_bbox,
measure_distance
)
class MiniCourt():
def __init__(self,frame):
self.drawing_rectangle_width = 250
self.drawing_rectangle_height = 500
self.buffer = 50
self.padding_court=20
self.set_canvas_background_box_position(frame)
self.set_mini_court_position()
self.set_court_drawing_key_points()
self.set_court_lines()
def convert_meters_to_pixels(self, meters):
return convert_meters_to_pixel_distance(meters,
constants.DOUBLE_LINE_WIDTH,
self.court_drawing_width
)
def set_court_drawing_key_points(self):
drawing_key_points = [0]*28
# point 0
drawing_key_points[0] , drawing_key_points[1] = int(self.court_start_x), int(self.court_start_y)
# point 1
drawing_key_points[2] , drawing_key_points[3] = int(self.court_end_x), int(self.court_start_y)
# point 2
drawing_key_points[4] = int(self.court_start_x)
drawing_key_points[5] = self.court_start_y + self.convert_meters_to_pixels(constants.HALF_COURT_LINE_HEIGHT*2)
# point 3
drawing_key_points[6] = drawing_key_points[0] + self.court_drawing_width
drawing_key_points[7] = drawing_key_points[5]
# #point 4
drawing_key_points[8] = drawing_key_points[0] + self.convert_meters_to_pixels(constants.DOUBLE_ALLY_DIFFERENCE)
drawing_key_points[9] = drawing_key_points[1]
# #point 5
drawing_key_points[10] = drawing_key_points[4] + self.convert_meters_to_pixels(constants.DOUBLE_ALLY_DIFFERENCE)
drawing_key_points[11] = drawing_key_points[5]
# #point 6
drawing_key_points[12] = drawing_key_points[2] - self.convert_meters_to_pixels(constants.DOUBLE_ALLY_DIFFERENCE)
drawing_key_points[13] = drawing_key_points[3]
# #point 7
drawing_key_points[14] = drawing_key_points[6] - self.convert_meters_to_pixels(constants.DOUBLE_ALLY_DIFFERENCE)
drawing_key_points[15] = drawing_key_points[7]
# #point 8
drawing_key_points[16] = drawing_key_points[8]
drawing_key_points[17] = drawing_key_points[9] + self.convert_meters_to_pixels(constants.NO_MANS_LAND_HEIGHT)
# # #point 9
drawing_key_points[18] = drawing_key_points[16] + self.convert_meters_to_pixels(constants.SINGLE_LINE_WIDTH)
drawing_key_points[19] = drawing_key_points[17]
# #point 10
drawing_key_points[20] = drawing_key_points[10]
drawing_key_points[21] = drawing_key_points[11] - self.convert_meters_to_pixels(constants.NO_MANS_LAND_HEIGHT)
# # #point 11
drawing_key_points[22] = drawing_key_points[20] + self.convert_meters_to_pixels(constants.SINGLE_LINE_WIDTH)
drawing_key_points[23] = drawing_key_points[21]
# # #point 12
drawing_key_points[24] = int((drawing_key_points[16] + drawing_key_points[18])/2)
drawing_key_points[25] = drawing_key_points[17]
# # #point 13
drawing_key_points[26] = int((drawing_key_points[20] + drawing_key_points[22])/2)
drawing_key_points[27] = drawing_key_points[21]
self.drawing_key_points=drawing_key_points
def set_court_lines(self):
self.lines = [
(0, 2),
(4, 5),
(6,7),
(1,3),
(0,1),
(8,9),
(10,11),
(10,11),
(2,3)
]
def set_mini_court_position(self):
self.court_start_x = self.start_x + self.padding_court
self.court_start_y = self.start_y + self.padding_court
self.court_end_x = self.end_x - self.padding_court
self.court_end_y = self.end_y - self.padding_court
self.court_drawing_width = self.court_end_x - self.court_start_x
def set_canvas_background_box_position(self,frame):
frame= frame.copy()
self.end_x = frame.shape[1] - self.buffer
self.end_y = self.buffer + self.drawing_rectangle_height
self.start_x = self.end_x - self.drawing_rectangle_width
self.start_y = self.end_y - self.drawing_rectangle_height
def draw_court(self,frame):
for i in range(0, len(self.drawing_key_points),2):
x = int(self.drawing_key_points[i])
y = int(self.drawing_key_points[i+1])
cv2.circle(frame, (x,y),5, (0,0,255),-1)
# draw Lines
for line in self.lines:
start_point = (int(self.drawing_key_points[line[0]*2]), int(self.drawing_key_points[line[0]*2+1]))
end_point = (int(self.drawing_key_points[line[1]*2]), int(self.drawing_key_points[line[1]*2+1]))
cv2.line(frame, start_point, end_point, (0, 0, 0), 2)
# Draw net
net_start_point = (self.drawing_key_points[0], int((self.drawing_key_points[1] + self.drawing_key_points[5])/2))
net_end_point = (self.drawing_key_points[2], int((self.drawing_key_points[1] + self.drawing_key_points[5])/2))
cv2.line(frame, net_start_point, net_end_point, (255, 0, 0), 2)
return frame
def draw_background_rectangle(self,frame):
shapes = np.zeros_like(frame,np.uint8)
# Draw the rectangle
cv2.rectangle(shapes, (self.start_x, self.start_y), (self.end_x, self.end_y), (255, 255, 255), cv2.FILLED)
out = frame.copy()
alpha=0.5
mask = shapes.astype(bool)
out[mask] = cv2.addWeighted(frame, alpha, shapes, 1 - alpha, 0)[mask]
return out
def draw_mini_court(self,frames):
output_frames = []
for frame in frames:
frame = self.draw_background_rectangle(frame)
frame = self.draw_court(frame)
output_frames.append(frame)
return output_frames
def get_start_point_of_mini_court(self):
return (self.court_start_x,self.court_start_y)
def get_width_of_mini_court(self):
return self.court_drawing_width
def get_court_drawing_keypoints(self):
return self.drawing_key_points
def get_mini_court_coordinates(self,
object_position,
closest_key_point,
closest_key_point_index,
player_height_in_pixels,
player_height_in_meters
):
distance_from_keypoint_x_pixels, distance_from_keypoint_y_pixels = measure_xy_distance(object_position, closest_key_point)
# Conver pixel distance to meters
distance_from_keypoint_x_meters = convert_pixel_distance_to_meters(distance_from_keypoint_x_pixels,
player_height_in_meters,
player_height_in_pixels
)
distance_from_keypoint_y_meters = convert_pixel_distance_to_meters(distance_from_keypoint_y_pixels,
player_height_in_meters,
player_height_in_pixels
)
# Convert to mini court coordinates
mini_court_x_distance_pixels = self.convert_meters_to_pixels(distance_from_keypoint_x_meters)
mini_court_y_distance_pixels = self.convert_meters_to_pixels(distance_from_keypoint_y_meters)
closest_mini_coourt_keypoint = ( self.drawing_key_points[closest_key_point_index*2],
self.drawing_key_points[closest_key_point_index*2+1]
)
mini_court_player_position = (closest_mini_coourt_keypoint[0]+mini_court_x_distance_pixels,
closest_mini_coourt_keypoint[1]+mini_court_y_distance_pixels
)
return mini_court_player_position
def convert_bounding_boxes_to_mini_court_coordinates(self,player_boxes, ball_boxes, original_court_key_points ):
player_heights = {
1: constants.PLAYER_1_HEIGHT_METERS,
2: constants.PLAYER_2_HEIGHT_METERS
}
output_player_boxes= []
output_ball_boxes= []
for frame_num, player_bbox in enumerate(player_boxes):
ball_box = ball_boxes[frame_num][1]
ball_position = get_center_of_bbox(ball_box)
closest_player_id_to_ball = min(player_bbox.keys(), key=lambda x: measure_distance(ball_position, get_center_of_bbox(player_bbox[x])))
output_player_bboxes_dict = {}
for player_id, bbox in player_bbox.items():
foot_position = get_foot_position(bbox)
# Get The closest keypoint in pixels
closest_key_point_index = get_closest_keypoint_index(foot_position,original_court_key_points, [0,2,12,13])
closest_key_point = (original_court_key_points[closest_key_point_index*2],
original_court_key_points[closest_key_point_index*2+1])
# Get Player height in pixels
frame_index_min = max(0, frame_num-20)
frame_index_max = min(len(player_boxes), frame_num+50)
bboxes_heights_in_pixels = [get_height_of_bbox(player_boxes[i][player_id]) for i in range (frame_index_min,frame_index_max)]
max_player_height_in_pixels = max(bboxes_heights_in_pixels)
mini_court_player_position = self.get_mini_court_coordinates(foot_position,
closest_key_point,
closest_key_point_index,
max_player_height_in_pixels,
player_heights[player_id]
)
output_player_bboxes_dict[player_id] = mini_court_player_position
if closest_player_id_to_ball == player_id:
# Get The closest keypoint in pixels
closest_key_point_index = get_closest_keypoint_index(ball_position,original_court_key_points, [0,2,12,13])
closest_key_point = (original_court_key_points[closest_key_point_index*2],
original_court_key_points[closest_key_point_index*2+1])
mini_court_player_position = self.get_mini_court_coordinates(ball_position,
closest_key_point,
closest_key_point_index,
max_player_height_in_pixels,
player_heights[player_id]
)
output_ball_boxes.append({1:mini_court_player_position})
output_player_boxes.append(output_player_bboxes_dict)
return output_player_boxes , output_ball_boxes
def draw_points_on_mini_court(self,frames,postions, color=(0,255,0)):
for frame_num, frame in enumerate(frames):
for _, position in postions[frame_num].items():
x,y = position
x= int(x)
y= int(y)
cv2.circle(frame, (x,y), 5, color, -1)
return frames
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import UserMixin
from models import db # Import the shared db instance
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(150), nullable=False)
email = db.Column(db.String(150), unique=True, nullable=False)
password_hash = db.Column(db.String(200))
created_at = db.Column(db.DateTime, default=datetime.utcnow)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
def __repr__(self):
return f'<User {self.email}>'
File added
File added
File added
File added
File added
{
"players": {
"daniilMedvedev": {
"playerOverview": {
"fullName": "Daniil Medvedev",
"nationality": "Russian",
"dateOfBirth": "1996-02-11",
"height": 198,
"weight": 83,
"playingHand": "Right-handed",
"playingStyle": "Aggressive baseliner with a powerful serve",
"turnedProYear": 2014
},
"careerHighlights": {
"totalCareerTitles": 20,
"grandSlamWins": 1,
"olympicMedals": {
"total": 0,
"details": []
},
"careerPrizeMoney": 31785620,
"careerHighRanking": 1,
"currentRanking": 3,
"yearEndNo1Rankings": []
},
"headToHeadRecords": {
"totalMatchesPlayed": 15,
"winsByMedvedev": 9,
"winsByZverev": 6,
"winsBySurface": {
"hard": {
"medvedev": 8,
"zverev": 5
},
"clay": {
"medvedev": 1,
"zverev": 1
},
"grass": {
"medvedev": 0,
"zverev": 0
}
},
"historicalTrends": "Medvedev has a stronger record on hard courts, while Zverev has managed to win key matches on clay."
},
"performanceStatistics": {
"overallWinPercentage": 73.4,
"serviceStats": {
"aces": 5200,
"doubleFaults": 1200,
"firstServePercentage": 63,
"firstServePointsWon": 76,
"secondServePointsWon": 54
},
"returnStats": {
"firstServeReturnPointsWon": 34,
"secondServeReturnPointsWon": 52
},
"tiebreakRecord": "32-24"
},
"tournamentHistory": {
"grandSlamPerformances": {
"australianOpen": "Finalist (2021, 2022)",
"frenchOpen": "Quarterfinals (2021)",
"wimbledon": "Fourth Round (2021)",
"usOpen": "Winner (2021)"
},
"atpFinalsTitles": 1,
"mastersTitles": 6
},
"rankingProgression": [
{"year": 2016, "ranking": 99},
{"year": 2017, "ranking": 65},
{"year": 2018, "ranking": 16},
{"year": 2019, "ranking": 4},
{"year": 2020, "ranking": 4},
{"year": 2021, "ranking": 2},
{"year": 2022, "ranking": 1},
{"year": 2023, "ranking": 3}
],
"historicalTrends": {
"matchWinStreaks": 20,
"performanceAgainstTop10Players": 60.2,
"notableUpsets": "Defeated Novak Djokovic in the 2021 US Open final to win his first Grand Slam."
}
},
"alexanderZverev": {
"playerOverview": {
"fullName": "Alexander Zverev",
"nationality": "German",
"dateOfBirth": "1997-04-20",
"height": 198,
"weight": 90,
"playingHand": "Right-handed",
"playingStyle": "Aggressive baseline player with strong serve and forehand",
"turnedProYear": 2013
},
"careerHighlights": {
"totalCareerTitles": 19,
"grandSlamWins": 0,
"olympicMedals": {
"total": 1,
"details": ["Gold in singles at Tokyo 2020"]
},
"careerPrizeMoney": 30752000,
"careerHighRanking": 2,
"currentRanking": 7,
"yearEndNo1Rankings": []
},
"headToHeadRecords": {
"totalMatchesPlayed": 15,
"winsByMedvedev": 9,
"winsByZverev": 6,
"winsBySurface": {
"hard": {
"medvedev": 8,
"zverev": 5
},
"clay": {
"medvedev": 1,
"zverev": 1
},
"grass": {
"medvedev": 0,
"zverev": 0
}
},
"historicalTrends": "Zverev has had success against Medvedev on clay and in crucial matchups."
},
"performanceStatistics": {
"overallWinPercentage": 69.5,
"serviceStats": {
"aces": 4500,
"doubleFaults": 1600,
"firstServePercentage": 65,
"firstServePointsWon": 75,
"secondServePointsWon": 52
},
"returnStats": {
"firstServeReturnPointsWon": 33,
"secondServeReturnPointsWon": 51
},
"tiebreakRecord": "40-30"
},
"tournamentHistory": {
"grandSlamPerformances": {
"australianOpen": "Semifinalist (2020)",
"frenchOpen": "Semifinalist (2021, 2022)",
"wimbledon": "Fourth Round (2017)",
"usOpen": "Finalist (2020)"
},
"atpFinalsTitles": 2,
"mastersTitles": 5
},
"rankingProgression": [
{"year": 2016, "ranking": 24},
{"year": 2017, "ranking": 4},
{"year": 2018, "ranking": 4},
{"year": 2019, "ranking": 7},
{"year": 2020, "ranking": 7},
{"year": 2021, "ranking": 3},
{"year": 2022, "ranking": 4},
{"year": 2023, "ranking": 7}
],
"historicalTrends": {
"matchWinStreaks": 13,
"performanceAgainstTop10Players": 55.3,
"notableUpsets": "Defeated Novak Djokovic in the semifinals of Tokyo 2020, eventually winning the gold medal."
}
}
}
}
\ No newline at end of file
{
"players": {
"rafaelNadal": {
"playerOverview": {
"fullName": "Rafael Nadal Parera",
"nationality": "Spanish",
"dateOfBirth": "1986-06-03",
"height": 185,
"weight": 85,
"playingHand": "Left-handed",
"playingStyle": "Aggressive baseline player with heavy topspin",
"turnedProYear": 2001
},
"careerHighlights": {
"totalCareerTitles": 92,
"grandSlamWins": 22,
"olympicMedals": {
"total": 2,
"details": {
"goldSingles": 1,
"goldDoubles": 1,
"silver": 0,
"bronze": 0
}
},
"careerPrizeMoney": 134640000,
"careerHighRanking": 1,
"currentRanking": 3,
"yearEndNo1Rankings": [2008, 2010, 2013, 2017, 2019]
},
"headToHeadRecords": {
"totalMatchesPlayed": 6,
"winsByNadal": 5,
"winsByMedvedev": 1,
"winsBySurface": {
"hard": {
"nadal": 4,
"medvedev": 1
},
"clay": {
"nadal": 1,
"medvedev": 0
},
"grass": {
"nadal": 0,
"medvedev": 0
}
},
"historicalTrends": "Nadal has dominated their encounters, particularly on hard courts."
},
"performanceStatistics": {
"overallWinPercentage": 83.2,
"serviceStats": {
"aces": 3700,
"doubleFaults": 1600,
"firstServePercentage": 68,
"firstServePointsWon": 74,
"secondServePointsWon": 57
},
"returnStats": {
"firstServeReturnPointsWon": 34,
"secondServeReturnPointsWon": 56
},
"tiebreakRecord": {
"played": 320,
"won": 200
}
},
"tournamentHistory": {
"grandSlamPerformances": {
"australianOpen": "Winner (2009)",
"frenchOpen": "Winner (2005-2008, 2010-2014, 2017-2020, 2022)",
"wimbledon": "Winner (2008, 2010)",
"usOpen": "Winner (2010, 2013, 2017, 2019)"
},
"atpFinalsTitles": 0,
"mastersTitles": 36
},
"rankingProgression": [
{"year": 2005, "ranking": 2},
{"year": 2010, "ranking": 1},
{"year": 2015, "ranking": 5},
{"year": 2020, "ranking": 2}
],
"historicalTrends": {
"matchWinStreaks": 32,
"performanceAgainstTop10Players": 62.5,
"notableUpsets": "Defeated Roger Federer in the 2008 Wimbledon final."
}
},
"daniilMedvedev": {
"playerOverview": {
"fullName": "Daniil Sergeyevich Medvedev",
"nationality": "Russian",
"dateOfBirth": "1996-02-11",
"height": 198,
"weight": 83,
"playingHand": "Right-handed",
"playingStyle": "Counterpuncher with a strong backhand",
"turnedProYear": 2014
},
"careerHighlights": {
"totalCareerTitles": 20,
"grandSlamWins": 1,
"olympicMedals": {
"total": 0,
"details": {}
},
"careerPrizeMoney": 29564000,
"careerHighRanking": 1,
"currentRanking": 5,
"yearEndNo1Rankings": []
},
"headToHeadRecords": {
"totalMatchesPlayed": 6,
"winsByNadal": 5,
"winsByMedvedev": 1,
"winsBySurface": {
"hard": {
"nadal": 4,
"medvedev": 1
},
"clay": {
"nadal": 1,
"medvedev": 0
},
"grass": {
"nadal": 0,
"medvedev": 0
}
},
"historicalTrends": "Medvedev has struggled against Nadal, with his sole victory on hard court."
},
"performanceStatistics": {
"overallWinPercentage": 73.4,
"serviceStats": {
"aces": 4200,
"doubleFaults": 1600,
"firstServePercentage": 63,
"firstServePointsWon": 78,
"secondServePointsWon": 52
},
"returnStats": {
"firstServeReturnPointsWon": 31,
"secondServeReturnPointsWon": 50
},
"tiebreakRecord": {
"played": 210,
"won": 130
}
},
"tournamentHistory": {
"grandSlamPerformances": {
"australianOpen": "Finalist (2021, 2022)",
"frenchOpen": "Quarterfinalist (2021)",
"wimbledon": "Fourth Round (2021)",
"usOpen": "Winner (2021)"
},
"atpFinalsTitles": 1,
"mastersTitles": 6
},
"rankingProgression": [
{"year": 2019, "ranking": 4},
{"year": 2020, "ranking": 4},
{"year": 2021, "ranking": 2},
{"year": 2022, "ranking": 1}
],
"historicalTrends": {
"matchWinStreaks": 20,
"performanceAgainstTop10Players": 55.1,
"notableUpsets": "Defeated Novak Djokovic in the 2021 US Open final."
}
}
}
}
\ No newline at end of file
File suppressed by a .gitattributes entry or the file's encoding is unsupported.
scp -i "C:/Users/gauth/Desktop/courses/capstone/project-code/capstone-project-deploy/capstone-project-group-8-key.pem" .\capstone-project.zip azureuser@20.64.237.114:/home/azureuser/capstone-project/.
sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update
sudo apt install -y python3.11 python3.11-venv python3.11-dev
sudo update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.11 1
sudo update-alternatives --config python3
sudo apt install -y python3-pip
python3.11 -m venv myenv
source myenv/bin/activate
sudo apt install unzip
unzip capston-project.zip
pip install -r requirements.txt
sudo apt install postgresql postgresql-contrib
sudo service postgresql start
sudo -u postgres psql
CREATE DATABASE capstone_project;
CREATE USER postgres1 WITH PASSWORD 'Test1234!';
GRANT ALL PRIVILEGES ON DATABASE capstone_project TO postgres1;
sudo mysql -u root -p
CREATE DATABASE flask_app_db;
CREATE USER 'flask_user'@'localhost' IDENTIFIED BY 'Test1234!';
GRANT ALL PRIVILEGES ON flask_app_db.* TO 'flask_user'@'localhost';
pip install pymysql
pip install cryptography
export flask_app=app.py
sudo ufw allow 5434
sudo ufw allow 5000
sudo apt install mysql-server
sudo service mysql start
flask db init
flask db migrate -m "Initial migration"
flask db revision -m "Add users table"
op.create_table(
'users',
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('name', sa.String(length=150), nullable=False),
sa.Column('email', sa.String(length=150), nullable=False, unique=True),
sa.Column('password_hash', sa.String(length=200)),
sa.Column('created_at', sa.DateTime, nullable=True),
)
flask db upgrade
app.run(host='0.0.0.0', port=5000, debug=True)
sudo ufw allow 5000
sudo ufw enable
sudo apt install gunicorn nginx -y
sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /etc/ssl/private/nginx-selfsigned.key \
-out /etc/ssl/certs/nginx-selfsigned.crt
Country Name (2 letter code) [AU]: US
State or Province Name (full name) [Some-State]: Washington
Locality Name (eg, city) []: Seattle
Organization Name (eg, company) [Internet Widgits Pty Ltd]: MyCompany
Organizational Unit Name (eg, section) []: IT
Common Name (e.g. server FQDN or YOUR name) []: capstone-project-group8.westus2.cloudapp.azure.com
Email Address []: admin@mydomain.com
sudo vi /etc/nginx/sites-available/default
server {
listen 443 ssl;
server_name capstone-project-group8.westus2.cloudapp.azure.com;
ssl_certificate /etc/ssl/certs/nginx-selfsigned.crt;
ssl_certificate_key /etc/ssl/private/nginx-selfsigned.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
listen 80;
server_name capstone-project-group8.westus2.cloudapp.azure.com;
return 301 https://$host$request_uri;
}
sudo nginx -t
sudo systemctl restart nginx
https://capstone-project-group8.westus2.cloudapp.azure.com