Compare commits
4 Commits
5adc05e1bb
...
d31c6b6735
| Author | SHA1 | Date | |
|---|---|---|---|
| d31c6b6735 | |||
| cb48ee68e5 | |||
| f3c75fc921 | |||
| eb0de24754 |
@ -21,12 +21,16 @@ export const scrape_race_links = async (): Promise<string[]> => {
|
|||||||
const $ = cheerio.load(races_text);
|
const $ = cheerio.load(races_text);
|
||||||
|
|
||||||
const race_links: string[] = [];
|
const race_links: string[] = [];
|
||||||
$("tbody > tr > td:first-child > p > a[href]", "div.f1-inner-wrapper table.f1-table").each(
|
$("tbody > tr > td:first-child > p > a[href]", "table.f1-table").each((_, element) => {
|
||||||
(_, element) => {
|
const href: string = element.attribs["href"];
|
||||||
race_links.push(element.attribs["href"]);
|
|
||||||
},
|
// Keks changed the link format, cut off the start
|
||||||
);
|
const substring: string = href.replace("/../../en/results/2025/", "");
|
||||||
|
|
||||||
|
race_links.push(substring);
|
||||||
|
});
|
||||||
console.log(`Found ${race_links.length} races...`);
|
console.log(`Found ${race_links.length} races...`);
|
||||||
|
console.log(race_links);
|
||||||
|
|
||||||
return race_links;
|
return race_links;
|
||||||
};
|
};
|
||||||
@ -53,13 +57,15 @@ export const scrape_starting_grids = async (
|
|||||||
const $ = cheerio.load(starting_grids_text);
|
const $ = cheerio.load(starting_grids_text);
|
||||||
|
|
||||||
// Obtain the positions for this starting grid for each driver
|
// Obtain the positions for this starting grid for each driver
|
||||||
$("tbody > tr", "div.f1-inner-wrapper table.f1-table").each((driver_index, element) => {
|
$("tbody > tr", "table.f1-table").each((driver_index, element) => {
|
||||||
const $$ = cheerio.load(element);
|
const $$ = cheerio.load(element);
|
||||||
|
|
||||||
let result: ScrapedStartingGrid = {
|
let result: ScrapedStartingGrid = {
|
||||||
id: "",
|
id: "",
|
||||||
race_step: index + 1,
|
race_step: index + 1,
|
||||||
driver_code: $$("td:nth-child(3) > p > span:last-child").text(),
|
driver_code: $$(
|
||||||
|
"td:nth-child(3) > p > span:first-child > span:last-child > span:last-child",
|
||||||
|
).text(),
|
||||||
position: driver_index + 1, // parseInt($$("td:nth-child(1) > p").text()),
|
position: driver_index + 1, // parseInt($$("td:nth-child(1) > p").text()),
|
||||||
time: $$("td:nth-child(5) > p").text(),
|
time: $$("td:nth-child(5) > p").text(),
|
||||||
};
|
};
|
||||||
@ -69,6 +75,7 @@ export const scrape_starting_grids = async (
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
console.log(`Scraped ${starting_grids.length} starting grids...`);
|
console.log(`Scraped ${starting_grids.length} starting grids...`);
|
||||||
|
// console.log(starting_grids);
|
||||||
|
|
||||||
return starting_grids;
|
return starting_grids;
|
||||||
};
|
};
|
||||||
@ -88,13 +95,15 @@ export const scrape_race_results = async (race_links: string[]): Promise<Scraped
|
|||||||
const $ = cheerio.load(race_text);
|
const $ = cheerio.load(race_text);
|
||||||
|
|
||||||
// Obtain the results for this race for each driver
|
// Obtain the results for this race for each driver
|
||||||
$("tbody > tr", "div.f1-inner-wrapper table.f1-table").each((driver_index, element) => {
|
$("tbody > tr", "table.f1-table").each((driver_index, element) => {
|
||||||
const $$ = cheerio.load(element);
|
const $$ = cheerio.load(element);
|
||||||
|
|
||||||
let result: ScrapedRaceResult = {
|
let result: ScrapedRaceResult = {
|
||||||
id: "",
|
id: "",
|
||||||
race_step: index + 1,
|
race_step: index + 1,
|
||||||
driver_code: $$("td:nth-child(3) > p > span:last-child").text(),
|
driver_code: $$(
|
||||||
|
"td:nth-child(3) > p > span:first-child > span:last-child > span:last-child",
|
||||||
|
).text(),
|
||||||
position: driver_index + 1, // parseInt($$("td:nth-child(1) > p").text()),
|
position: driver_index + 1, // parseInt($$("td:nth-child(1) > p").text()),
|
||||||
status: $$("td:nth-child(6) > p").text(),
|
status: $$("td:nth-child(6) > p").text(),
|
||||||
points: parseInt($$("td:nth-child(7) > p").text()),
|
points: parseInt($$("td:nth-child(7) > p").text()),
|
||||||
@ -110,6 +119,7 @@ export const scrape_race_results = async (race_links: string[]): Promise<Scraped
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
console.log(`Scraped ${race_results.length} race results...`);
|
console.log(`Scraped ${race_results.length} race results...`);
|
||||||
|
// console.log(race_results);
|
||||||
|
|
||||||
return race_results;
|
return race_results;
|
||||||
};
|
};
|
||||||
@ -124,12 +134,12 @@ export const scrape_driver_standings = async (): Promise<ScrapedDriverStanding[]
|
|||||||
const $ = cheerio.load(standings_text);
|
const $ = cheerio.load(standings_text);
|
||||||
|
|
||||||
const driver_standings: ScrapedDriverStanding[] = [];
|
const driver_standings: ScrapedDriverStanding[] = [];
|
||||||
$("tbody > tr", "div.f1-inner-wrapper table.f1-table").each((driver_index, element) => {
|
$("tbody > tr", "table.f1-table").each((driver_index, element) => {
|
||||||
const $$ = cheerio.load(element);
|
const $$ = cheerio.load(element);
|
||||||
|
|
||||||
let standing: ScrapedDriverStanding = {
|
let standing: ScrapedDriverStanding = {
|
||||||
id: "",
|
id: "",
|
||||||
driver_code: $$("td:nth-child(2) > p > a > span:last-child").text(),
|
driver_code: $$("td:nth-child(2) > p > a > span:last-child > span:last-child").text(),
|
||||||
position: driver_index + 1,
|
position: driver_index + 1,
|
||||||
points: parseInt($$("td:nth-child(5) > p").text()),
|
points: parseInt($$("td:nth-child(5) > p").text()),
|
||||||
};
|
};
|
||||||
@ -137,6 +147,7 @@ export const scrape_driver_standings = async (): Promise<ScrapedDriverStanding[]
|
|||||||
driver_standings.push(standing);
|
driver_standings.push(standing);
|
||||||
});
|
});
|
||||||
console.log(`Scraped ${driver_standings.length} driver standings...`);
|
console.log(`Scraped ${driver_standings.length} driver standings...`);
|
||||||
|
// console.log(driver_standings);
|
||||||
|
|
||||||
return driver_standings;
|
return driver_standings;
|
||||||
};
|
};
|
||||||
@ -151,7 +162,7 @@ export const scrape_team_standings = async (): Promise<ScrapedTeamStanding[]> =>
|
|||||||
const $ = cheerio.load(standings_text);
|
const $ = cheerio.load(standings_text);
|
||||||
|
|
||||||
const team_standings: ScrapedTeamStanding[] = [];
|
const team_standings: ScrapedTeamStanding[] = [];
|
||||||
$("tbody > tr", "div.f1-inner-wrapper table.f1-table").each((team_index, element) => {
|
$("tbody > tr", "table.f1-table").each((team_index, element) => {
|
||||||
const $$ = cheerio.load(element);
|
const $$ = cheerio.load(element);
|
||||||
|
|
||||||
let standing: ScrapedTeamStanding = {
|
let standing: ScrapedTeamStanding = {
|
||||||
@ -164,6 +175,7 @@ export const scrape_team_standings = async (): Promise<ScrapedTeamStanding[]> =>
|
|||||||
team_standings.push(standing);
|
team_standings.push(standing);
|
||||||
});
|
});
|
||||||
console.log(`Scraped ${team_standings.length} team standings...`);
|
console.log(`Scraped ${team_standings.length} team standings...`);
|
||||||
|
// console.log(team_standings);
|
||||||
|
|
||||||
return team_standings;
|
return team_standings;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,28 +1,63 @@
|
|||||||
import type { ToastSettings } from "@skeletonlabs/skeleton";
|
import type { ToastSettings } from "@skeletonlabs/skeleton";
|
||||||
|
|
||||||
export const get_info_toast = (message: string, timeout: number = 2000): ToastSettings => {
|
export const get_info_toast = (
|
||||||
return {
|
message: string,
|
||||||
message,
|
timeout: number | null = 2000,
|
||||||
hideDismiss: true,
|
action_label: string | undefined = undefined,
|
||||||
timeout,
|
action_response: (() => void) | undefined = undefined,
|
||||||
background: "variant-filled-tertiary",
|
): ToastSettings =>
|
||||||
};
|
get_toast(message, "variant-filled-tertiary", timeout, action_label, action_response);
|
||||||
};
|
|
||||||
|
|
||||||
export const get_warning_toast = (message: string, timeout: number = 2000): ToastSettings => {
|
export const get_warning_toast = (
|
||||||
return {
|
message: string,
|
||||||
message,
|
timeout: number | null = 2000,
|
||||||
hideDismiss: true,
|
action_label: string | undefined = undefined,
|
||||||
timeout,
|
action_response: (() => void) | undefined = undefined,
|
||||||
background: "variant-filled-secondary",
|
): ToastSettings =>
|
||||||
};
|
get_toast(message, "variant-filled-secondary", timeout, action_label, action_response);
|
||||||
};
|
|
||||||
|
|
||||||
export const get_error_toast = (message: string, timeout: number = 2000): ToastSettings => {
|
export const get_error_toast = (
|
||||||
|
message: string,
|
||||||
|
timeout: number | null = 2000,
|
||||||
|
action_label: string | undefined = undefined,
|
||||||
|
action_response: (() => void) | undefined = undefined,
|
||||||
|
): ToastSettings =>
|
||||||
|
get_toast(message, "variant-filled-primary", timeout, action_label, action_response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility function to create [ToastSettings].
|
||||||
|
* If [timeout] is defined, the toast will disappear automatically and the dismiss-button will be hidden.
|
||||||
|
* If [timeout] is undefined, the toast will stay until dismissed from the dismiss-button.
|
||||||
|
* If [action_label] and [action_response] are defined, a callback function will be executed after accepting.
|
||||||
|
* In this case, [timeout] behaves as if undefined.
|
||||||
|
*/
|
||||||
|
const get_toast = (
|
||||||
|
message: string,
|
||||||
|
background: string,
|
||||||
|
timeout: number | null = 2000,
|
||||||
|
action_label: string | undefined = undefined,
|
||||||
|
action_response: (() => void) | undefined = undefined,
|
||||||
|
): ToastSettings => {
|
||||||
return {
|
return {
|
||||||
message,
|
message,
|
||||||
hideDismiss: true,
|
background,
|
||||||
timeout,
|
|
||||||
background: "variant-filled-primary",
|
// If we have a timeout and no action, dismiss is hidden
|
||||||
|
hideDismiss: !!timeout && (!action_label || !action_response),
|
||||||
|
|
||||||
|
// If we have a timeout and no action, the timeout is used
|
||||||
|
timeout: !!timeout && (!action_label || !action_response) ? timeout : undefined,
|
||||||
|
|
||||||
|
// If we have a timeout and no action, autohide is true
|
||||||
|
autohide: !!timeout && (!action_label || !action_response),
|
||||||
|
|
||||||
|
// If we have a label and a response, use the action
|
||||||
|
action:
|
||||||
|
!!action_label && !!action_response
|
||||||
|
? {
|
||||||
|
label: action_label,
|
||||||
|
response: action_response,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,16 +1,32 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
|
import { invalidate } from "$app/navigation";
|
||||||
import { Button } from "$lib/components";
|
import { Button } from "$lib/components";
|
||||||
import { pbUser } from "$lib/pocketbase";
|
import { pbUser } from "$lib/pocketbase";
|
||||||
|
import { get_error_toast, get_warning_toast } from "$lib/toast";
|
||||||
|
import { getToastStore, type ToastStore } from "@skeletonlabs/skeleton";
|
||||||
import type { Snippet } from "svelte";
|
import type { Snippet } from "svelte";
|
||||||
|
|
||||||
let { children }: { children: Snippet } = $props();
|
let { children }: { children: Snippet } = $props();
|
||||||
|
|
||||||
const scrape_official_data = async () => {
|
const toastStore: ToastStore = getToastStore();
|
||||||
// TODO: Unauthorized toast
|
|
||||||
if (!$pbUser || !$pbUser.admin) return;
|
|
||||||
|
|
||||||
// TODO: Success/error toast
|
const scrape_message: string =
|
||||||
|
"This will clear and redownload all data from f1.com. Please don't refresh the page during the process.";
|
||||||
|
|
||||||
|
// This callback will be executed once the admin presses the "Proceed"-button in the warning toast
|
||||||
|
const scrape_callback = async () => {
|
||||||
const response: Response = await fetch("/api/scrape", { method: "POST" });
|
const response: Response = await fetch("/api/scrape", { method: "POST" });
|
||||||
|
invalidate("data:official");
|
||||||
|
};
|
||||||
|
|
||||||
|
const scrape_official_data = async () => {
|
||||||
|
if (!$pbUser || !$pbUser.admin) {
|
||||||
|
toastStore.trigger(get_error_toast("Only admins may perform this action!"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No timeout + action toast
|
||||||
|
toastStore.trigger(get_warning_toast(scrape_message, null, "Proceed", scrape_callback));
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -64,7 +64,8 @@
|
|||||||
|
|
||||||
const active_drivers: Driver[] = drivers.filter((driver: Driver) => driver.active);
|
const active_drivers: Driver[] = drivers.filter((driver: Driver) => driver.active);
|
||||||
const active_results: ScrapedRaceResultAcc[] = scraped_raceresultsacc.filter(
|
const active_results: ScrapedRaceResultAcc[] = scraped_raceresultsacc.filter(
|
||||||
(result: ScrapedRaceResultAcc) => get_by_value(drivers, "code", result.driver_code)?.active,
|
(result: ScrapedRaceResultAcc) =>
|
||||||
|
get_by_value(drivers ?? [], "code", result.driver_code)?.active,
|
||||||
);
|
);
|
||||||
|
|
||||||
return active_drivers
|
return active_drivers
|
||||||
|
|||||||
Reference in New Issue
Block a user