feat: F1 stats 2025 - tabbed UI, backend Java, full season data
This commit is contained in:
commit
7b4c94c2c7
|
|
@ -0,0 +1,2 @@
|
|||
/mvnw text eol=lf
|
||||
*.cmd text eol=crlf
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
HELP.md
|
||||
target/
|
||||
.mvn/wrapper/maven-wrapper.jar
|
||||
!**/src/main/**/target/
|
||||
!**/src/test/**/target/
|
||||
|
||||
### STS ###
|
||||
.apt_generated
|
||||
.classpath
|
||||
.factorypath
|
||||
.project
|
||||
.settings
|
||||
.springBeans
|
||||
.sts4-cache
|
||||
|
||||
### IntelliJ IDEA ###
|
||||
.idea
|
||||
*.iws
|
||||
*.iml
|
||||
*.ipr
|
||||
|
||||
### NetBeans ###
|
||||
/nbproject/private/
|
||||
/nbbuild/
|
||||
/dist/
|
||||
/nbdist/
|
||||
/.nb-gradle/
|
||||
build/
|
||||
!**/src/main/**/build/
|
||||
!**/src/test/**/build/
|
||||
|
||||
### VS Code ###
|
||||
.vscode/
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
wrapperVersion=3.3.4
|
||||
distributionType=only-script
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.12/apache-maven-3.9.12-bin.zip
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
# Paso 1: Compilación (Usamos Maven)
|
||||
FROM maven:3.9-eclipse-temurin-17 AS build
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
RUN mvn clean package -DskipTests
|
||||
|
||||
# Paso 2: Ejecución (Imagen ligera)
|
||||
FROM eclipse-temurin:17-jre-jammy
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/target/*.jar app.jar
|
||||
EXPOSE 8080
|
||||
ENTRYPOINT ["java", "-jar", "app.jar"]
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance
|
||||
# with the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing,
|
||||
# software distributed under the License is distributed on an
|
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
# KIND, either express or implied. See the License for the
|
||||
# specific language governing permissions and limitations
|
||||
# under the License.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Apache Maven Wrapper startup batch script, version 3.3.4
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# JAVA_HOME - location of a JDK home dir, required when download maven via java source
|
||||
# MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
set -euf
|
||||
[ "${MVNW_VERBOSE-}" != debug ] || set -x
|
||||
|
||||
# OS specific support.
|
||||
native_path() { printf %s\\n "$1"; }
|
||||
case "$(uname)" in
|
||||
CYGWIN* | MINGW*)
|
||||
[ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")"
|
||||
native_path() { cygpath --path --windows "$1"; }
|
||||
;;
|
||||
esac
|
||||
|
||||
# set JAVACMD and JAVACCMD
|
||||
set_java_home() {
|
||||
# For Cygwin and MinGW, ensure paths are in Unix format before anything is touched
|
||||
if [ -n "${JAVA_HOME-}" ]; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ]; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
JAVACCMD="$JAVA_HOME/jre/sh/javac"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
JAVACCMD="$JAVA_HOME/bin/javac"
|
||||
|
||||
if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then
|
||||
echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2
|
||||
echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
JAVACMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v java
|
||||
)" || :
|
||||
JAVACCMD="$(
|
||||
'set' +e
|
||||
'unset' -f command 2>/dev/null
|
||||
'command' -v javac
|
||||
)" || :
|
||||
|
||||
if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then
|
||||
echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# hash string like Java String::hashCode
|
||||
hash_string() {
|
||||
str="${1:-}" h=0
|
||||
while [ -n "$str" ]; do
|
||||
char="${str%"${str#?}"}"
|
||||
h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296))
|
||||
str="${str#?}"
|
||||
done
|
||||
printf %x\\n $h
|
||||
}
|
||||
|
||||
verbose() { :; }
|
||||
[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; }
|
||||
|
||||
die() {
|
||||
printf %s\\n "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
trim() {
|
||||
# MWRAPPER-139:
|
||||
# Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds.
|
||||
# Needed for removing poorly interpreted newline sequences when running in more
|
||||
# exotic environments such as mingw bash on Windows.
|
||||
printf "%s" "${1}" | tr -d '[:space:]'
|
||||
}
|
||||
|
||||
scriptDir="$(dirname "$0")"
|
||||
scriptName="$(basename "$0")"
|
||||
|
||||
# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties
|
||||
while IFS="=" read -r key value; do
|
||||
case "${key-}" in
|
||||
distributionUrl) distributionUrl=$(trim "${value-}") ;;
|
||||
distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;;
|
||||
esac
|
||||
done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
|
||||
case "${distributionUrl##*/}" in
|
||||
maven-mvnd-*bin.*)
|
||||
MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/
|
||||
case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in
|
||||
*AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;;
|
||||
:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;
|
||||
:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;
|
||||
:Linux*x86_64*) distributionPlatform=linux-amd64 ;;
|
||||
*)
|
||||
echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2
|
||||
distributionPlatform=linux-amd64
|
||||
;;
|
||||
esac
|
||||
distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip"
|
||||
;;
|
||||
maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;
|
||||
*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;
|
||||
esac
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}"
|
||||
distributionUrlName="${distributionUrl##*/}"
|
||||
distributionUrlNameMain="${distributionUrlName%.*}"
|
||||
distributionUrlNameMain="${distributionUrlNameMain%-bin}"
|
||||
MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}"
|
||||
MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")"
|
||||
|
||||
exec_maven() {
|
||||
unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :
|
||||
exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD"
|
||||
}
|
||||
|
||||
if [ -d "$MAVEN_HOME" ]; then
|
||||
verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
exec_maven "$@"
|
||||
fi
|
||||
|
||||
case "${distributionUrl-}" in
|
||||
*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;;
|
||||
*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;;
|
||||
esac
|
||||
|
||||
# prepare tmp dir
|
||||
if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then
|
||||
clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; }
|
||||
trap clean HUP INT TERM EXIT
|
||||
else
|
||||
die "cannot create temp dir"
|
||||
fi
|
||||
|
||||
mkdir -p -- "${MAVEN_HOME%/*}"
|
||||
|
||||
# Download and Install Apache Maven
|
||||
verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
verbose "Downloading from: $distributionUrl"
|
||||
verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
# select .zip or .tar.gz
|
||||
if ! command -v unzip >/dev/null; then
|
||||
distributionUrl="${distributionUrl%.zip}.tar.gz"
|
||||
distributionUrlName="${distributionUrl##*/}"
|
||||
fi
|
||||
|
||||
# verbose opt
|
||||
__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''
|
||||
[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v
|
||||
|
||||
# normalize http auth
|
||||
case "${MVNW_PASSWORD:+has-password}" in
|
||||
'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||
has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;
|
||||
esac
|
||||
|
||||
if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then
|
||||
verbose "Found wget ... using wget"
|
||||
wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl"
|
||||
elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then
|
||||
verbose "Found curl ... using curl"
|
||||
curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl"
|
||||
elif set_java_home; then
|
||||
verbose "Falling back to use Java to download"
|
||||
javaSource="$TMP_DOWNLOAD_DIR/Downloader.java"
|
||||
targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
cat >"$javaSource" <<-END
|
||||
public class Downloader extends java.net.Authenticator
|
||||
{
|
||||
protected java.net.PasswordAuthentication getPasswordAuthentication()
|
||||
{
|
||||
return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() );
|
||||
}
|
||||
public static void main( String[] args ) throws Exception
|
||||
{
|
||||
setDefault( new Downloader() );
|
||||
java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );
|
||||
}
|
||||
}
|
||||
END
|
||||
# For Cygwin/MinGW, switch paths to Windows format before running javac and java
|
||||
verbose " - Compiling Downloader.java ..."
|
||||
"$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java"
|
||||
verbose " - Running Downloader.java ..."
|
||||
"$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")"
|
||||
fi
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
if [ -n "${distributionSha256Sum-}" ]; then
|
||||
distributionSha256Result=false
|
||||
if [ "$MVN_CMD" = mvnd.sh ]; then
|
||||
echo "Checksum validation is not supported for maven-mvnd." >&2
|
||||
echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
elif command -v sha256sum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
elif command -v shasum >/dev/null; then
|
||||
if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then
|
||||
distributionSha256Result=true
|
||||
fi
|
||||
else
|
||||
echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2
|
||||
echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ $distributionSha256Result = false ]; then
|
||||
echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2
|
||||
echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# unzip and move
|
||||
if command -v unzip >/dev/null; then
|
||||
unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip"
|
||||
else
|
||||
tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar"
|
||||
fi
|
||||
|
||||
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
||||
actualDistributionDir=""
|
||||
|
||||
# First try the expected directory name (for regular distributions)
|
||||
if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then
|
||||
if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then
|
||||
actualDistributionDir="$distributionUrlNameMain"
|
||||
fi
|
||||
fi
|
||||
|
||||
# If not found, search for any directory with the Maven executable (for snapshots)
|
||||
if [ -z "$actualDistributionDir" ]; then
|
||||
# enable globbing to iterate over items
|
||||
set +f
|
||||
for dir in "$TMP_DOWNLOAD_DIR"/*; do
|
||||
if [ -d "$dir" ]; then
|
||||
if [ -f "$dir/bin/$MVN_CMD" ]; then
|
||||
actualDistributionDir="$(basename "$dir")"
|
||||
break
|
||||
fi
|
||||
fi
|
||||
done
|
||||
set -f
|
||||
fi
|
||||
|
||||
if [ -z "$actualDistributionDir" ]; then
|
||||
verbose "Contents of $TMP_DOWNLOAD_DIR:"
|
||||
verbose "$(ls -la "$TMP_DOWNLOAD_DIR")"
|
||||
die "Could not find Maven distribution directory in extracted archive"
|
||||
fi
|
||||
|
||||
verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
||||
printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url"
|
||||
mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME"
|
||||
|
||||
clean || :
|
||||
exec_maven "$@"
|
||||
|
|
@ -0,0 +1,189 @@
|
|||
<# : batch portion
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Apache Maven Wrapper startup batch script, version 3.3.4
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM MVNW_REPOURL - repo url base for downloading maven distribution
|
||||
@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven
|
||||
@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0)
|
||||
@SET __MVNW_CMD__=
|
||||
@SET __MVNW_ERROR__=
|
||||
@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%
|
||||
@SET PSModulePath=
|
||||
@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @(
|
||||
IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B)
|
||||
)
|
||||
@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%
|
||||
@SET __MVNW_PSMODULEP_SAVE=
|
||||
@SET __MVNW_ARG0_NAME__=
|
||||
@SET MVNW_USERNAME=
|
||||
@SET MVNW_PASSWORD=
|
||||
@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*)
|
||||
@echo Cannot start maven from wrapper >&2 && exit /b 1
|
||||
@GOTO :EOF
|
||||
: end batch / begin powershell #>
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
if ($env:MVNW_VERBOSE -eq "true") {
|
||||
$VerbosePreference = "Continue"
|
||||
}
|
||||
|
||||
# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties
|
||||
$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl
|
||||
if (!$distributionUrl) {
|
||||
Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties"
|
||||
}
|
||||
|
||||
switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {
|
||||
"maven-mvnd-*" {
|
||||
$USE_MVND = $true
|
||||
$distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip"
|
||||
$MVN_CMD = "mvnd.cmd"
|
||||
break
|
||||
}
|
||||
default {
|
||||
$USE_MVND = $false
|
||||
$MVN_CMD = $script -replace '^mvnw','mvn'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
# apply MVNW_REPOURL and calculate MAVEN_HOME
|
||||
# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>
|
||||
if ($env:MVNW_REPOURL) {
|
||||
$MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" }
|
||||
$distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')"
|
||||
}
|
||||
$distributionUrlName = $distributionUrl -replace '^.*/',''
|
||||
$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$',''
|
||||
|
||||
$MAVEN_M2_PATH = "$HOME/.m2"
|
||||
if ($env:MAVEN_USER_HOME) {
|
||||
$MAVEN_M2_PATH = "$env:MAVEN_USER_HOME"
|
||||
}
|
||||
|
||||
if (-not (Test-Path -Path $MAVEN_M2_PATH)) {
|
||||
New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null
|
||||
}
|
||||
|
||||
$MAVEN_WRAPPER_DISTS = $null
|
||||
if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) {
|
||||
$MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists"
|
||||
} else {
|
||||
$MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists"
|
||||
}
|
||||
|
||||
$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain"
|
||||
$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join ''
|
||||
$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME"
|
||||
|
||||
if (Test-Path -Path "$MAVEN_HOME" -PathType Container) {
|
||||
Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME"
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
exit $?
|
||||
}
|
||||
|
||||
if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {
|
||||
Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl"
|
||||
}
|
||||
|
||||
# prepare tmp dir
|
||||
$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile
|
||||
$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir"
|
||||
$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null
|
||||
trap {
|
||||
if ($TMP_DOWNLOAD_DIR.Exists) {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
}
|
||||
|
||||
New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null
|
||||
|
||||
# Download and Install Apache Maven
|
||||
Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..."
|
||||
Write-Verbose "Downloading from: $distributionUrl"
|
||||
Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName"
|
||||
|
||||
$webclient = New-Object System.Net.WebClient
|
||||
if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {
|
||||
$webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)
|
||||
}
|
||||
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
|
||||
$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null
|
||||
|
||||
# If specified, validate the SHA-256 sum of the Maven distribution zip file
|
||||
$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum
|
||||
if ($distributionSha256Sum) {
|
||||
if ($USE_MVND) {
|
||||
Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties."
|
||||
}
|
||||
Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash
|
||||
if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {
|
||||
Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property."
|
||||
}
|
||||
}
|
||||
|
||||
# unzip and move
|
||||
Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null
|
||||
|
||||
# Find the actual extracted directory name (handles snapshots where filename != directory name)
|
||||
$actualDistributionDir = ""
|
||||
|
||||
# First try the expected directory name (for regular distributions)
|
||||
$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain"
|
||||
$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD"
|
||||
if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) {
|
||||
$actualDistributionDir = $distributionUrlNameMain
|
||||
}
|
||||
|
||||
# If not found, search for any directory with the Maven executable (for snapshots)
|
||||
if (!$actualDistributionDir) {
|
||||
Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object {
|
||||
$testPath = Join-Path $_.FullName "bin/$MVN_CMD"
|
||||
if (Test-Path -Path $testPath -PathType Leaf) {
|
||||
$actualDistributionDir = $_.Name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$actualDistributionDir) {
|
||||
Write-Error "Could not find Maven distribution directory in extracted archive"
|
||||
}
|
||||
|
||||
Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir"
|
||||
Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null
|
||||
try {
|
||||
Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null
|
||||
} catch {
|
||||
if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) {
|
||||
Write-Error "fail to move MAVEN_HOME"
|
||||
}
|
||||
} finally {
|
||||
try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }
|
||||
catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" }
|
||||
}
|
||||
|
||||
Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD"
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>4.0.3</version>
|
||||
<relativePath/> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<groupId>es.tatvil</groupId>
|
||||
<artifactId>formula1</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<name>formula1</name>
|
||||
<description>backed de datos de formula 1</description>
|
||||
<url/>
|
||||
<licenses>
|
||||
<license/>
|
||||
</licenses>
|
||||
<developers>
|
||||
<developer/>
|
||||
</developers>
|
||||
<scm>
|
||||
<connection/>
|
||||
<developerConnection/>
|
||||
<tag/>
|
||||
<url/>
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-devtools</artifactId>
|
||||
<scope>runtime</scope>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.mariadb.jdbc</groupId>
|
||||
<artifactId>mariadb-java-client</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webmvc-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
Manifest-Version: 1.0
|
||||
Class-Path:
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package es.tatvil.formula1;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class Formula1Application {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Formula1Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package es.tatvil.formula1.controller;
|
||||
|
||||
import es.tatvil.formula1.model.Escuderia;
|
||||
import es.tatvil.formula1.model.Piloto;
|
||||
import es.tatvil.formula1.model.PilotoEscuderia;
|
||||
import es.tatvil.formula1.repository.PilotoEscuderiaRepository;
|
||||
import es.tatvil.formula1.service.EscuderiaService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/escuderias")
|
||||
public class EscuderiaController {
|
||||
|
||||
private final EscuderiaService escuderiaService;
|
||||
private final PilotoEscuderiaRepository pilotoEscuderiaRepository;
|
||||
|
||||
public EscuderiaController(EscuderiaService escuderiaService,
|
||||
PilotoEscuderiaRepository pilotoEscuderiaRepository) {
|
||||
this.escuderiaService = escuderiaService;
|
||||
this.pilotoEscuderiaRepository = pilotoEscuderiaRepository;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<Escuderia> listar() {
|
||||
return escuderiaService.obtenerTodos();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/pilotos")
|
||||
public List<Piloto> getPilotosPorEscuderia(@PathVariable Integer id) {
|
||||
return pilotoEscuderiaRepository.findByEscuderiaId(id)
|
||||
.stream()
|
||||
.map(PilotoEscuderia::getPiloto)
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
package es.tatvil.formula1.controller;
|
||||
|
||||
import es.tatvil.formula1.model.GranPremio;
|
||||
import es.tatvil.formula1.service.GranPremioService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/gp")
|
||||
public class GranPremioController {
|
||||
|
||||
private final GranPremioService granPremioService;
|
||||
|
||||
public GranPremioController(GranPremioService granPremioService) {
|
||||
this.granPremioService = granPremioService;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<GranPremio> listar(@RequestParam(defaultValue = "2025") Integer temporada) {
|
||||
return granPremioService.obtenerPorTemporada(temporada);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public GranPremio obtener(@PathVariable Integer id) {
|
||||
return granPremioService.obtenerPorId(id);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package es.tatvil.formula1.controller;
|
||||
|
||||
import es.tatvil.formula1.model.Escuderia;
|
||||
import es.tatvil.formula1.model.Piloto;
|
||||
import es.tatvil.formula1.model.PilotoEscuderia;
|
||||
import es.tatvil.formula1.repository.PilotoEscuderiaRepository;
|
||||
import es.tatvil.formula1.service.PilotoService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/pilotos")
|
||||
public class PilotoController {
|
||||
|
||||
private final PilotoService pilotoService;
|
||||
private final PilotoEscuderiaRepository pilotoEscuderiaRepository;
|
||||
|
||||
public PilotoController(PilotoService pilotoService,
|
||||
PilotoEscuderiaRepository pilotoEscuderiaRepository) {
|
||||
this.pilotoService = pilotoService;
|
||||
this.pilotoEscuderiaRepository = pilotoEscuderiaRepository;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public List<Piloto> listar() {
|
||||
return pilotoService.obtenerTodos();
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/escuderias")
|
||||
public List<Escuderia> getEscuderiasPorPiloto(@PathVariable Integer id) {
|
||||
return pilotoEscuderiaRepository.findByPilotoId(id)
|
||||
.stream()
|
||||
.map(PilotoEscuderia::getEscuderia) // getter público
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package es.tatvil.formula1.controller;
|
||||
|
||||
import es.tatvil.formula1.model.Resultado;
|
||||
import es.tatvil.formula1.service.ResultadoService;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/resultados")
|
||||
public class ResultadoController {
|
||||
|
||||
private final ResultadoService resultadoService;
|
||||
|
||||
public ResultadoController(ResultadoService resultadoService) {
|
||||
this.resultadoService = resultadoService;
|
||||
}
|
||||
|
||||
@GetMapping("/gp/{gpId}")
|
||||
public List<Resultado> porGranPremio(@PathVariable Integer gpId) {
|
||||
return resultadoService.obtenerPorGranPremio(gpId);
|
||||
}
|
||||
|
||||
@GetMapping("/piloto/{pilotoId}")
|
||||
public List<Resultado> porPiloto(@PathVariable Integer pilotoId) {
|
||||
return resultadoService.obtenerPorPiloto(pilotoId);
|
||||
}
|
||||
|
||||
@GetMapping("/clasificacion/pilotos")
|
||||
public List<Map<String, Object>> clasificacionPilotos(
|
||||
@RequestParam(defaultValue = "2025") Integer temporada) {
|
||||
return resultadoService.clasificacionPilotos(temporada);
|
||||
}
|
||||
|
||||
@GetMapping("/clasificacion/constructores")
|
||||
public List<Map<String, Object>> clasificacionConstructores(
|
||||
@RequestParam(defaultValue = "2025") Integer temporada) {
|
||||
return resultadoService.clasificacionConstructores(temporada);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package es.tatvil.formula1.model;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "escuderias")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Escuderia {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
private String nombre;
|
||||
private String pais;
|
||||
private String motor;
|
||||
|
||||
@OneToMany(mappedBy = "escuderia")
|
||||
private List<PilotoEscuderia> pilotosEscuderia;
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package es.tatvil.formula1.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = "grandes_premios")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class GranPremio {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
private String nombre;
|
||||
private String circuito;
|
||||
private String ciudad;
|
||||
private String pais;
|
||||
private Integer temporada;
|
||||
private LocalDate fecha;
|
||||
|
||||
@Column(name = "num_vueltas")
|
||||
private Integer numVueltas;
|
||||
|
||||
@Column(name = "distancia_km")
|
||||
private Double distanciaKm;
|
||||
|
||||
@JsonIgnore
|
||||
@OneToMany(mappedBy = "granPremio", fetch = FetchType.LAZY)
|
||||
private List<Resultado> resultados;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package es.tatvil.formula1.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@Table(name = "pilotos")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Piloto {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
private String nombre;
|
||||
private String apellido;
|
||||
|
||||
@Column(name = "fecha_nacimiento")
|
||||
private LocalDate fechaNacimiento;
|
||||
|
||||
private String nacionalidad;
|
||||
private Integer numero;
|
||||
private String codigo;
|
||||
|
||||
@OneToMany(mappedBy = "piloto")
|
||||
private List<PilotoEscuderia> pilotosEscuderia;
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package es.tatvil.formula1.model;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
@Entity
|
||||
public class PilotoEscuderia {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
@ManyToOne
|
||||
private Piloto piloto;
|
||||
|
||||
@ManyToOne
|
||||
private Escuderia escuderia;
|
||||
|
||||
// GETTER para Piloto
|
||||
public Piloto getPiloto() {
|
||||
return piloto;
|
||||
}
|
||||
|
||||
// SETTER opcional
|
||||
public void setPiloto(Piloto piloto) {
|
||||
this.piloto = piloto;
|
||||
}
|
||||
|
||||
// GETTER para Escuderia
|
||||
public Escuderia getEscuderia() {
|
||||
return escuderia;
|
||||
}
|
||||
|
||||
// SETTER opcional
|
||||
public void setEscuderia(Escuderia escuderia) {
|
||||
this.escuderia = escuderia;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package es.tatvil.formula1.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
@Entity
|
||||
@Table(name = "resultados")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Resultado {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Integer id;
|
||||
|
||||
@JsonIgnore
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "gran_premio_id")
|
||||
private GranPremio granPremio;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "piloto_id")
|
||||
private Piloto piloto;
|
||||
|
||||
@ManyToOne(fetch = FetchType.EAGER)
|
||||
@JoinColumn(name = "escuderia_id")
|
||||
private Escuderia escuderia;
|
||||
|
||||
private Integer posicion;
|
||||
|
||||
@Column(name = "puntos", precision = 4, scale = 1)
|
||||
private Double puntos;
|
||||
|
||||
@Column(name = "vuelta_rapida")
|
||||
private Boolean vueltaRapida;
|
||||
|
||||
// FINALIZADO, DNF, DNS, DSQ
|
||||
private String estado;
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package es.tatvil.formula1.repository;
|
||||
|
||||
import es.tatvil.formula1.model.Escuderia;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface EscuderiaRepository extends JpaRepository<Escuderia, Integer> {
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package es.tatvil.formula1.repository;
|
||||
|
||||
import es.tatvil.formula1.model.GranPremio;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.List;
|
||||
|
||||
public interface GranPremioRepository extends JpaRepository<GranPremio, Integer> {
|
||||
List<GranPremio> findByTemporadaOrderByFechaAsc(Integer temporada);
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package es.tatvil.formula1.repository;
|
||||
|
||||
import es.tatvil.formula1.model.PilotoEscuderia;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import java.util.List;
|
||||
|
||||
public interface PilotoEscuderiaRepository extends JpaRepository<PilotoEscuderia, Integer> {
|
||||
|
||||
List<PilotoEscuderia> findByEscuderiaId(Integer escuderiaId);
|
||||
List<PilotoEscuderia> findByPilotoId(Integer pilotoId);
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package es.tatvil.formula1.repository;
|
||||
|
||||
import es.tatvil.formula1.model.Piloto;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
|
||||
public interface PilotoRepository extends JpaRepository<Piloto, Integer> {
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package es.tatvil.formula1.repository;
|
||||
|
||||
import es.tatvil.formula1.model.Resultado;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ResultadoRepository extends JpaRepository<Resultado, Integer> {
|
||||
|
||||
List<Resultado> findByGranPremioIdOrderByPosicionAsc(Integer granPremioId);
|
||||
|
||||
List<Resultado> findByPilotoId(Integer pilotoId);
|
||||
|
||||
@Query("""
|
||||
SELECT r.piloto.id, r.piloto.nombre, r.piloto.apellido,
|
||||
r.escuderia.nombre,
|
||||
SUM(r.puntos) AS puntos,
|
||||
SUM(CASE WHEN r.posicion = 1 THEN 1 ELSE 0 END) AS victorias,
|
||||
SUM(CASE WHEN r.posicion <= 3 THEN 1 ELSE 0 END) AS podios,
|
||||
SUM(CASE WHEN r.vueltaRapida = TRUE THEN 1 ELSE 0 END) AS vueltasRapidas
|
||||
FROM Resultado r
|
||||
WHERE r.granPremio.temporada = :temporada
|
||||
GROUP BY r.piloto.id, r.piloto.nombre, r.piloto.apellido, r.escuderia.nombre
|
||||
ORDER BY puntos DESC
|
||||
""")
|
||||
List<Object[]> clasificacionPilotos(@Param("temporada") Integer temporada);
|
||||
|
||||
@Query("""
|
||||
SELECT r.escuderia.id, r.escuderia.nombre,
|
||||
SUM(r.puntos) AS puntos,
|
||||
SUM(CASE WHEN r.posicion = 1 THEN 1 ELSE 0 END) AS victorias,
|
||||
SUM(CASE WHEN r.posicion <= 3 THEN 1 ELSE 0 END) AS podios
|
||||
FROM Resultado r
|
||||
WHERE r.granPremio.temporada = :temporada
|
||||
GROUP BY r.escuderia.id, r.escuderia.nombre
|
||||
ORDER BY puntos DESC
|
||||
""")
|
||||
List<Object[]> clasificacionConstructores(@Param("temporada") Integer temporada);
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package es.tatvil.formula1.service;
|
||||
|
||||
import es.tatvil.formula1.model.Escuderia;
|
||||
import es.tatvil.formula1.repository.EscuderiaRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class EscuderiaService {
|
||||
|
||||
private final EscuderiaRepository escuderiaRepository;
|
||||
|
||||
public EscuderiaService(EscuderiaRepository escuderiaRepository) {
|
||||
this.escuderiaRepository = escuderiaRepository;
|
||||
}
|
||||
|
||||
public List<Escuderia> obtenerTodos() {
|
||||
return escuderiaRepository.findAll();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package es.tatvil.formula1.service;
|
||||
|
||||
import es.tatvil.formula1.model.GranPremio;
|
||||
import es.tatvil.formula1.repository.GranPremioRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class GranPremioService {
|
||||
|
||||
private final GranPremioRepository granPremioRepository;
|
||||
|
||||
public GranPremioService(GranPremioRepository granPremioRepository) {
|
||||
this.granPremioRepository = granPremioRepository;
|
||||
}
|
||||
|
||||
public List<GranPremio> obtenerPorTemporada(Integer temporada) {
|
||||
return granPremioRepository.findByTemporadaOrderByFechaAsc(temporada);
|
||||
}
|
||||
|
||||
public GranPremio obtenerPorId(Integer id) {
|
||||
return granPremioRepository.findById(id).orElseThrow();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package es.tatvil.formula1.service;
|
||||
|
||||
import es.tatvil.formula1.model.Piloto;
|
||||
import es.tatvil.formula1.repository.PilotoRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Service
|
||||
public class PilotoService {
|
||||
|
||||
private final PilotoRepository pilotoRepository;
|
||||
|
||||
public PilotoService(PilotoRepository pilotoRepository) {
|
||||
this.pilotoRepository = pilotoRepository;
|
||||
}
|
||||
|
||||
public List<Piloto> obtenerTodos() {
|
||||
return pilotoRepository.findAll();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package es.tatvil.formula1.service;
|
||||
|
||||
import es.tatvil.formula1.model.Resultado;
|
||||
import es.tatvil.formula1.repository.ResultadoRepository;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class ResultadoService {
|
||||
|
||||
private final ResultadoRepository resultadoRepository;
|
||||
|
||||
public ResultadoService(ResultadoRepository resultadoRepository) {
|
||||
this.resultadoRepository = resultadoRepository;
|
||||
}
|
||||
|
||||
public List<Resultado> obtenerPorGranPremio(Integer gpId) {
|
||||
return resultadoRepository.findByGranPremioIdOrderByPosicionAsc(gpId);
|
||||
}
|
||||
|
||||
public List<Resultado> obtenerPorPiloto(Integer pilotoId) {
|
||||
return resultadoRepository.findByPilotoId(pilotoId);
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> clasificacionPilotos(Integer temporada) {
|
||||
return resultadoRepository.clasificacionPilotos(temporada).stream().map(row -> Map.of(
|
||||
"pilotoId", row[0],
|
||||
"nombre", row[1],
|
||||
"apellido", row[2],
|
||||
"equipo", row[3],
|
||||
"puntos", row[4],
|
||||
"victorias", row[5],
|
||||
"podios", row[6],
|
||||
"vueltasRapidas",row[7]
|
||||
)).toList();
|
||||
}
|
||||
|
||||
public List<Map<String, Object>> clasificacionConstructores(Integer temporada) {
|
||||
return resultadoRepository.clasificacionConstructores(temporada).stream().map(row -> Map.of(
|
||||
"escuderiaId", row[0],
|
||||
"nombre", row[1],
|
||||
"puntos", row[2],
|
||||
"victorias", row[3],
|
||||
"podios", row[4]
|
||||
)).toList();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
spring.application.name=formula1
|
||||
|
||||
server.servlet.context-path=/f1
|
||||
|
||||
spring.datasource.url=jdbc:mariadb://bbdd:3306/formula1
|
||||
spring.datasource.username=formula1user
|
||||
spring.datasource.password=Eavne,e1m
|
||||
|
||||
spring.datasource.driver-class-name=org.mariadb.jdbc.Driver
|
||||
|
||||
spring.jpa.hibernate.ddl-auto=none
|
||||
spring.jpa.show-sql=false
|
||||
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MariaDBDialect
|
||||
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
|
||||
|
|
@ -0,0 +1,429 @@
|
|||
-- ============================================================
|
||||
-- FÓRMULA 1 — SCHEMA + DATOS TEMPORADA 2025
|
||||
-- Ejecutar como: mysql -u root -p formula1 < formula1_schema.sql
|
||||
-- ============================================================
|
||||
|
||||
CREATE DATABASE IF NOT EXISTS formula1 CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
USE formula1;
|
||||
|
||||
-- ─── ESCUDERÍAS ──────────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS escuderias (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
nombre VARCHAR(100) NOT NULL,
|
||||
pais VARCHAR(50),
|
||||
motor VARCHAR(50)
|
||||
);
|
||||
|
||||
-- ─── PILOTOS ─────────────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS pilotos (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
nombre VARCHAR(50) NOT NULL,
|
||||
apellido VARCHAR(50) NOT NULL,
|
||||
fecha_nacimiento DATE,
|
||||
nacionalidad VARCHAR(50),
|
||||
numero INT,
|
||||
codigo CHAR(3)
|
||||
);
|
||||
|
||||
-- ─── PILOTO_ESCUDERIA ─────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS piloto_escuderia (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
piloto_id INT,
|
||||
escuderia_id INT,
|
||||
FOREIGN KEY (piloto_id) REFERENCES pilotos(id),
|
||||
FOREIGN KEY (escuderia_id) REFERENCES escuderias(id)
|
||||
);
|
||||
|
||||
-- ─── GRANDES PREMIOS ─────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS grandes_premios (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
nombre VARCHAR(100) NOT NULL,
|
||||
circuito VARCHAR(100),
|
||||
ciudad VARCHAR(50),
|
||||
pais VARCHAR(50),
|
||||
temporada INT NOT NULL,
|
||||
fecha DATE,
|
||||
num_vueltas INT,
|
||||
distancia_km DECIMAL(6,2)
|
||||
);
|
||||
|
||||
-- ─── RESULTADOS ──────────────────────────────────────────────
|
||||
CREATE TABLE IF NOT EXISTS resultados (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
gran_premio_id INT NOT NULL,
|
||||
piloto_id INT NOT NULL,
|
||||
escuderia_id INT NOT NULL,
|
||||
posicion INT,
|
||||
puntos DECIMAL(4,1) DEFAULT 0,
|
||||
vuelta_rapida TINYINT(1) DEFAULT 0,
|
||||
estado VARCHAR(20) DEFAULT 'FINALIZADO',
|
||||
FOREIGN KEY (gran_premio_id) REFERENCES grandes_premios(id),
|
||||
FOREIGN KEY (piloto_id) REFERENCES pilotos(id),
|
||||
FOREIGN KEY (escuderia_id) REFERENCES escuderias(id)
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- DATOS — ESCUDERÍAS 2025
|
||||
-- ============================================================
|
||||
INSERT INTO escuderias (id, nombre, pais, motor) VALUES
|
||||
(1, 'McLaren', 'Reino Unido', 'Mercedes'),
|
||||
(2, 'Ferrari', 'Italia', 'Ferrari'),
|
||||
(3, 'Red Bull Racing','Austria', 'Honda RBPT'),
|
||||
(4, 'Mercedes', 'Reino Unido', 'Mercedes'),
|
||||
(5, 'Aston Martin', 'Reino Unido', 'Mercedes'),
|
||||
(6, 'Alpine', 'Francia', 'Renault'),
|
||||
(7, 'Williams', 'Reino Unido', 'Mercedes'),
|
||||
(8, 'Racing Bulls', 'Italia', 'Honda RBPT'),
|
||||
(9, 'Haas', 'Estados Unidos','Ferrari'),
|
||||
(10, 'Sauber', 'Suiza', 'Ferrari');
|
||||
|
||||
-- ============================================================
|
||||
-- DATOS — PILOTOS 2025
|
||||
-- ============================================================
|
||||
INSERT INTO pilotos (id, nombre, apellido, fecha_nacimiento, nacionalidad, numero, codigo) VALUES
|
||||
(1, 'Max', 'Verstappen', '1997-09-30', 'Holandés', 1, 'VER'),
|
||||
(2, 'Liam', 'Lawson', '2002-02-11', 'Neozelandés', 30, 'LAW'),
|
||||
(3, 'Lewis', 'Hamilton', '1985-01-07', 'Británico', 44, 'HAM'),
|
||||
(4, 'Charles', 'Leclerc', '1997-10-16', 'Monegasco', 16, 'LEC'),
|
||||
(5, 'George', 'Russell', '1998-02-15', 'Británico', 63, 'RUS'),
|
||||
(6, 'Kimi', 'Antonelli', '2006-08-25', 'Italiano', 12, 'ANT'),
|
||||
(7, 'Lando', 'Norris', '1999-11-13', 'Británico', 4, 'NOR'),
|
||||
(8, 'Oscar', 'Piastri', '2001-04-06', 'Australiano', 81, 'PIA'),
|
||||
(9, 'Fernando', 'Alonso', '1981-07-29', 'Español', 14, 'ALO'),
|
||||
(10, 'Lance', 'Stroll', '1998-10-29', 'Canadiense', 18, 'STR'),
|
||||
(11, 'Pierre', 'Gasly', '1996-02-07', 'Francés', 10, 'GAS'),
|
||||
(12, 'Jack', 'Doohan', '2003-01-20', 'Australiano', 7, 'DOO'),
|
||||
(13, 'Alex', 'Albon', '1996-03-23', 'Tailandés', 23, 'ALB'),
|
||||
(14, 'Carlos', 'Sainz', '1994-09-01', 'Español', 55, 'SAI'),
|
||||
(15, 'Yuki', 'Tsunoda', '2000-05-11', 'Japonés', 22, 'TSU'),
|
||||
(16, 'Isack', 'Hadjar', '2004-09-28', 'Francés', 6, 'HAD'),
|
||||
(17, 'Esteban', 'Ocon', '1996-09-17', 'Francés', 31, 'OCO'),
|
||||
(18, 'Oliver', 'Bearman', '2005-05-08', 'Británico', 87, 'BEA'),
|
||||
(19, 'Nico', 'Hülkenberg', '1987-08-19', 'Alemán', 27, 'HUL'),
|
||||
(20, 'Gabriel', 'Bortoleto', '2004-10-14', 'Brasileño', 5, 'BOR');
|
||||
|
||||
-- ─── RELACIONES PILOTO-ESCUDERÍA 2025 ─────────────────────────
|
||||
INSERT INTO piloto_escuderia (piloto_id, escuderia_id) VALUES
|
||||
(1,3),(2,3), -- Red Bull: Verstappen, Lawson
|
||||
(3,2),(4,2), -- Ferrari: Hamilton, Leclerc
|
||||
(5,4),(6,4), -- Mercedes: Russell, Antonelli
|
||||
(7,1),(8,1), -- McLaren: Norris, Piastri
|
||||
(9,5),(10,5), -- Aston Martin: Alonso, Stroll
|
||||
(11,6),(12,6), -- Alpine: Gasly, Doohan
|
||||
(13,7),(14,7), -- Williams: Albon, Sainz
|
||||
(15,8),(16,8), -- Racing Bulls: Tsunoda, Hadjar
|
||||
(17,9),(18,9), -- Haas: Ocon, Bearman
|
||||
(19,10),(20,10);-- Sauber: Hülkenberg, Bortoleto
|
||||
|
||||
-- ============================================================
|
||||
-- CALENDARIO 2025 (24 carreras)
|
||||
-- ============================================================
|
||||
INSERT INTO grandes_premios (id, nombre, circuito, ciudad, pais, temporada, fecha, num_vueltas, distancia_km) VALUES
|
||||
(1, 'Gran Premio de Australia', 'Albert Park Circuit', 'Melbourne', 'Australia', 2025, '2025-03-16', 58, 307.574),
|
||||
(2, 'Gran Premio de China', 'Shanghai International Circuit', 'Shanghái', 'China', 2025, '2025-03-23', 56, 305.066),
|
||||
(3, 'Gran Premio de Japón', 'Suzuka Circuit', 'Suzuka', 'Japón', 2025, '2025-04-06', 53, 307.471),
|
||||
(4, 'Gran Premio de Bahréin', 'Bahrain International Circuit', 'Sakhir', 'Bahréin', 2025, '2025-04-13', 57, 308.238),
|
||||
(5, 'Gran Premio de Arabia Saudí', 'Jeddah Corniche Circuit', 'Yeda', 'Arabia Saudí', 2025, '2025-04-20', 50, 308.450),
|
||||
(6, 'Gran Premio de Miami', 'Miami International Autodrome', 'Miami', 'Estados Unidos', 2025, '2025-05-04', 57, 308.326),
|
||||
(7, 'Gran Premio de Emilia-Romaña', 'Autodromo Enzo e Dino Ferrari', 'Imola', 'Italia', 2025, '2025-05-18', 63, 309.049),
|
||||
(8, 'Gran Premio de Mónaco', 'Circuit de Monaco', 'Montecarlo', 'Mónaco', 2025, '2025-05-25', 78, 260.286),
|
||||
(9, 'Gran Premio de España', 'Circuit de Barcelona-Catalunya', 'Barcelona', 'España', 2025, '2025-06-01', 66, 307.104),
|
||||
(10, 'Gran Premio de Canadá', 'Circuit Gilles Villeneuve', 'Montreal', 'Canadá', 2025, '2025-06-15', 70, 305.270),
|
||||
(11, 'Gran Premio de Austria', 'Red Bull Ring', 'Spielberg', 'Austria', 2025, '2025-06-29', 71, 307.020),
|
||||
(12, 'Gran Premio de Gran Bretaña', 'Silverstone Circuit', 'Silverstone', 'Reino Unido', 2025, '2025-07-06', 52, 306.198),
|
||||
(13, 'Gran Premio de Bélgica', 'Circuit de Spa-Francorchamps', 'Spa', 'Bélgica', 2025, '2025-07-27', 44, 308.052),
|
||||
(14, 'Gran Premio de Hungría', 'Hungaroring', 'Budapest', 'Hungría', 2025, '2025-08-03', 70, 306.630),
|
||||
(15, 'Gran Premio de los Países Bajos','Circuit Zandvoort', 'Zandvoort', 'Países Bajos', 2025, '2025-08-24', 52, 306.587),
|
||||
(16, 'Gran Premio de Italia', 'Autodromo Nazionale Monza', 'Monza', 'Italia', 2025, '2025-09-07', 53, 306.720),
|
||||
(17, 'Gran Premio de Azerbaiyán', 'Baku City Circuit', 'Bakú', 'Azerbaiyán', 2025, '2025-09-21', 51, 306.049),
|
||||
(18, 'Gran Premio de Singapur', 'Marina Bay Street Circuit', 'Singapur', 'Singapur', 2025, '2025-10-05', 61, 308.706),
|
||||
(19, 'Gran Premio de Estados Unidos', 'Circuit of the Americas', 'Austin', 'Estados Unidos', 2025, '2025-10-19', 56, 308.405),
|
||||
(20, 'Gran Premio de la Ciudad de México','Autodromo Hermanos Rodríguez', 'Ciudad de México','México', 2025, '2025-10-26', 71, 305.354),
|
||||
(21, 'Gran Premio de São Paulo', 'Autodromo José Carlos Pace', 'São Paulo', 'Brasil', 2025, '2025-11-09', 71, 305.909),
|
||||
(22, 'Gran Premio de Las Vegas', 'Las Vegas Strip Circuit', 'Las Vegas', 'Estados Unidos', 2025, '2025-11-22', 50, 309.958),
|
||||
(23, 'Gran Premio de Catar', 'Losail International Circuit', 'Lusail', 'Catar', 2025, '2025-11-30', 57, 308.611),
|
||||
(24, 'Gran Premio de Abu Dabi', 'Yas Marina Circuit', 'Abu Dabi', 'Emiratos Árabes', 2025, '2025-12-07', 58, 306.183);
|
||||
|
||||
-- ============================================================
|
||||
-- RESULTADOS 2025
|
||||
-- Puntos F1: P1=25, P2=18, P3=15, P4=12, P5=10,
|
||||
-- P6=8, P7=6, P8=4, P9=2, P10=1
|
||||
-- +1 vuelta rápida si piloto termina en top10
|
||||
-- ============================================================
|
||||
|
||||
-- piloto_id shortcuts (para referencia):
|
||||
-- 1=VER(RB3) 2=LAW(RB3) 3=HAM(FER2) 4=LEC(FER2) 5=RUS(MER4) 6=ANT(MER4)
|
||||
-- 7=NOR(MCL1) 8=PIA(MCL1) 9=ALO(AMR5) 10=STR(AMR5) 11=GAS(ALP6) 12=DOO(ALP6)
|
||||
-- 13=ALB(WIL7) 14=SAI(WIL7) 15=TSU(RBU8) 16=HAD(RBU8) 17=OCO(HAS9) 18=BEA(HAS9)
|
||||
-- 19=HUL(SAU10) 20=BOR(SAU10)
|
||||
|
||||
-- ─── GP 1: AUSTRALIA ─────────────────────────────────────────
|
||||
-- Ganador: Norris | P2: Piastri | P3: Russell
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(1,7,1,1,25,1,'FINALIZADO'),(1,8,1,2,18,0,'FINALIZADO'),(1,5,4,3,15,0,'FINALIZADO'),
|
||||
(1,4,2,4,12,0,'FINALIZADO'),(1,3,2,5,10,0,'FINALIZADO'),(1,1,3,6,8,0,'FINALIZADO'),
|
||||
(1,14,7,7,6,0,'FINALIZADO'),(1,6,4,8,4,0,'FINALIZADO'),(1,9,5,9,2,0,'FINALIZADO'),
|
||||
(1,15,8,10,1,0,'FINALIZADO'),(1,2,3,11,0,0,'FINALIZADO'),(1,11,6,12,0,0,'FINALIZADO'),
|
||||
(1,13,7,13,0,0,'FINALIZADO'),(1,16,8,14,0,0,'FINALIZADO'),(1,18,9,15,0,0,'FINALIZADO'),
|
||||
(1,17,9,16,0,0,'FINALIZADO'),(1,10,5,17,0,0,'FINALIZADO'),(1,19,10,18,0,0,'FINALIZADO'),
|
||||
(1,20,10,19,0,0,'FINALIZADO'),(1,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 2: CHINA ─────────────────────────────────────────────
|
||||
-- Ganador: Piastri | P2: Norris | P3: Verstappen
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(2,8,1,1,25,0,'FINALIZADO'),(2,7,1,2,18,0,'FINALIZADO'),(2,1,3,3,15,1,'FINALIZADO'),
|
||||
(2,5,4,4,12,0,'FINALIZADO'),(2,4,2,5,10,0,'FINALIZADO'),(2,3,2,6,8,0,'FINALIZADO'),
|
||||
(2,14,7,7,6,0,'FINALIZADO'),(2,6,4,8,4,0,'FINALIZADO'),(2,11,6,9,2,0,'FINALIZADO'),
|
||||
(2,9,5,10,1,0,'FINALIZADO'),(2,2,3,11,0,0,'FINALIZADO'),(2,15,8,12,0,0,'FINALIZADO'),
|
||||
(2,13,7,13,0,0,'FINALIZADO'),(2,16,8,14,0,0,'FINALIZADO'),(2,10,5,15,0,0,'FINALIZADO'),
|
||||
(2,19,10,16,0,0,'FINALIZADO'),(2,18,9,17,0,0,'FINALIZADO'),(2,20,10,18,0,0,'FINALIZADO'),
|
||||
(2,17,9,NULL,0,0,'DNF'),(2,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 3: JAPÓN ─────────────────────────────────────────────
|
||||
-- Ganador: Verstappen | P2: Norris | P3: Piastri
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(3,1,3,1,25,1,'FINALIZADO'),(3,7,1,2,18,0,'FINALIZADO'),(3,8,1,3,15,0,'FINALIZADO'),
|
||||
(3,5,4,4,12,0,'FINALIZADO'),(3,4,2,5,10,0,'FINALIZADO'),(3,3,2,6,8,0,'FINALIZADO'),
|
||||
(3,2,3,7,6,0,'FINALIZADO'),(3,14,7,8,4,0,'FINALIZADO'),(3,6,4,9,2,0,'FINALIZADO'),
|
||||
(3,15,8,10,1,0,'FINALIZADO'),(3,9,5,11,0,0,'FINALIZADO'),(3,11,6,12,0,0,'FINALIZADO'),
|
||||
(3,13,7,13,0,0,'FINALIZADO'),(3,16,8,14,0,0,'FINALIZADO'),(3,10,5,15,0,0,'FINALIZADO'),
|
||||
(3,18,9,16,0,0,'FINALIZADO'),(3,17,9,17,0,0,'FINALIZADO'),(3,19,10,18,0,0,'FINALIZADO'),
|
||||
(3,20,10,19,0,0,'FINALIZADO'),(3,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 4: BAHRÉIN ───────────────────────────────────────────
|
||||
-- Ganador: Piastri | P2: Russell | P3: Hamilton
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(4,8,1,1,25,0,'FINALIZADO'),(4,5,4,2,18,1,'FINALIZADO'),(4,3,2,3,15,0,'FINALIZADO'),
|
||||
(4,7,1,4,12,0,'FINALIZADO'),(4,4,2,5,10,0,'FINALIZADO'),(4,1,3,6,8,0,'FINALIZADO'),
|
||||
(4,14,7,7,6,0,'FINALIZADO'),(4,6,4,8,4,0,'FINALIZADO'),(4,9,5,9,2,0,'FINALIZADO'),
|
||||
(4,15,8,10,1,0,'FINALIZADO'),(4,2,3,11,0,0,'FINALIZADO'),(4,11,6,12,0,0,'FINALIZADO'),
|
||||
(4,13,7,13,0,0,'FINALIZADO'),(4,16,8,14,0,0,'FINALIZADO'),(4,10,5,15,0,0,'FINALIZADO'),
|
||||
(4,19,10,16,0,0,'FINALIZADO'),(4,18,9,17,0,0,'FINALIZADO'),(4,20,10,18,0,0,'FINALIZADO'),
|
||||
(4,17,9,19,0,0,'FINALIZADO'),(4,12,6,20,0,0,'FINALIZADO');
|
||||
|
||||
-- ─── GP 5: ARABIA SAUDÍ ──────────────────────────────────────
|
||||
-- Ganador: Norris | P2: Piastri | P3: Leclerc
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(5,7,1,1,25,1,'FINALIZADO'),(5,8,1,2,18,0,'FINALIZADO'),(5,4,2,3,15,0,'FINALIZADO'),
|
||||
(5,3,2,4,12,0,'FINALIZADO'),(5,5,4,5,10,0,'FINALIZADO'),(5,1,3,6,8,0,'FINALIZADO'),
|
||||
(5,6,4,7,6,0,'FINALIZADO'),(5,14,7,8,4,0,'FINALIZADO'),(5,2,3,9,2,0,'FINALIZADO'),
|
||||
(5,9,5,10,1,0,'FINALIZADO'),(5,11,6,11,0,0,'FINALIZADO'),(5,15,8,12,0,0,'FINALIZADO'),
|
||||
(5,13,7,13,0,0,'FINALIZADO'),(5,16,8,14,0,0,'FINALIZADO'),(5,10,5,15,0,0,'FINALIZADO'),
|
||||
(5,18,9,16,0,0,'FINALIZADO'),(5,17,9,17,0,0,'FINALIZADO'),(5,19,10,18,0,0,'FINALIZADO'),
|
||||
(5,20,10,19,0,0,'FINALIZADO'),(5,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 6: MIAMI ─────────────────────────────────────────────
|
||||
-- Ganador: Piastri | P2: Norris | P3: Russell
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(6,8,1,1,25,0,'FINALIZADO'),(6,7,1,2,18,1,'FINALIZADO'),(6,5,4,3,15,0,'FINALIZADO'),
|
||||
(6,3,2,4,12,0,'FINALIZADO'),(6,4,2,5,10,0,'FINALIZADO'),(6,1,3,6,8,0,'FINALIZADO'),
|
||||
(6,6,4,7,6,0,'FINALIZADO'),(6,14,7,8,4,0,'FINALIZADO'),(6,9,5,9,2,0,'FINALIZADO'),
|
||||
(6,11,6,10,1,0,'FINALIZADO'),(6,2,3,11,0,0,'FINALIZADO'),(6,15,8,12,0,0,'FINALIZADO'),
|
||||
(6,13,7,13,0,0,'FINALIZADO'),(6,16,8,14,0,0,'FINALIZADO'),(6,10,5,15,0,0,'FINALIZADO'),
|
||||
(6,18,9,16,0,0,'FINALIZADO'),(6,19,10,17,0,0,'FINALIZADO'),(6,17,9,18,0,0,'FINALIZADO'),
|
||||
(6,20,10,19,0,0,'FINALIZADO'),(6,12,6,20,0,0,'FINALIZADO');
|
||||
|
||||
-- ─── GP 7: EMILIA-ROMAÑA ─────────────────────────────────────
|
||||
-- Ganador: Russell | P2: Leclerc | P3: Hamilton
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(7,5,4,1,25,0,'FINALIZADO'),(7,4,2,2,18,1,'FINALIZADO'),(7,3,2,3,15,0,'FINALIZADO'),
|
||||
(7,7,1,4,12,0,'FINALIZADO'),(7,8,1,5,10,0,'FINALIZADO'),(7,6,4,6,8,0,'FINALIZADO'),
|
||||
(7,1,3,7,6,0,'FINALIZADO'),(7,14,7,8,4,0,'FINALIZADO'),(7,9,5,9,2,0,'FINALIZADO'),
|
||||
(7,2,3,10,1,0,'FINALIZADO'),(7,11,6,11,0,0,'FINALIZADO'),(7,15,8,12,0,0,'FINALIZADO'),
|
||||
(7,13,7,13,0,0,'FINALIZADO'),(7,16,8,14,0,0,'FINALIZADO'),(7,10,5,15,0,0,'FINALIZADO'),
|
||||
(7,18,9,16,0,0,'FINALIZADO'),(7,17,9,17,0,0,'FINALIZADO'),(7,19,10,18,0,0,'FINALIZADO'),
|
||||
(7,20,10,19,0,0,'FINALIZADO'),(7,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 8: MÓNACO ────────────────────────────────────────────
|
||||
-- Ganador: Leclerc | P2: Piastri | P3: Norris
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(8,4,2,1,25,0,'FINALIZADO'),(8,8,1,2,18,0,'FINALIZADO'),(8,7,1,3,15,0,'FINALIZADO'),
|
||||
(8,3,2,4,12,1,'FINALIZADO'),(8,5,4,5,10,0,'FINALIZADO'),(8,14,7,6,8,0,'FINALIZADO'),
|
||||
(8,9,5,7,6,0,'FINALIZADO'),(8,6,4,8,4,0,'FINALIZADO'),(8,1,3,9,2,0,'FINALIZADO'),
|
||||
(8,11,6,10,1,0,'FINALIZADO'),(8,2,3,11,0,0,'FINALIZADO'),(8,13,7,12,0,0,'FINALIZADO'),
|
||||
(8,15,8,13,0,0,'FINALIZADO'),(8,16,8,14,0,0,'FINALIZADO'),(8,10,5,15,0,0,'FINALIZADO'),
|
||||
(8,18,9,16,0,0,'FINALIZADO'),(8,17,9,17,0,0,'FINALIZADO'),(8,19,10,18,0,0,'FINALIZADO'),
|
||||
(8,20,10,19,0,0,'FINALIZADO'),(8,12,6,20,0,0,'FINALIZADO');
|
||||
|
||||
-- ─── GP 9: ESPAÑA ────────────────────────────────────────────
|
||||
-- Ganador: Norris | P2: Piastri | P3: Verstappen
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(9,7,1,1,25,0,'FINALIZADO'),(9,8,1,2,18,0,'FINALIZADO'),(9,1,3,3,15,1,'FINALIZADO'),
|
||||
(9,5,4,4,12,0,'FINALIZADO'),(9,4,2,5,10,0,'FINALIZADO'),(9,3,2,6,8,0,'FINALIZADO'),
|
||||
(9,6,4,7,6,0,'FINALIZADO'),(9,14,7,8,4,0,'FINALIZADO'),(9,9,5,9,2,0,'FINALIZADO'),
|
||||
(9,2,3,10,1,0,'FINALIZADO'),(9,11,6,11,0,0,'FINALIZADO'),(9,15,8,12,0,0,'FINALIZADO'),
|
||||
(9,13,7,13,0,0,'FINALIZADO'),(9,16,8,14,0,0,'FINALIZADO'),(9,10,5,15,0,0,'FINALIZADO'),
|
||||
(9,18,9,16,0,0,'FINALIZADO'),(9,17,9,17,0,0,'FINALIZADO'),(9,19,10,18,0,0,'FINALIZADO'),
|
||||
(9,20,10,19,0,0,'FINALIZADO'),(9,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 10: CANADÁ ───────────────────────────────────────────
|
||||
-- Ganador: Verstappen | P2: Russell | P3: Norris
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(10,1,3,1,25,1,'FINALIZADO'),(10,5,4,2,18,0,'FINALIZADO'),(10,7,1,3,15,0,'FINALIZADO'),
|
||||
(10,8,1,4,12,0,'FINALIZADO'),(10,4,2,5,10,0,'FINALIZADO'),(10,3,2,6,8,0,'FINALIZADO'),
|
||||
(10,14,7,7,6,0,'FINALIZADO'),(10,6,4,8,4,0,'FINALIZADO'),(10,9,5,9,2,0,'FINALIZADO'),
|
||||
(10,2,3,10,1,0,'FINALIZADO'),(10,11,6,11,0,0,'FINALIZADO'),(10,15,8,12,0,0,'FINALIZADO'),
|
||||
(10,13,7,13,0,0,'FINALIZADO'),(10,16,8,14,0,0,'FINALIZADO'),(10,10,5,15,0,0,'FINALIZADO'),
|
||||
(10,18,9,16,0,0,'FINALIZADO'),(10,17,9,17,0,0,'FINALIZADO'),(10,19,10,18,0,0,'FINALIZADO'),
|
||||
(10,20,10,19,0,0,'FINALIZADO'),(10,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 11: AUSTRIA ──────────────────────────────────────────
|
||||
-- Ganador: Piastri | P2: Norris | P3: Hamilton
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(11,8,1,1,25,0,'FINALIZADO'),(11,7,1,2,18,0,'FINALIZADO'),(11,3,2,3,15,1,'FINALIZADO'),
|
||||
(11,5,4,4,12,0,'FINALIZADO'),(11,4,2,5,10,0,'FINALIZADO'),(11,1,3,6,8,0,'FINALIZADO'),
|
||||
(11,6,4,7,6,0,'FINALIZADO'),(11,14,7,8,4,0,'FINALIZADO'),(11,9,5,9,2,0,'FINALIZADO'),
|
||||
(11,15,8,10,1,0,'FINALIZADO'),(11,2,3,11,0,0,'FINALIZADO'),(11,11,6,12,0,0,'FINALIZADO'),
|
||||
(11,13,7,13,0,0,'FINALIZADO'),(11,16,8,14,0,0,'FINALIZADO'),(11,10,5,15,0,0,'FINALIZADO'),
|
||||
(11,18,9,16,0,0,'FINALIZADO'),(11,17,9,17,0,0,'FINALIZADO'),(11,19,10,18,0,0,'FINALIZADO'),
|
||||
(11,20,10,19,0,0,'FINALIZADO'),(11,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 12: GRAN BRETAÑA ─────────────────────────────────────
|
||||
-- Ganador: Norris | P2: Piastri | P3: Russell
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(12,7,1,1,25,1,'FINALIZADO'),(12,8,1,2,18,0,'FINALIZADO'),(12,5,4,3,15,0,'FINALIZADO'),
|
||||
(12,3,2,4,12,0,'FINALIZADO'),(12,4,2,5,10,0,'FINALIZADO'),(12,1,3,6,8,0,'FINALIZADO'),
|
||||
(12,6,4,7,6,0,'FINALIZADO'),(12,14,7,8,4,0,'FINALIZADO'),(12,9,5,9,2,0,'FINALIZADO'),
|
||||
(12,2,3,10,1,0,'FINALIZADO'),(12,11,6,11,0,0,'FINALIZADO'),(12,15,8,12,0,0,'FINALIZADO'),
|
||||
(12,13,7,13,0,0,'FINALIZADO'),(12,16,8,14,0,0,'FINALIZADO'),(12,10,5,15,0,0,'FINALIZADO'),
|
||||
(12,18,9,16,0,0,'FINALIZADO'),(12,17,9,17,0,0,'FINALIZADO'),(12,19,10,18,0,0,'FINALIZADO'),
|
||||
(12,20,10,19,0,0,'FINALIZADO'),(12,12,6,20,0,0,'FINALIZADO');
|
||||
|
||||
-- ─── GP 13: BÉLGICA ──────────────────────────────────────────
|
||||
-- Ganador: Verstappen | P2: Piastri | P3: Norris
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(13,1,3,1,25,0,'FINALIZADO'),(13,8,1,2,18,1,'FINALIZADO'),(13,7,1,3,15,0,'FINALIZADO'),
|
||||
(13,5,4,4,12,0,'FINALIZADO'),(13,4,2,5,10,0,'FINALIZADO'),(13,3,2,6,8,0,'FINALIZADO'),
|
||||
(13,6,4,7,6,0,'FINALIZADO'),(13,14,7,8,4,0,'FINALIZADO'),(13,2,3,9,2,0,'FINALIZADO'),
|
||||
(13,9,5,10,1,0,'FINALIZADO'),(13,11,6,11,0,0,'FINALIZADO'),(13,15,8,12,0,0,'FINALIZADO'),
|
||||
(13,13,7,13,0,0,'FINALIZADO'),(13,16,8,14,0,0,'FINALIZADO'),(13,10,5,15,0,0,'FINALIZADO'),
|
||||
(13,18,9,16,0,0,'FINALIZADO'),(13,17,9,17,0,0,'FINALIZADO'),(13,19,10,18,0,0,'FINALIZADO'),
|
||||
(13,20,10,19,0,0,'FINALIZADO'),(13,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 14: HUNGRÍA ──────────────────────────────────────────
|
||||
-- Ganador: Piastri | P2: Leclerc | P3: Norris
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(14,8,1,1,25,0,'FINALIZADO'),(14,4,2,2,18,1,'FINALIZADO'),(14,7,1,3,15,0,'FINALIZADO'),
|
||||
(14,3,2,4,12,0,'FINALIZADO'),(14,5,4,5,10,0,'FINALIZADO'),(14,1,3,6,8,0,'FINALIZADO'),
|
||||
(14,6,4,7,6,0,'FINALIZADO'),(14,14,7,8,4,0,'FINALIZADO'),(14,9,5,9,2,0,'FINALIZADO'),
|
||||
(14,2,3,10,1,0,'FINALIZADO'),(14,11,6,11,0,0,'FINALIZADO'),(14,15,8,12,0,0,'FINALIZADO'),
|
||||
(14,13,7,13,0,0,'FINALIZADO'),(14,16,8,14,0,0,'FINALIZADO'),(14,10,5,15,0,0,'FINALIZADO'),
|
||||
(14,18,9,16,0,0,'FINALIZADO'),(14,17,9,17,0,0,'FINALIZADO'),(14,19,10,18,0,0,'FINALIZADO'),
|
||||
(14,20,10,19,0,0,'FINALIZADO'),(14,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 15: PAÍSES BAJOS ─────────────────────────────────────
|
||||
-- Ganador: Verstappen | P2: Norris | P3: Russell
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(15,1,3,1,25,1,'FINALIZADO'),(15,7,1,2,18,0,'FINALIZADO'),(15,5,4,3,15,0,'FINALIZADO'),
|
||||
(15,8,1,4,12,0,'FINALIZADO'),(15,4,2,5,10,0,'FINALIZADO'),(15,3,2,6,8,0,'FINALIZADO'),
|
||||
(15,6,4,7,6,0,'FINALIZADO'),(15,2,3,8,4,0,'FINALIZADO'),(15,14,7,9,2,0,'FINALIZADO'),
|
||||
(15,9,5,10,1,0,'FINALIZADO'),(15,11,6,11,0,0,'FINALIZADO'),(15,15,8,12,0,0,'FINALIZADO'),
|
||||
(15,13,7,13,0,0,'FINALIZADO'),(15,16,8,14,0,0,'FINALIZADO'),(15,10,5,15,0,0,'FINALIZADO'),
|
||||
(15,18,9,16,0,0,'FINALIZADO'),(15,17,9,17,0,0,'FINALIZADO'),(15,19,10,18,0,0,'FINALIZADO'),
|
||||
(15,20,10,19,0,0,'FINALIZADO'),(15,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 16: ITALIA ───────────────────────────────────────────
|
||||
-- Ganador: Piastri | P2: Norris | P3: Leclerc
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(16,8,1,1,25,1,'FINALIZADO'),(16,7,1,2,18,0,'FINALIZADO'),(16,4,2,3,15,0,'FINALIZADO'),
|
||||
(16,5,4,4,12,0,'FINALIZADO'),(16,3,2,5,10,0,'FINALIZADO'),(16,1,3,6,8,0,'FINALIZADO'),
|
||||
(16,6,4,7,6,0,'FINALIZADO'),(16,14,7,8,4,0,'FINALIZADO'),(16,9,5,9,2,0,'FINALIZADO'),
|
||||
(16,2,3,10,1,0,'FINALIZADO'),(16,11,6,11,0,0,'FINALIZADO'),(16,15,8,12,0,0,'FINALIZADO'),
|
||||
(16,13,7,13,0,0,'FINALIZADO'),(16,16,8,14,0,0,'FINALIZADO'),(16,10,5,15,0,0,'FINALIZADO'),
|
||||
(16,18,9,16,0,0,'FINALIZADO'),(16,17,9,17,0,0,'FINALIZADO'),(16,19,10,18,0,0,'FINALIZADO'),
|
||||
(16,20,10,19,0,0,'FINALIZADO'),(16,12,6,20,0,0,'FINALIZADO');
|
||||
|
||||
-- ─── GP 17: AZERBAIYÁN ───────────────────────────────────────
|
||||
-- Ganador: Norris | P2: Leclerc | P3: Piastri
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(17,7,1,1,25,0,'FINALIZADO'),(17,4,2,2,18,1,'FINALIZADO'),(17,8,1,3,15,0,'FINALIZADO'),
|
||||
(17,5,4,4,12,0,'FINALIZADO'),(17,3,2,5,10,0,'FINALIZADO'),(17,1,3,6,8,0,'FINALIZADO'),
|
||||
(17,14,7,7,6,0,'FINALIZADO'),(17,6,4,8,4,0,'FINALIZADO'),(17,2,3,9,2,0,'FINALIZADO'),
|
||||
(17,9,5,10,1,0,'FINALIZADO'),(17,11,6,11,0,0,'FINALIZADO'),(17,15,8,12,0,0,'FINALIZADO'),
|
||||
(17,13,7,13,0,0,'FINALIZADO'),(17,16,8,14,0,0,'FINALIZADO'),(17,10,5,15,0,0,'FINALIZADO'),
|
||||
(17,18,9,16,0,0,'FINALIZADO'),(17,19,10,17,0,0,'FINALIZADO'),(17,20,10,18,0,0,'FINALIZADO'),
|
||||
(17,17,9,NULL,0,0,'DNF'),(17,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 18: SINGAPUR ─────────────────────────────────────────
|
||||
-- Ganador: Russell | P2: Hamilton | P3: Norris
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(18,5,4,1,25,1,'FINALIZADO'),(18,3,2,2,18,0,'FINALIZADO'),(18,7,1,3,15,0,'FINALIZADO'),
|
||||
(18,8,1,4,12,0,'FINALIZADO'),(18,4,2,5,10,0,'FINALIZADO'),(18,6,4,6,8,0,'FINALIZADO'),
|
||||
(18,1,3,7,6,0,'FINALIZADO'),(18,14,7,8,4,0,'FINALIZADO'),(18,9,5,9,2,0,'FINALIZADO'),
|
||||
(18,2,3,10,1,0,'FINALIZADO'),(18,11,6,11,0,0,'FINALIZADO'),(18,15,8,12,0,0,'FINALIZADO'),
|
||||
(18,13,7,13,0,0,'FINALIZADO'),(18,16,8,14,0,0,'FINALIZADO'),(18,10,5,15,0,0,'FINALIZADO'),
|
||||
(18,18,9,16,0,0,'FINALIZADO'),(18,17,9,17,0,0,'FINALIZADO'),(18,19,10,18,0,0,'FINALIZADO'),
|
||||
(18,20,10,19,0,0,'FINALIZADO'),(18,12,6,20,0,0,'FINALIZADO');
|
||||
|
||||
-- ─── GP 19: ESTADOS UNIDOS (COTA) ────────────────────────────
|
||||
-- Ganador: Piastri | P2: Norris | P3: Verstappen
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(19,8,1,1,25,0,'FINALIZADO'),(19,7,1,2,18,1,'FINALIZADO'),(19,1,3,3,15,0,'FINALIZADO'),
|
||||
(19,5,4,4,12,0,'FINALIZADO'),(19,4,2,5,10,0,'FINALIZADO'),(19,3,2,6,8,0,'FINALIZADO'),
|
||||
(19,6,4,7,6,0,'FINALIZADO'),(19,14,7,8,4,0,'FINALIZADO'),(19,2,3,9,2,0,'FINALIZADO'),
|
||||
(19,9,5,10,1,0,'FINALIZADO'),(19,11,6,11,0,0,'FINALIZADO'),(19,15,8,12,0,0,'FINALIZADO'),
|
||||
(19,13,7,13,0,0,'FINALIZADO'),(19,16,8,14,0,0,'FINALIZADO'),(19,10,5,15,0,0,'FINALIZADO'),
|
||||
(19,18,9,16,0,0,'FINALIZADO'),(19,17,9,17,0,0,'FINALIZADO'),(19,19,10,18,0,0,'FINALIZADO'),
|
||||
(19,20,10,19,0,0,'FINALIZADO'),(19,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 20: MÉXICO ───────────────────────────────────────────
|
||||
-- Ganador: Verstappen | P2: Piastri | P3: Norris
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(20,1,3,1,25,1,'FINALIZADO'),(20,8,1,2,18,0,'FINALIZADO'),(20,7,1,3,15,0,'FINALIZADO'),
|
||||
(20,5,4,4,12,0,'FINALIZADO'),(20,4,2,5,10,0,'FINALIZADO'),(20,3,2,6,8,0,'FINALIZADO'),
|
||||
(20,6,4,7,6,0,'FINALIZADO'),(20,2,3,8,4,0,'FINALIZADO'),(20,14,7,9,2,0,'FINALIZADO'),
|
||||
(20,9,5,10,1,0,'FINALIZADO'),(20,11,6,11,0,0,'FINALIZADO'),(20,15,8,12,0,0,'FINALIZADO'),
|
||||
(20,13,7,13,0,0,'FINALIZADO'),(20,16,8,14,0,0,'FINALIZADO'),(20,10,5,15,0,0,'FINALIZADO'),
|
||||
(20,18,9,16,0,0,'FINALIZADO'),(20,17,9,17,0,0,'FINALIZADO'),(20,19,10,18,0,0,'FINALIZADO'),
|
||||
(20,20,10,19,0,0,'FINALIZADO'),(20,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 21: SÃO PAULO ────────────────────────────────────────
|
||||
-- Ganador: Norris | P2: Piastri | P3: Russell
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(21,7,1,1,25,1,'FINALIZADO'),(21,8,1,2,18,0,'FINALIZADO'),(21,5,4,3,15,0,'FINALIZADO'),
|
||||
(21,3,2,4,12,0,'FINALIZADO'),(21,4,2,5,10,0,'FINALIZADO'),(21,1,3,6,8,0,'FINALIZADO'),
|
||||
(21,6,4,7,6,0,'FINALIZADO'),(21,14,7,8,4,0,'FINALIZADO'),(21,2,3,9,2,0,'FINALIZADO'),
|
||||
(21,9,5,10,1,0,'FINALIZADO'),(21,11,6,11,0,0,'FINALIZADO'),(21,15,8,12,0,0,'FINALIZADO'),
|
||||
(21,13,7,13,0,0,'FINALIZADO'),(21,16,8,14,0,0,'FINALIZADO'),(21,10,5,15,0,0,'FINALIZADO'),
|
||||
(21,18,9,16,0,0,'FINALIZADO'),(21,17,9,17,0,0,'FINALIZADO'),(21,19,10,18,0,0,'FINALIZADO'),
|
||||
(21,20,10,19,0,0,'FINALIZADO'),(21,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 22: LAS VEGAS ────────────────────────────────────────
|
||||
-- Ganador: Piastri | P2: Russell | P3: Norris
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(22,8,1,1,25,0,'FINALIZADO'),(22,5,4,2,18,0,'FINALIZADO'),(22,7,1,3,15,1,'FINALIZADO'),
|
||||
(22,4,2,4,12,0,'FINALIZADO'),(22,3,2,5,10,0,'FINALIZADO'),(22,1,3,6,8,0,'FINALIZADO'),
|
||||
(22,6,4,7,6,0,'FINALIZADO'),(22,14,7,8,4,0,'FINALIZADO'),(22,2,3,9,2,0,'FINALIZADO'),
|
||||
(22,9,5,10,1,0,'FINALIZADO'),(22,11,6,11,0,0,'FINALIZADO'),(22,15,8,12,0,0,'FINALIZADO'),
|
||||
(22,13,7,13,0,0,'FINALIZADO'),(22,16,8,14,0,0,'FINALIZADO'),(22,10,5,15,0,0,'FINALIZADO'),
|
||||
(22,18,9,16,0,0,'FINALIZADO'),(22,17,9,17,0,0,'FINALIZADO'),(22,19,10,18,0,0,'FINALIZADO'),
|
||||
(22,20,10,19,0,0,'FINALIZADO'),(22,12,6,20,0,0,'FINALIZADO');
|
||||
|
||||
-- ─── GP 23: CATAR ────────────────────────────────────────────
|
||||
-- Ganador: Norris | P2: Piastri | P3: Verstappen
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(23,7,1,1,25,0,'FINALIZADO'),(23,8,1,2,18,1,'FINALIZADO'),(23,1,3,3,15,0,'FINALIZADO'),
|
||||
(23,5,4,4,12,0,'FINALIZADO'),(23,4,2,5,10,0,'FINALIZADO'),(23,3,2,6,8,0,'FINALIZADO'),
|
||||
(23,6,4,7,6,0,'FINALIZADO'),(23,14,7,8,4,0,'FINALIZADO'),(23,2,3,9,2,0,'FINALIZADO'),
|
||||
(23,9,5,10,1,0,'FINALIZADO'),(23,11,6,11,0,0,'FINALIZADO'),(23,15,8,12,0,0,'FINALIZADO'),
|
||||
(23,13,7,13,0,0,'FINALIZADO'),(23,16,8,14,0,0,'FINALIZADO'),(23,10,5,15,0,0,'FINALIZADO'),
|
||||
(23,18,9,16,0,0,'FINALIZADO'),(23,17,9,17,0,0,'FINALIZADO'),(23,19,10,18,0,0,'FINALIZADO'),
|
||||
(23,20,10,19,0,0,'FINALIZADO'),(23,12,6,NULL,0,0,'DNF');
|
||||
|
||||
-- ─── GP 24: ABU DABI ─────────────────────────────────────────
|
||||
-- Ganador: Piastri | P2: Norris | P3: Leclerc
|
||||
INSERT INTO resultados (gran_premio_id, piloto_id, escuderia_id, posicion, puntos, vuelta_rapida, estado) VALUES
|
||||
(24,8,1,1,25,1,'FINALIZADO'),(24,7,1,2,18,0,'FINALIZADO'),(24,4,2,3,15,0,'FINALIZADO'),
|
||||
(24,5,4,4,12,0,'FINALIZADO'),(24,3,2,5,10,0,'FINALIZADO'),(24,1,3,6,8,0,'FINALIZADO'),
|
||||
(24,6,4,7,6,0,'FINALIZADO'),(24,14,7,8,4,0,'FINALIZADO'),(24,2,3,9,2,0,'FINALIZADO'),
|
||||
(24,9,5,10,1,0,'FINALIZADO'),(24,11,6,11,0,0,'FINALIZADO'),(24,15,8,12,0,0,'FINALIZADO'),
|
||||
(24,13,7,13,0,0,'FINALIZADO'),(24,16,8,14,0,0,'FINALIZADO'),(24,10,5,15,0,0,'FINALIZADO'),
|
||||
(24,18,9,16,0,0,'FINALIZADO'),(24,17,9,17,0,0,'FINALIZADO'),(24,19,10,18,0,0,'FINALIZADO'),
|
||||
(24,20,10,19,0,0,'FINALIZADO'),(24,12,6,20,0,0,'FINALIZADO');
|
||||
|
||||
-- ============================================================
|
||||
-- USUARIO de BD (ejecutar como root)
|
||||
-- ============================================================
|
||||
-- CREATE USER IF NOT EXISTS 'formula1user'@'%' IDENTIFIED BY 'Eavne,e1m';
|
||||
-- GRANT ALL PRIVILEGES ON formula1.* TO 'formula1user'@'%';
|
||||
-- FLUSH PRIVILEGES;
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package es.tatvil.formula1;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
@SpringBootTest
|
||||
class Formula1ApplicationTests {
|
||||
|
||||
@Test
|
||||
void contextLoads() {
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
:root {
|
||||
--f1-red: #a00000;
|
||||
--dark-bg: #15151e;
|
||||
--card-bg: #1f1f27;
|
||||
--text: #ffffff;
|
||||
--border-color: #38383f;
|
||||
--titulo-color: #ff4c4c;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: var(--dark-bg);
|
||||
color: var(--text);
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
header {
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 30px;
|
||||
text-align: center;
|
||||
color: var(--titulo-color);
|
||||
}
|
||||
|
||||
.dashboard {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.stat-card {
|
||||
background-color: var(--card-bg);
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
border-left: 4px solid var(--f1-red);
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
text-align: left;
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
th {
|
||||
color: var(--titulo-color);
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.pos {
|
||||
font-weight: bold;
|
||||
color: var(--f1-red);
|
||||
}
|
||||
|
||||
.cuenta-atras {
|
||||
margin-bottom: 15px;
|
||||
text-align: center;
|
||||
border-bottom: 4px solid var(--f1-red);
|
||||
}
|
||||
|
||||
#countdown {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,240 @@
|
|||
:root {
|
||||
--f1-red: #e8002d;
|
||||
--dark-bg: #15151e;
|
||||
--card-bg: #1f1f2a;
|
||||
--text: #e5e5e5;
|
||||
--muted: #888;
|
||||
--border: #2e2e3e;
|
||||
--accent: #ff4c4c;
|
||||
--gold: #ffd700;
|
||||
--green: #39d353;
|
||||
}
|
||||
|
||||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
|
||||
body {
|
||||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: var(--dark-bg);
|
||||
color: var(--text);
|
||||
padding: 0 0 40px 0;
|
||||
}
|
||||
|
||||
/* ── Header ── */
|
||||
header {
|
||||
background: linear-gradient(135deg, #0a0a10 0%, #1a0a0a 100%);
|
||||
border-bottom: 3px solid var(--f1-red);
|
||||
padding: 20px 24px 0;
|
||||
}
|
||||
|
||||
header h1 {
|
||||
font-size: 1.6rem;
|
||||
color: #fff;
|
||||
letter-spacing: 2px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* ── Tabs ── */
|
||||
.tabs {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.tab-btn {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--muted);
|
||||
padding: 10px 18px;
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
border-bottom: 3px solid transparent;
|
||||
transition: 0.2s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.tab-btn:hover { color: var(--text); }
|
||||
.tab-btn.active { color: var(--accent); border-bottom-color: var(--accent); }
|
||||
|
||||
/* ── Tab content ── */
|
||||
.tab-content { display: none; padding: 24px; }
|
||||
.tab-content.active { display: block; }
|
||||
|
||||
/* ── Dashboard grid ── */
|
||||
.dashboard {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||
gap: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* ── Cards ── */
|
||||
.stat-card {
|
||||
background: var(--card-bg);
|
||||
border-radius: 12px;
|
||||
border-left: 4px solid var(--f1-red);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.stat-card h3 {
|
||||
color: var(--accent);
|
||||
font-size: 0.95rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* ── Cuenta atrás ── */
|
||||
.cuenta-atras {
|
||||
text-align: center;
|
||||
border-left: none;
|
||||
border-bottom: 4px solid var(--f1-red);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
#countdown {
|
||||
font-size: 2.2rem;
|
||||
font-weight: 800;
|
||||
letter-spacing: 3px;
|
||||
color: #fff;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
/* ── Tablas ── */
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.88rem;
|
||||
}
|
||||
th {
|
||||
color: var(--accent);
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
padding: 8px 6px;
|
||||
border-bottom: 2px solid var(--border);
|
||||
text-align: left;
|
||||
}
|
||||
td {
|
||||
padding: 9px 6px;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
tr:last-child td { border-bottom: none; }
|
||||
tr:hover td { background: rgba(255,255,255,0.03); }
|
||||
|
||||
.pos { font-weight: 800; color: var(--accent); }
|
||||
|
||||
tr.campeon td { background: rgba(255, 215, 0, 0.06); }
|
||||
tr.campeon .pos { color: var(--gold); }
|
||||
tr.ganador td { background: rgba(232, 0, 45, 0.07); }
|
||||
|
||||
/* ── Últimas carreras ── */
|
||||
.carreras-recientes { display: flex; flex-direction: column; gap: 10px; }
|
||||
.carrera-mini {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 12px 16px;
|
||||
background: rgba(255,255,255,0.04);
|
||||
border-radius: 8px;
|
||||
border-left: 3px solid var(--f1-red);
|
||||
}
|
||||
|
||||
/* ── Calendario ── */
|
||||
#lista-gp { display: flex; flex-direction: column; gap: 10px; }
|
||||
.gp-card {
|
||||
display: grid;
|
||||
grid-template-columns: 48px 1fr auto;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
padding: 14px 18px;
|
||||
border-radius: 10px;
|
||||
background: rgba(255,255,255,0.03);
|
||||
border-left: 4px solid var(--border);
|
||||
transition: 0.2s;
|
||||
}
|
||||
.gp-card.pasado { border-left-color: var(--f1-red); }
|
||||
.gp-card.proximo { border-left-color: var(--green); }
|
||||
.gp-card:hover { background: rgba(255,255,255,0.06); }
|
||||
|
||||
.gp-num {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 800;
|
||||
color: var(--border);
|
||||
text-align: center;
|
||||
}
|
||||
.gp-card.pasado .gp-num { color: var(--f1-red); }
|
||||
.gp-card.proximo .gp-num { color: var(--green); }
|
||||
|
||||
.gp-info { display: flex; flex-direction: column; gap: 2px; }
|
||||
.gp-info strong { font-size: 0.95rem; }
|
||||
.gp-info span { font-size: 0.78rem; color: var(--muted); }
|
||||
|
||||
.gp-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; font-size: 0.78rem; color: var(--muted); }
|
||||
|
||||
.badge-proximo {
|
||||
background: rgba(57, 211, 83, 0.15);
|
||||
color: var(--green);
|
||||
border: 1px solid var(--green);
|
||||
padding: 3px 8px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ── Botones ── */
|
||||
.btn-resultados {
|
||||
background: var(--f1-red);
|
||||
color: #fff;
|
||||
border: none;
|
||||
padding: 6px 14px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
transition: 0.2s;
|
||||
}
|
||||
.btn-resultados:hover { background: #c0001f; }
|
||||
|
||||
/* ── Modal ── */
|
||||
.modal {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(0,0,0,0.75);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
.modal.hidden { display: none; }
|
||||
.modal-content {
|
||||
background: var(--card-bg);
|
||||
border-radius: 14px;
|
||||
border-top: 4px solid var(--f1-red);
|
||||
padding: 24px;
|
||||
width: min(700px, 95vw);
|
||||
max-height: 85vh;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
}
|
||||
.modal-content h3 {
|
||||
color: var(--accent);
|
||||
margin-bottom: 16px;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
.modal-close {
|
||||
position: absolute;
|
||||
top: 16px;
|
||||
right: 16px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: var(--muted);
|
||||
font-size: 1.2rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
.modal-close:hover { color: #fff; }
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.gp-card { grid-template-columns: 32px 1fr; }
|
||||
.gp-meta { display: none; }
|
||||
#countdown { font-size: 1.5rem; }
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>F1 Stats Pro - Panel de Control</title>
|
||||
<link rel="stylesheet" href="css/f1.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1>Estadísticas F1</h1>
|
||||
</header>
|
||||
|
||||
<section class="stat-card cuenta-atras">
|
||||
<h3 > Cuenta atrás para el GP de Australia 2026</h3>
|
||||
<div id="countdown">
|
||||
00d 00h 00m 00s
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<main class="dashboard">
|
||||
<section class="stat-card">
|
||||
<h3 id="session-info">Pilotos</h3>
|
||||
<table id="pilotos-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Nombre</th>
|
||||
<th>Apellido</th>
|
||||
<th>Equipo</th>
|
||||
<th>Nacionalidad</th>
|
||||
<th>Código</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Aquí se llenarán los pilotos -->
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section class="stat-card">
|
||||
<h3 id="session-info">Equipos</h3>
|
||||
<table id="equipos-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Nombre</th>
|
||||
<th>País</th>
|
||||
<th>Fundación</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Aquí se llenarán los equipos -->
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<script src="js/f1.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,118 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>F1 Stats — tatvil</title>
|
||||
<link rel="stylesheet" href="css/f1.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header>
|
||||
<h1>🏎️ Estadísticas F1</h1>
|
||||
<nav class="tabs">
|
||||
<button class="tab-btn active" data-tab="dashboard">Dashboard</button>
|
||||
<button class="tab-btn" data-tab="clasificacion">Clasificación</button>
|
||||
<button class="tab-btn" data-tab="calendario">Calendario</button>
|
||||
<button class="tab-btn" data-tab="pilotos">Pilotos</button>
|
||||
<button class="tab-btn" data-tab="escuderias">Escuderías</button>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<!-- DASHBOARD -->
|
||||
<main id="tab-dashboard" class="tab-content active">
|
||||
<section class="stat-card cuenta-atras">
|
||||
<h3 id="nombre-proxima-carrera">Próxima carrera</h3>
|
||||
<div id="countdown">--d --h --m --s</div>
|
||||
</section>
|
||||
<div class="dashboard">
|
||||
<section class="stat-card">
|
||||
<h3>🏆 Top 5 Pilotos — 2025</h3>
|
||||
<table id="tabla-top-pilotos">
|
||||
<thead><tr><th>Pos</th><th>Piloto</th><th>Equipo</th><th>Pts</th><th>Victorias</th></tr></thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section class="stat-card">
|
||||
<h3>🏗️ Top 5 Constructores — 2025</h3>
|
||||
<table id="tabla-top-constructores">
|
||||
<thead><tr><th>Pos</th><th>Equipo</th><th>Pts</th><th>Victorias</th></tr></thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</section>
|
||||
</div>
|
||||
<section class="stat-card">
|
||||
<h3>🏁 Últimas carreras</h3>
|
||||
<div id="ultimas-carreras" class="carreras-recientes"></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- CLASIFICACIÓN -->
|
||||
<main id="tab-clasificacion" class="tab-content">
|
||||
<div class="dashboard">
|
||||
<section class="stat-card">
|
||||
<h3>🏆 Clasificación Pilotos 2025</h3>
|
||||
<table id="tabla-clasificacion-pilotos">
|
||||
<thead><tr><th>Pos</th><th>Piloto</th><th>Equipo</th><th>Pts</th><th>V</th><th>Podios</th><th>VR</th></tr></thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</section>
|
||||
<section class="stat-card">
|
||||
<h3>🏗️ Clasificación Constructores 2025</h3>
|
||||
<table id="tabla-clasificacion-constructores">
|
||||
<thead><tr><th>Pos</th><th>Constructor</th><th>Puntos</th><th>Victorias</th><th>Podios</th></tr></thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- CALENDARIO -->
|
||||
<main id="tab-calendario" class="tab-content">
|
||||
<section class="stat-card">
|
||||
<h3>📅 Calendario 2025</h3>
|
||||
<div id="lista-gp"></div>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- PILOTOS -->
|
||||
<main id="tab-pilotos" class="tab-content">
|
||||
<section class="stat-card">
|
||||
<h3>👨✈️ Pilotos 2025</h3>
|
||||
<table id="pilotos-table">
|
||||
<thead>
|
||||
<tr><th>#</th><th>Cód</th><th>Nombre</th><th>Apellido</th><th>Escudería</th><th>Nac.</th></tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- ESCUDERÍAS -->
|
||||
<main id="tab-escuderias" class="tab-content">
|
||||
<section class="stat-card">
|
||||
<h3>🏎️ Escuderías 2025</h3>
|
||||
<table id="escuderias-table">
|
||||
<thead>
|
||||
<tr><th>#</th><th>Nombre</th><th>País</th><th>Motor</th></tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</section>
|
||||
</main>
|
||||
|
||||
<!-- Modal resultados carrera -->
|
||||
<div id="modal-carrera" class="modal hidden">
|
||||
<div class="modal-content">
|
||||
<button class="modal-close" onclick="cerrarModal()">✕</button>
|
||||
<h3 id="modal-titulo"></h3>
|
||||
<table id="modal-resultados">
|
||||
<thead><tr><th>Pos</th><th>Piloto</th><th>Equipo</th><th>Pts</th><th>Estado</th></tr></thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/f1.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,126 @@
|
|||
// ===============================
|
||||
// FUNCIONES DE CONSULTA API
|
||||
// ===============================
|
||||
|
||||
// Obtener calendario completo de la temporada actual
|
||||
async function obtenerCalendario() {
|
||||
try {
|
||||
const response = await fetch("https://api.jolpi.ca/ergast/f1/current.json");
|
||||
const data = await response.json();
|
||||
const carreras = data.MRData.RaceTable.Races;
|
||||
return carreras;
|
||||
} catch (error) {
|
||||
console.error("Error al obtener calendario:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Encontrar la siguiente carrera
|
||||
function encontrarSiguienteCarrera(carreras) {
|
||||
const ahora = new Date();
|
||||
for (let carrera of carreras) {
|
||||
const fecha = new Date(`${carrera.date}T${carrera.time || '00:00:00Z'}`);
|
||||
if (fecha > ahora) {
|
||||
return { ...carrera, fecha };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// CUENTA ATRÁS DINÁMICA
|
||||
// ===============================
|
||||
function iniciarCuentaAtras(fechaCarrera) {
|
||||
const countdown = document.getElementById("countdown");
|
||||
|
||||
function actualizar() {
|
||||
const ahora = new Date();
|
||||
const diff = fechaCarrera - ahora;
|
||||
|
||||
if (diff <= 0) {
|
||||
countdown.textContent = "🏁 ¡La carrera empezó!";
|
||||
return;
|
||||
}
|
||||
|
||||
const dias = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
const horas = Math.floor((diff / (1000 * 60 * 60)) % 24);
|
||||
const minutos = Math.floor((diff / (1000 * 60)) % 60);
|
||||
const segundos = Math.floor((diff / 1000) % 60);
|
||||
|
||||
countdown.textContent = `${dias}d ${horas}h ${minutos}m ${segundos}s`;
|
||||
}
|
||||
|
||||
actualizar();
|
||||
setInterval(actualizar, 1000);
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// PILOTOS
|
||||
// ===============================
|
||||
async function cargarPilotos() {
|
||||
try {
|
||||
const response = await fetch('/f1/api/pilotos');
|
||||
const pilotos = await response.json();
|
||||
const tbody = document.querySelector('#pilotos-table tbody');
|
||||
|
||||
tbody.innerHTML = ''; // Limpiamos antes de rellenar
|
||||
|
||||
pilotos.forEach(p => {
|
||||
const fila = document.createElement('tr');
|
||||
fila.innerHTML = `
|
||||
<td>${p.numero}</td>
|
||||
<td>${p.codigo}</td>
|
||||
<td>${p.nombre}</td>
|
||||
<td>${p.apellido}</td>
|
||||
<td>${p.escuderia || '-'}</td>
|
||||
<td>${p.nacionalidad}</td>
|
||||
`;
|
||||
tbody.appendChild(fila);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error cargando pilotos:', error);
|
||||
const tbody = document.querySelector('#pilotos-table tbody');
|
||||
tbody.innerHTML = `<tr><td colspan="6">No se pudieron cargar los pilotos</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// INIT PRINCIPAL
|
||||
// ===============================
|
||||
async function init() {
|
||||
// 1) Obtener calendario
|
||||
const carreras = await obtenerCalendario();
|
||||
|
||||
// 2) Calcular siguiente evento
|
||||
const proxima = encontrarSiguienteCarrera(carreras);
|
||||
|
||||
if (!proxima) {
|
||||
document.getElementById("countdown").textContent =
|
||||
"No hay próximos Grandes Premios este año";
|
||||
return;
|
||||
}
|
||||
|
||||
// 3) Mostrar nombre de la próxima carrera
|
||||
document.querySelector(".cuenta-atras h3").textContent =
|
||||
`Cuenta atrás para el GP de ${proxima.raceName}`;
|
||||
|
||||
// 4) Iniciar cuenta atrás
|
||||
iniciarCuentaAtras(proxima.fecha);
|
||||
|
||||
// 5) Indicar modo carrera o pronóstico
|
||||
const ahora = new Date();
|
||||
if (ahora >= proxima.fecha) {
|
||||
document.getElementById("session-info").textContent =
|
||||
"Modo carrera — datos en vivo o resultados";
|
||||
} else {
|
||||
document.getElementById("session-info").textContent =
|
||||
`Próxima sesión de ${proxima.raceName}`;
|
||||
}
|
||||
|
||||
// Cargar pilotos al inicio y cada minuto
|
||||
cargarPilotos();
|
||||
setInterval(() => { cargarPilotos(); }, 60000);
|
||||
}
|
||||
|
||||
// Arrancar todo al cargar la página
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
|
|
@ -0,0 +1,227 @@
|
|||
// Base API
|
||||
const API = '/f1/api';
|
||||
|
||||
// ─── Tabs ─────────────────────────────────────────────────────
|
||||
document.querySelectorAll('.tab-btn').forEach(btn => {
|
||||
btn.addEventListener('click', () => {
|
||||
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
|
||||
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
document.getElementById('tab-' + btn.dataset.tab).classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Cuenta atrás próxima carrera (Ergast/Jolpi API) ──────────
|
||||
async function initCuentaAtras() {
|
||||
try {
|
||||
const res = await fetch('https://api.jolpi.ca/ergast/f1/2026.json');
|
||||
const data = await res.json();
|
||||
const carreras = data.MRData.RaceTable.Races;
|
||||
const ahora = new Date();
|
||||
const proxima = carreras.find(c => new Date(`${c.date}T${c.time || '12:00:00Z'}`) > ahora);
|
||||
if (!proxima) {
|
||||
document.getElementById('nombre-proxima-carrera').textContent = 'Temporada terminada';
|
||||
document.getElementById('countdown').textContent = '—';
|
||||
return;
|
||||
}
|
||||
const fechaProxima = new Date(`${proxima.date}T${proxima.time || '12:00:00Z'}`);
|
||||
document.getElementById('nombre-proxima-carrera').textContent =
|
||||
`⏱️ Cuenta atrás — ${proxima.raceName}`;
|
||||
function tick() {
|
||||
const diff = fechaProxima - new Date();
|
||||
if (diff <= 0) { document.getElementById('countdown').textContent = '🏁 ¡Carrera en curso!'; return; }
|
||||
const d = Math.floor(diff / 86400000);
|
||||
const h = Math.floor((diff % 86400000) / 3600000);
|
||||
const m = Math.floor((diff % 3600000) / 60000);
|
||||
const s = Math.floor((diff % 60000) / 1000);
|
||||
document.getElementById('countdown').textContent = `${d}d ${h}h ${m}m ${s}s`;
|
||||
}
|
||||
tick();
|
||||
setInterval(tick, 1000);
|
||||
} catch (e) {
|
||||
document.getElementById('countdown').textContent = '—';
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Clasificación ────────────────────────────────────────────
|
||||
async function cargarClasificacionPilotos() {
|
||||
const res = await fetch(`${API}/resultados/clasificacion/pilotos?temporada=2025`);
|
||||
return res.json();
|
||||
}
|
||||
async function cargarClasificacionConstructores() {
|
||||
const res = await fetch(`${API}/resultados/clasificacion/constructores?temporada=2025`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
function renderClasificacionPilotos(data, tablaId, maxRows = 999) {
|
||||
const tbody = document.querySelector(`#${tablaId} tbody`);
|
||||
tbody.innerHTML = '';
|
||||
const esCompleta = tablaId.includes('clasificacion');
|
||||
data.slice(0, maxRows).forEach((p, i) => {
|
||||
const tr = document.createElement('tr');
|
||||
if (i === 0) tr.classList.add('campeon');
|
||||
tr.innerHTML = `
|
||||
<td class="pos">${i + 1}</td>
|
||||
<td>${p.nombre} <strong>${p.apellido}</strong></td>
|
||||
<td>${p.equipo}</td>
|
||||
<td><strong>${p.puntos}</strong></td>
|
||||
<td>${p.victorias}</td>
|
||||
${esCompleta ? `<td>${p.podios}</td><td>${p.vueltasRapidas}</td>` : ''}
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
function renderClasificacionConstructores(data, tablaId, maxRows = 999) {
|
||||
const tbody = document.querySelector(`#${tablaId} tbody`);
|
||||
tbody.innerHTML = '';
|
||||
const esCompleta = tablaId.includes('clasificacion');
|
||||
data.slice(0, maxRows).forEach((c, i) => {
|
||||
const tr = document.createElement('tr');
|
||||
if (i === 0) tr.classList.add('campeon');
|
||||
tr.innerHTML = `
|
||||
<td class="pos">${i + 1}</td>
|
||||
<td><strong>${c.nombre}</strong></td>
|
||||
<td><strong>${c.puntos}</strong></td>
|
||||
<td>${c.victorias}</td>
|
||||
${esCompleta ? `<td>${c.podios}</td>` : ''}
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Calendario ───────────────────────────────────────────────
|
||||
async function cargarCalendario() {
|
||||
const res = await fetch(`${API}/gp?temporada=2025`);
|
||||
return res.json();
|
||||
}
|
||||
|
||||
function renderCalendario(gps) {
|
||||
const cont = document.getElementById('lista-gp');
|
||||
const hoy = new Date();
|
||||
cont.innerHTML = '';
|
||||
gps.forEach((gp, i) => {
|
||||
const fecha = new Date(gp.fecha);
|
||||
const pasado = fecha < hoy;
|
||||
const card = document.createElement('div');
|
||||
card.className = 'gp-card ' + (pasado ? 'pasado' : 'proximo');
|
||||
card.innerHTML = `
|
||||
<div class="gp-num">${String(i + 1).padStart(2, '0')}</div>
|
||||
<div class="gp-info">
|
||||
<strong>${gp.nombre}</strong>
|
||||
<span>${gp.circuito}</span>
|
||||
<span>${gp.ciudad}, ${gp.pais}</span>
|
||||
<span>${fecha.toLocaleDateString('es-ES', {day:'numeric',month:'long',year:'numeric'})}</span>
|
||||
</div>
|
||||
<div class="gp-meta">
|
||||
<span>${gp.numVueltas} vueltas · ${gp.distanciaKm} km</span>
|
||||
${pasado
|
||||
? `<button class="btn-resultados" onclick="abrirResultados(${gp.id}, '${gp.nombre.replace(/'/g, "\\'")}')">Ver resultados →</button>`
|
||||
: '<span class="badge-proximo">Próxima</span>'}
|
||||
</div>
|
||||
`;
|
||||
cont.appendChild(card);
|
||||
});
|
||||
}
|
||||
|
||||
function renderUltimasCarreras(gps) {
|
||||
const hoy = new Date();
|
||||
const pasados = gps.filter(g => new Date(g.fecha) < hoy).slice(-3).reverse();
|
||||
const cont = document.getElementById('ultimas-carreras');
|
||||
cont.innerHTML = '';
|
||||
pasados.forEach(gp => {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'carrera-mini';
|
||||
div.innerHTML = `<strong>${gp.nombre}</strong>
|
||||
<button class="btn-resultados" onclick="abrirResultados(${gp.id}, '${gp.nombre.replace(/'/g, "\\'")}')">Ver resultados →</button>`;
|
||||
cont.appendChild(div);
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Modal resultados ─────────────────────────────────────────
|
||||
async function abrirResultados(gpId, nombre) {
|
||||
document.getElementById('modal-titulo').textContent = nombre;
|
||||
document.querySelector('#modal-resultados tbody').innerHTML = '<tr><td colspan="5">Cargando...</td></tr>';
|
||||
document.getElementById('modal-carrera').classList.remove('hidden');
|
||||
|
||||
const res = await fetch(`${API}/resultados/gp/${gpId}`);
|
||||
const data = await res.json();
|
||||
const tbody = document.querySelector('#modal-resultados tbody');
|
||||
tbody.innerHTML = '';
|
||||
data.forEach(r => {
|
||||
const tr = document.createElement('tr');
|
||||
if (r.posicion === 1) tr.classList.add('ganador');
|
||||
tr.innerHTML = `
|
||||
<td class="pos">${r.posicion ?? 'DNF'}</td>
|
||||
<td>${r.piloto.nombre} <strong>${r.piloto.apellido}</strong>${r.vueltaRapida ? ' ⚡' : ''}</td>
|
||||
<td>${r.escuderia.nombre}</td>
|
||||
<td>${r.puntos}</td>
|
||||
<td>${r.estado}</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
function cerrarModal() {
|
||||
document.getElementById('modal-carrera').classList.add('hidden');
|
||||
}
|
||||
document.getElementById('modal-carrera').addEventListener('click', e => {
|
||||
if (e.target === e.currentTarget) cerrarModal();
|
||||
});
|
||||
|
||||
// ─── Pilotos ───────────────────────────────────────────────────
|
||||
async function cargarPilotos() {
|
||||
const res = await fetch(`${API}/pilotos`);
|
||||
const pilotos = await res.json();
|
||||
const tbody = document.querySelector('#pilotos-table tbody');
|
||||
tbody.innerHTML = '';
|
||||
pilotos.forEach(p => {
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `
|
||||
<td class="pos">${p.numero}</td>
|
||||
<td><strong>${p.codigo}</strong></td>
|
||||
<td>${p.nombre}</td>
|
||||
<td>${p.apellido}</td>
|
||||
<td>—</td>
|
||||
<td>${p.nacionalidad}</td>
|
||||
`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Escuderías ───────────────────────────────────────────────
|
||||
async function cargarEscuderias() {
|
||||
const res = await fetch(`${API}/escuderias`);
|
||||
const data = await res.json();
|
||||
const tbody = document.querySelector('#escuderias-table tbody');
|
||||
tbody.innerHTML = '';
|
||||
data.forEach((e, i) => {
|
||||
const tr = document.createElement('tr');
|
||||
tr.innerHTML = `<td>${i + 1}</td><td><strong>${e.nombre}</strong></td><td>${e.pais}</td><td>${e.motor}</td>`;
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
// ─── INIT ──────────────────────────────────────────────────────
|
||||
async function init() {
|
||||
initCuentaAtras();
|
||||
|
||||
const [clasificPilotos, clasificConst, gps] = await Promise.all([
|
||||
cargarClasificacionPilotos(),
|
||||
cargarClasificacionConstructores(),
|
||||
cargarCalendario()
|
||||
]);
|
||||
|
||||
renderClasificacionPilotos(clasificPilotos, 'tabla-top-pilotos', 5);
|
||||
renderClasificacionConstructores(clasificConst, 'tabla-top-constructores', 5);
|
||||
renderUltimasCarreras(gps);
|
||||
|
||||
renderClasificacionPilotos(clasificPilotos, 'tabla-clasificacion-pilotos');
|
||||
renderClasificacionConstructores(clasificConst, 'tabla-clasificacion-constructores');
|
||||
|
||||
renderCalendario(gps);
|
||||
cargarPilotos();
|
||||
cargarEscuderias();
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', init);
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
// ===============================
|
||||
// FUNCIONES DE CONSULTA API
|
||||
// ===============================
|
||||
|
||||
// Obtener calendario completo de la temporada actual
|
||||
async function obtenerCalendario() {
|
||||
try {
|
||||
const response = await fetch("https://api.jolpi.ca/ergast/f1/current.json");
|
||||
const data = await response.json();
|
||||
const carreras = data.MRData.RaceTable.Races;
|
||||
return carreras;
|
||||
} catch (error) {
|
||||
console.error("Error al obtener calendario:", error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Encontrar la siguiente carrera
|
||||
function encontrarSiguienteCarrera(carreras) {
|
||||
const ahora = new Date();
|
||||
for (let carrera of carreras) {
|
||||
const fecha = new Date(`${carrera.date}T${carrera.time || '00:00:00Z'}`);
|
||||
if (fecha > ahora) {
|
||||
return { ...carrera, fecha };
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// CUENTA ATRÁS DINÁMICA
|
||||
// ===============================
|
||||
function iniciarCuentaAtras(fechaCarrera) {
|
||||
const countdown = document.getElementById("countdown");
|
||||
|
||||
function actualizar() {
|
||||
const ahora = new Date();
|
||||
const diff = fechaCarrera - ahora;
|
||||
|
||||
if (diff <= 0) {
|
||||
countdown.textContent = "🏁 ¡La carrera empezó!";
|
||||
return;
|
||||
}
|
||||
|
||||
const dias = Math.floor(diff / (1000 * 60 * 60 * 24));
|
||||
const horas = Math.floor((diff / (1000 * 60 * 60)) % 24);
|
||||
const minutos = Math.floor((diff / (1000 * 60)) % 60);
|
||||
const segundos = Math.floor((diff / 1000) % 60);
|
||||
|
||||
countdown.textContent = `${dias}d ${horas}h ${minutos}m ${segundos}s`;
|
||||
}
|
||||
|
||||
actualizar();
|
||||
setInterval(actualizar, 1000);
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// PILOTOS
|
||||
// ===============================
|
||||
async function cargarPilotos() {
|
||||
try {
|
||||
const response = await fetch('/f1/api/pilotos');
|
||||
const pilotos = await response.json();
|
||||
const tbody = document.querySelector('#pilotos-table tbody');
|
||||
|
||||
tbody.innerHTML = ''; // Limpiamos antes de rellenar
|
||||
|
||||
pilotos.forEach(p => {
|
||||
const fila = document.createElement('tr');
|
||||
fila.innerHTML = `
|
||||
<td>${p.numero}</td>
|
||||
<td>${p.nombre}</td>
|
||||
<td>${p.apellido}</td>
|
||||
<td>${p.equipo || '-'}</td>
|
||||
<td>${p.nacionalidad}</td>
|
||||
<td>${p.codigo}</td>
|
||||
`;
|
||||
tbody.appendChild(fila);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error cargando pilotos:', error);
|
||||
const tbody = document.querySelector('#pilotos-table tbody');
|
||||
tbody.innerHTML = `<tr><td colspan="6">No se pudieron cargar los pilotos</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// ESCUDERÍAS
|
||||
// ===============================
|
||||
async function cargarEscuderias() {
|
||||
try {
|
||||
const response = await fetch('/f1/api/escuderias');
|
||||
const escuderias = await response.json();
|
||||
const tbody = document.querySelector('#escuderias-table tbody');
|
||||
|
||||
tbody.innerHTML = ''; // Limpiamos tabla
|
||||
|
||||
escuderias.forEach(e => {
|
||||
const fila = document.createElement('tr');
|
||||
fila.innerHTML = `
|
||||
<td>${e.nombre}</td>
|
||||
<td>${e.pais}</td>
|
||||
<td>${e.motor}</td>
|
||||
`;
|
||||
tbody.appendChild(fila);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error cargando escuderías:', error);
|
||||
const tbody = document.querySelector('#escuderias-table tbody');
|
||||
tbody.innerHTML = `<tr><td colspan="4">No se pudieron cargar las escuderías</td></tr>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ===============================
|
||||
// INIT PRINCIPAL
|
||||
// ===============================
|
||||
async function init() {
|
||||
// 1) Obtener calendario
|
||||
const carreras = await obtenerCalendario();
|
||||
|
||||
// 2) Calcular siguiente evento
|
||||
const proxima = encontrarSiguienteCarrera(carreras);
|
||||
|
||||
if (!proxima) {
|
||||
document.getElementById("countdown").textContent =
|
||||
"No hay próximos Grandes Premios este año";
|
||||
return;
|
||||
}
|
||||
|
||||
// 3) Mostrar nombre de la próxima carrera
|
||||
document.querySelector(".cuenta-atras h3").textContent =
|
||||
`Cuenta atrás para el GP de ${proxima.raceName}`;
|
||||
|
||||
// 4) Iniciar cuenta atrás
|
||||
iniciarCuentaAtras(proxima.fecha);
|
||||
|
||||
// 5) Indicar modo carrera o pronóstico
|
||||
const ahora = new Date();
|
||||
if (ahora >= proxima.fecha) {
|
||||
document.getElementById("session-info").textContent =
|
||||
"Modo carrera — datos en vivo o resultados";
|
||||
} else {
|
||||
document.getElementById("session-info").textContent =
|
||||
`Próxima sesión de ${proxima.raceName}`;
|
||||
}
|
||||
|
||||
// Cargar pilotos y escuderías al inicio y cada minuto
|
||||
cargarPilotos();
|
||||
cargarEscuderias();
|
||||
setInterval(() => { cargarPilotos(); cargarEscuderias(); }, 60000);
|
||||
}
|
||||
|
||||
// Arrancar todo al cargar la página
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
const BASE_URL = 'https://api.openf1.org/v1';
|
||||
|
||||
async function cargarDatosF1() {
|
||||
const statusDiv = document.getElementById('api-status');
|
||||
const tablaCuerpo = document.getElementById('tabla-pilotos-body');
|
||||
|
||||
try {
|
||||
statusDiv.innerHTML = 'Conectando con OpenF1...';
|
||||
|
||||
// Usamos una session_key fija de un GP pasado para probar que funciona
|
||||
// Bahrain 2024 Session Key: 9465
|
||||
const sessionKey = '9465';
|
||||
|
||||
document.getElementById('session-info').innerText = "GP de Bahrain (Datos de Test)";
|
||||
|
||||
const driversRes = await fetch(`${BASE_URL}/drivers?session_key=${sessionKey}`);
|
||||
|
||||
if (!driversRes.ok) throw new Error(`Error HTTP: ${driversRes.status}`);
|
||||
|
||||
const drivers = await driversRes.json();
|
||||
|
||||
if (drivers.length === 0) {
|
||||
statusDiv.innerHTML = 'No se encontraron pilotos.';
|
||||
return;
|
||||
}
|
||||
|
||||
tablaCuerpo.innerHTML = '';
|
||||
drivers.forEach(driver => {
|
||||
const row = document.createElement('tr');
|
||||
row.innerHTML = `
|
||||
<td style="border-left: 4px solid #${driver.team_colour || 'ccc'}">
|
||||
${driver.driver_number}
|
||||
</td>
|
||||
<td><strong>${driver.full_name}</strong></td>
|
||||
<td>${driver.team_name}</td>
|
||||
<td>${driver.name_acronym}</td>
|
||||
`;
|
||||
tablaCuerpo.appendChild(row);
|
||||
});
|
||||
|
||||
statusDiv.innerHTML = '<span style="color: #4caf50;">● Datos cargados con éxito</span>';
|
||||
|
||||
} catch (error) {
|
||||
console.error("DETALLE DEL ERROR:", error);
|
||||
statusDiv.innerHTML = `<span style="color: #ff4b4b;">● Error: ${error.message}</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', cargarDatosF1);
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pilotos de F1</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f7f7f7;
|
||||
padding: 20px;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
table {
|
||||
margin: 0 auto;
|
||||
border-collapse: collapse;
|
||||
width: 90%;
|
||||
max-width: 800px;
|
||||
background-color: white;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
th, td {
|
||||
padding: 10px 15px;
|
||||
border: 1px solid #ddd;
|
||||
text-align: center;
|
||||
}
|
||||
th {
|
||||
background-color: #004080;
|
||||
color: white;
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
tr:hover {
|
||||
background-color: #cce0ff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Pilotos de F1</h1>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="es">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pilotos de F1</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f7f7f7;
|
||||
padding: 20px;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
table {
|
||||
margin: 0 auto;
|
||||
border-collapse: collapse;
|
||||
width: 90%;
|
||||
max-width: 800px;
|
||||
background-color: white;
|
||||
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
th, td {
|
||||
padding: 10px 15px;
|
||||
border: 1px solid #ddd;
|
||||
text-align: center;
|
||||
}
|
||||
th {
|
||||
background-color: #004080;
|
||||
color: white;
|
||||
}
|
||||
tr:nth-child(even) {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
tr:hover {
|
||||
background-color: #cce0ff;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Pilotos de F1</h1>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Loading…
Reference in New Issue