Compare commits
3 Commits
3ca967591e
...
c14958e5bf
| Author | SHA1 | Date | |
|---|---|---|---|
| c14958e5bf | |||
| 9d6e4e1b0b | |||
| f5d8f56330 |
@ -13,6 +13,7 @@ import type {
|
|||||||
RaceResult,
|
RaceResult,
|
||||||
ScrapedDriverStanding,
|
ScrapedDriverStanding,
|
||||||
ScrapedRaceResult,
|
ScrapedRaceResult,
|
||||||
|
ScrapedStartingGrid,
|
||||||
ScrapedTeamStanding,
|
ScrapedTeamStanding,
|
||||||
SeasonPick,
|
SeasonPick,
|
||||||
SeasonPickedUser,
|
SeasonPickedUser,
|
||||||
@ -333,6 +334,19 @@ export const fetch_scraped_teamstandings = async (
|
|||||||
return scraped_teamstandings;
|
return scraped_teamstandings;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all [ScrapedStartingGrids] from the database, ordered descendingly by race step.
|
||||||
|
*/
|
||||||
|
export const fetch_scraped_startinggrids = async (
|
||||||
|
fetch: (_: any) => Promise<Response>,
|
||||||
|
): Promise<ScrapedStartingGrid[]> => {
|
||||||
|
const scraped_startinggrids: ScrapedStartingGrid[] = await pb
|
||||||
|
.collection("scraped_startinggrids")
|
||||||
|
.getFullList({ fetch: fetch, sort: "-race_step,+position" });
|
||||||
|
|
||||||
|
return scraped_startinggrids;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch all [ScrapedRaceResults] from the database, ordered descendingly by race step.
|
* Fetch all [ScrapedRaceResults] from the database, ordered descendingly by race step.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -159,6 +159,14 @@ export interface RacePickPointsTotal {
|
|||||||
|
|
||||||
// Scraped Data
|
// Scraped Data
|
||||||
|
|
||||||
|
export interface ScrapedStartingGrid {
|
||||||
|
id: string;
|
||||||
|
race_step: number; // This maps to races
|
||||||
|
driver_code: string; // This maps to drivers
|
||||||
|
position: number;
|
||||||
|
time: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ScrapedRaceResult {
|
export interface ScrapedRaceResult {
|
||||||
id: string;
|
id: string;
|
||||||
race_step: number; // This maps to races
|
race_step: number; // This maps to races
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
import type { ScrapedDriverStanding, ScrapedRaceResult, ScrapedTeamStanding } from "$lib/schema";
|
import type {
|
||||||
|
ScrapedDriverStanding,
|
||||||
|
ScrapedStartingGrid,
|
||||||
|
ScrapedRaceResult,
|
||||||
|
ScrapedTeamStanding,
|
||||||
|
} from "$lib/schema";
|
||||||
import * as cheerio from "cheerio";
|
import * as cheerio from "cheerio";
|
||||||
|
|
||||||
// TODO: Validate the generated stuff
|
// TODO: Validate the generated stuff
|
||||||
@ -26,6 +31,48 @@ export const scrape_race_links = async (): Promise<string[]> => {
|
|||||||
return race_links;
|
return race_links;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of [ScrapedStartingGrids] for all races contained in [race_links],
|
||||||
|
* based on official f1.com data.
|
||||||
|
*/
|
||||||
|
export const scrape_starting_grids = async (
|
||||||
|
race_links: string[],
|
||||||
|
): Promise<ScrapedStartingGrid[]> => {
|
||||||
|
// Update the race_links to point to the qualifications
|
||||||
|
const starting_grid_links: string[] = race_links.map((link: string) =>
|
||||||
|
link.replace("/race-result", "/starting-grid"),
|
||||||
|
);
|
||||||
|
|
||||||
|
const starting_grids: ScrapedStartingGrid[] = [];
|
||||||
|
await Promise.all(
|
||||||
|
starting_grid_links.map(async (link: string, index: number) => {
|
||||||
|
console.log(`Fetching qualifying results from ${base_url}/${link}...`);
|
||||||
|
const starting_grids_response = await fetch(`${base_url}/${link}`);
|
||||||
|
const starting_grids_text = await starting_grids_response.text();
|
||||||
|
|
||||||
|
const $ = cheerio.load(starting_grids_text);
|
||||||
|
|
||||||
|
// Obtain the positions for this starting grid for each driver
|
||||||
|
$("tbody > tr", "div.f1-inner-wrapper table.f1-table").each((driver_index, element) => {
|
||||||
|
const $$ = cheerio.load(element);
|
||||||
|
|
||||||
|
let result: ScrapedStartingGrid = {
|
||||||
|
id: "",
|
||||||
|
race_step: index + 1,
|
||||||
|
driver_code: $$("td:nth-child(3) > p > span:last-child").text(),
|
||||||
|
position: driver_index + 1, // parseInt($$("td:nth-child(1) > p").text()),
|
||||||
|
time: $$("td:nth-child(5) > p").text(),
|
||||||
|
};
|
||||||
|
|
||||||
|
starting_grids.push(result);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
console.log(`Scraped ${starting_grids.length} starting grids...`);
|
||||||
|
|
||||||
|
return starting_grids;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of [ScrapedRaceResults] for all races contained in [race_links],
|
* Returns a list of [ScrapedRaceResults] for all races contained in [race_links],
|
||||||
* based on official f1.com data.
|
* based on official f1.com data.
|
||||||
|
|||||||
@ -1,14 +1,21 @@
|
|||||||
import {
|
import {
|
||||||
fetch_scraped_driverstandings,
|
fetch_scraped_driverstandings,
|
||||||
fetch_scraped_raceresults,
|
fetch_scraped_raceresults,
|
||||||
|
fetch_scraped_startinggrids,
|
||||||
fetch_scraped_teamstandings,
|
fetch_scraped_teamstandings,
|
||||||
} from "$lib/fetch";
|
} from "$lib/fetch";
|
||||||
import { pb } from "$lib/pocketbase";
|
import { pb } from "$lib/pocketbase";
|
||||||
import type { ScrapedDriverStanding, ScrapedRaceResult, ScrapedTeamStanding } from "$lib/schema";
|
import type {
|
||||||
|
ScrapedDriverStanding,
|
||||||
|
ScrapedRaceResult,
|
||||||
|
ScrapedStartingGrid,
|
||||||
|
ScrapedTeamStanding,
|
||||||
|
} from "$lib/schema";
|
||||||
import {
|
import {
|
||||||
scrape_driver_standings,
|
scrape_driver_standings,
|
||||||
scrape_race_links,
|
scrape_race_links,
|
||||||
scrape_race_results,
|
scrape_race_results,
|
||||||
|
scrape_starting_grids,
|
||||||
scrape_team_standings,
|
scrape_team_standings,
|
||||||
} from "$lib/server/scrape";
|
} from "$lib/server/scrape";
|
||||||
import type { RequestHandler } from "./$types";
|
import type { RequestHandler } from "./$types";
|
||||||
@ -24,12 +31,27 @@ export const POST: RequestHandler = async ({ request }) => {
|
|||||||
|
|
||||||
// Obtain the results for each race
|
// Obtain the results for each race
|
||||||
const racelinks: string[] = await scrape_race_links();
|
const racelinks: string[] = await scrape_race_links();
|
||||||
|
const startinggrids: ScrapedStartingGrid[] = await scrape_starting_grids(racelinks);
|
||||||
const raceresults: ScrapedRaceResult[] = await scrape_race_results(racelinks);
|
const raceresults: ScrapedRaceResult[] = await scrape_race_results(racelinks);
|
||||||
const driverstandings: ScrapedDriverStanding[] = await scrape_driver_standings();
|
const driverstandings: ScrapedDriverStanding[] = await scrape_driver_standings();
|
||||||
const teamstandings: ScrapedTeamStanding[] = await scrape_team_standings();
|
const teamstandings: ScrapedTeamStanding[] = await scrape_team_standings();
|
||||||
|
|
||||||
// Clear existing PocketBase data
|
// Clear existing PocketBase data
|
||||||
|
// TODO: Do I really have to fetch everything just to delete it???
|
||||||
let deleted: number = 0;
|
let deleted: number = 0;
|
||||||
|
const scraped_startinggrids: ScrapedStartingGrid[] = await fetch_scraped_startinggrids(fetch);
|
||||||
|
for (const grid of scraped_startinggrids) {
|
||||||
|
try {
|
||||||
|
await pb.collection("scraped_startinggrids").delete(grid.id);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
return new Response(); // TODO: Return error
|
||||||
|
}
|
||||||
|
deleted++;
|
||||||
|
}
|
||||||
|
console.log(`Deleted ${deleted}/${scraped_startinggrids.length} starting grids`);
|
||||||
|
|
||||||
|
deleted = 0;
|
||||||
const scraped_raceresults: ScrapedRaceResult[] = await fetch_scraped_raceresults(fetch);
|
const scraped_raceresults: ScrapedRaceResult[] = await fetch_scraped_raceresults(fetch);
|
||||||
for (const result of scraped_raceresults) {
|
for (const result of scraped_raceresults) {
|
||||||
try {
|
try {
|
||||||
@ -71,6 +93,23 @@ export const POST: RequestHandler = async ({ request }) => {
|
|||||||
|
|
||||||
// Submit new data to PocketBase
|
// Submit new data to PocketBase
|
||||||
let submissions: number = 0;
|
let submissions: number = 0;
|
||||||
|
for (const grid of startinggrids) {
|
||||||
|
try {
|
||||||
|
// TODO: Authenticate this
|
||||||
|
await pb.collection("scraped_startinggrids").create(grid);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("Error occured while submitting scraped data to PocketBase:");
|
||||||
|
console.log(e);
|
||||||
|
console.log("Error occured for this starting grid:");
|
||||||
|
console.log(grid);
|
||||||
|
console.log("Aborting submissions...");
|
||||||
|
return new Response(); // TODO: Return error
|
||||||
|
}
|
||||||
|
submissions++;
|
||||||
|
}
|
||||||
|
console.log(`Submitted ${submissions}/${startinggrids.length} starting grids.`);
|
||||||
|
|
||||||
|
submissions = 0;
|
||||||
for (const result of raceresults) {
|
for (const result of raceresults) {
|
||||||
try {
|
try {
|
||||||
// TODO: Authenticate this
|
// TODO: Authenticate this
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
>
|
>
|
||||||
<Button href="driverstandings" color="primary" activate_href>Drivers</Button>
|
<Button href="driverstandings" color="primary" activate_href>Drivers</Button>
|
||||||
<Button href="teamstandings" color="primary" activate_href>Teams</Button>
|
<Button href="teamstandings" color="primary" activate_href>Teams</Button>
|
||||||
|
<Button href="startinggrids" color="primary" activate_href>Grids</Button>
|
||||||
<Button href="raceresults" color="primary" activate_href>Race Results</Button>
|
<Button href="raceresults" color="primary" activate_href>Race Results</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
49
src/routes/data/official/startinggrids/+page.svelte
Normal file
49
src/routes/data/official/startinggrids/+page.svelte
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { Table, type TableColumn } from "$lib/components";
|
||||||
|
import type { PageData } from "./$types";
|
||||||
|
import { get_by_value } from "$lib/database";
|
||||||
|
import type { Race } from "$lib/schema";
|
||||||
|
|
||||||
|
let { data }: { data: PageData } = $props();
|
||||||
|
|
||||||
|
const grids_columns: TableColumn[] = $derived([
|
||||||
|
{
|
||||||
|
data_value_name: "race_step",
|
||||||
|
label: "Race",
|
||||||
|
valuefun: async (value: string): Promise<string> => {
|
||||||
|
const racename: string = get_by_value(await data.races, "step", value)?.name ?? "Invalid";
|
||||||
|
return `<span class='badge variant-filled-surface'>${racename}</span>`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data_value_name: "race_step",
|
||||||
|
label: "Step",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data_value_name: "driver_code",
|
||||||
|
label: "Driver",
|
||||||
|
valuefun: async (value: string): Promise<string> =>
|
||||||
|
`<span class='badge variant-filled-surface'>${value}</span>`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data_value_name: "position",
|
||||||
|
label: "Position",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
data_value_name: "time",
|
||||||
|
label: "Time",
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Formula 11 - Official Starting Grids</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
{#await data.scraped_startinggrids then grids}
|
||||||
|
<Table
|
||||||
|
data={grids}
|
||||||
|
columns={grids_columns}
|
||||||
|
height="h-[calc(100vh-260px)] lg:h-[calc(100vh-180px)]"
|
||||||
|
/>
|
||||||
|
{/await}
|
||||||
12
src/routes/data/official/startinggrids/+page.ts
Normal file
12
src/routes/data/official/startinggrids/+page.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { fetch_drivers, fetch_races, fetch_scraped_startinggrids } from "$lib/fetch";
|
||||||
|
import type { PageLoad } from "../../../$types";
|
||||||
|
|
||||||
|
export const load: PageLoad = async ({ fetch, depends }) => {
|
||||||
|
depends("data:scraped_startinggrids", "data:races", "data:drivers");
|
||||||
|
|
||||||
|
return {
|
||||||
|
scraped_startinggrids: fetch_scraped_startinggrids(fetch),
|
||||||
|
races: fetch_races(fetch),
|
||||||
|
drivers: fetch_drivers(fetch),
|
||||||
|
};
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user