Skip to content

Commit

Permalink
feat(frc_events): Add FRC Events as an alternative match data source (#…
Browse files Browse the repository at this point in the history
…60)

Co-authored-by: Evan Strat <[email protected]>
  • Loading branch information
evan10s and evan10s authored Oct 29, 2023
1 parent e505aba commit 71ffca1
Show file tree
Hide file tree
Showing 24 changed files with 566 additions and 13 deletions.
25 changes: 25 additions & 0 deletions client/src/components/alerts/FrcEventsWarning.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<template>
<VAlert v-if="!settingsStore.isFirstLoad && settingsStore.settings?.useFrcEventsApi"
color="warning"
variant="tonal"
density="comfortable"
:rounded="props.rounded ?? 0"
icon="mdi-alert-outline"
>
<template v-slot:text>
<strong>Using FRC Events API.</strong> This is not recommended unless match data is unavailable on The Blue
Alliance.
</template>
</VAlert>
</template>
<script lang="ts" setup>
import {useSettingsStore} from "@/stores/settings";
const settingsStore = useSettingsStore();
settingsStore.getSettings();
interface IProps {
rounded?: number;
}
const props = defineProps<IProps>();
</script>
2 changes: 2 additions & 0 deletions client/src/components/alerts/GlobalAlerts.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<SandboxModeAlert />
<PrivateUploads />
<NoPlaylistMappings />
<FrcEventsWarning />
</div>
</template>

Expand All @@ -12,4 +13,5 @@ import SandboxModeAlert from "@/components/alerts/SandboxModeAlert.vue";
import YouTubeAuthIncompleteAlert from "@/components/alerts/YouTubeAuthIncompleteAlert.vue";
import NoPlaylistMappings from "@/components/alerts/NoPlaylistMappings.vue";
import PrivateUploads from "@/components/alerts/PrivateUploads.vue";
import FrcEventsWarning from "@/components/alerts/FrcEventsWarning.vue";
</script>
1 change: 1 addition & 0 deletions client/src/components/alerts/NoPlaylistMappings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<VAlert v-if="!playlistsStore.isFirstLoad && !playlistsStore.playlists?.length"
color="info"
variant="tonal"
density="comfortable"
:rounded="props.rounded ?? 0"
icon="mdi-upload-off"
>
Expand Down
1 change: 1 addition & 0 deletions client/src/components/alerts/PrivateUploads.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<VAlert v-if="!settingsStore.isFirstLoad && settingsStore.settings?.youTubeVideoPrivacy !== 'public'"
color="warning"
variant="tonal"
density="comfortable"
:rounded="props.rounded ?? 0"
icon="mdi-eye-off"
>
Expand Down
1 change: 1 addition & 0 deletions client/src/components/alerts/SandboxModeAlert.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<VAlert v-if="!settingsStore.isFirstLoad && settingsStore.settings?.sandboxModeEnabled"
color="warning"
variant="tonal"
density="comfortable"
:rounded="props.rounded ?? 0"
icon="mdi-upload-off"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
color="error"
variant="tonal"
:rounded="0"
density="comfortable"
icon="mdi-alert-circle"
>
<template v-slot:text>
Expand Down
19 changes: 19 additions & 0 deletions client/src/components/matches/MatchDataAttribution.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<p v-if="settingsStore.settings?.useFrcEventsApi"
class="text-caption"
>
<VIcon icon="mdi-information" /> <a target="_blank" href="https://frc-events.firstinspires.org/services/API">
Event Data provided by <em>FIRST</em>
</a>
</p>
<p v-else class="text-caption">
<VIcon icon="mdi-information" /> <a target="_blank" href="https://thebluealliance.com">
Powered by The Blue Alliance
</a>
</p>
</template>
<script lang="ts" setup>
import {useSettingsStore} from "@/stores/settings";
const settingsStore = useSettingsStore();
</script>
13 changes: 12 additions & 1 deletion client/src/components/matches/MatchSelector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@
>
{{ matchListStore.error }}
</VAlert>

<VRow class="mb-1">
<VCol>
<MatchDataAttribution />
</VCol>
</VRow>

<!--
Note: this component may be laggy when running in development, but in production builds the performance
should become significantly better.
Expand All @@ -31,7 +38,7 @@
>
Refresh
</VBtn>
<VBtn v-if="matchStore.selectedMatchKey"
<VBtn v-if="!settingsStore.settings?.useFrcEventsApi && matchStore.selectedMatchKey"
prepend-icon="mdi-open-in-new"
variant="outlined"
class="ml-2"
Expand Down Expand Up @@ -71,9 +78,13 @@
import {useMatchStore} from "@/stores/match";
import {useMatchListStore} from "@/stores/matchList";
import {onMounted} from "vue";
import FrcEventsWarning from "@/components/alerts/FrcEventsWarning.vue";
import {useSettingsStore} from "@/stores/settings";
import MatchDataAttribution from "@/components/matches/MatchDataAttribution.vue";
const matchStore = useMatchStore();
const matchListStore = useMatchListStore();
const settingsStore = useSettingsStore();
onMounted(() => {
matchListStore.getMatchList();
Expand Down
1 change: 1 addition & 0 deletions client/src/types/IObfuscatedSecrets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ export interface IObfuscatedSecrets {
theBlueAllianceReadApiKey: boolean,
theBlueAllianceTrustedApiAuthId: boolean,
theBlueAllianceTrustedApiAuthSecret: boolean,
frcEventsApiKey: boolean,
[key: string]: boolean,
}
1 change: 1 addition & 0 deletions client/src/types/ISettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface ISettings {
sandboxModeEnabled: boolean,
youTubeVideoPrivacy: string,
linkVideosOnTheBlueAlliance: boolean;
useFrcEventsApi: boolean;
[key: string]: string | boolean,
}

Expand Down
57 changes: 55 additions & 2 deletions client/src/views/Settings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@
variant="tonal"
class="mt-4 mb-4"
>
Before you can enter your TBA trusted API auth ID and secret, you need to enable this feature using the
toggle above.
Some settings are hidden because linking match videos on TBA is disabled. Enable the feature to see them.
</VAlert>

<AutosavingTextInput v-if="
Expand Down Expand Up @@ -120,6 +119,50 @@
''"
/>

<h2 class="mt-4">
FRC Events API
</h2>
<p class="mt-4">
If match data is unavailable on The Blue Alliance, it may be available on the FRC Events API. You can check
if data is available on
<a href="https://frc-events.firstinspires.org/">https://frc-events.firstinspires.org/</a> and request API
credentials at
<a href="https://frc-events.firstinspires.org/services/API">
https://frc-events.firstinspires.org/services/API
</a>.
</p>
<p class="mt-4 mb-2">
To compute the API key, base64 encode the text <code>username:AuthorizationKey</code> and then
paste it below.
</p>

<p class="mt-4">Retrieve match data from FRC Events</p>
<AutosavingBtnSelectGroup :choices="['On', 'Off']"
:default-value="settingsStore.settings?.useFrcEventsApi ? 'On' : 'Off'"
:loading="savingFrcEventsApiKey"
@on-choice-selected="saveFrcEventsApiKey"
/>

<VAlert v-if="!settingsStore.settings?.useFrcEventsApi"
color="info"
variant="tonal"
class="mt-4 mb-4"
>
Some settings are hidden because FRC Events integration is disabled. Enable the feature to see them.
</VAlert>
<AutosavingTextInput v-else
:key="`frcEventsApiKey-${dataRefreshKey}`"
:on-submit="submit"
initial-value=""
name="frcEventsApiKey"
label="Base64-encoded FRC Events API key"
input-type="password"
setting-type="secret"
:help-text="settingsStore.obfuscatedSecrets?.frcEventsApiKey ?
'Current value hidden' :
''"
/>

<h2 class="mt-4">
Playlist mappings
</h2>
Expand Down Expand Up @@ -301,6 +344,16 @@ const tbaReadApiKeyHelpText = computed((): string => {
return baseText;
});
// TODO(Evan): Move into its own component
const savingFrcEventsApiKey = ref(false);
async function saveFrcEventsApiKey(value: string): Promise<void> {
savingFrcEventsApiKey.value = true;
await submit("useFrcEventsApi", value === "On", "setting");
await refreshData(false);
savingFrcEventsApiKey.value = false;
}
</script>

<style scoped>
Expand Down
3 changes: 2 additions & 1 deletion server/settings/secrets.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@
"googleTokenExpiry": "",
"theBlueAllianceReadApiKey": "",
"theBlueAllianceTrustedApiAuthId": "",
"theBlueAllianceTrustedApiAuthSecret": ""
"theBlueAllianceTrustedApiAuthSecret": "",
"frcEventsApiKey": ""
}
3 changes: 2 additions & 1 deletion server/settings/settings.example.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@
"sandboxModeEnabled": true,
"playoffsType": "Double elimination playoff",
"youTubeVideoPrivacy": "private",
"linkVideosOnTheBlueAlliance": false
"linkVideosOnTheBlueAlliance": false,
"useFrcEventsApi": false
}
32 changes: 32 additions & 0 deletions server/src/models/CompLevel.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/* eslint-disable no-unused-vars */
import { type FrcApiTournamentLevel } from "@src/models/frcEvents/frcTournamentLevel";

/**
* Represents the competition level of a match.
*/
Expand Down Expand Up @@ -48,3 +50,33 @@ export function abbreviatedCompLevel(compLevel: CompLevel): string {
throw new Error(`Comp level ${compLevel} is invalid.`);
}
}

export function toFrcApiTournamentLevel(compLevel: CompLevel): FrcApiTournamentLevel {
switch (compLevel) {
case CompLevel.Qualification:
return "qual";
case CompLevel.Quarterfinal:
return "playoff";
case CompLevel.Semifinal:
return "playoff";
case CompLevel.Final:
return "playoff";
default:
throw new Error(`Comp level ${compLevel} is invalid.`);
}
}

export function toFrcEventsUrlTournamentLevel(compLevel: CompLevel): string {
switch (compLevel) {
case CompLevel.Qualification:
return "qualifications";
case CompLevel.Quarterfinal:
return "playoffs";
case CompLevel.Semifinal:
return "playoffs";
case CompLevel.Final:
return "playoffs";
default:
throw new Error(`Comp level ${compLevel} is invalid.`);
}
}
2 changes: 2 additions & 0 deletions server/src/models/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface ISettings {
sandboxModeEnabled: boolean,
youTubeVideoPrivacy: string,
linkVideosOnTheBlueAlliance: boolean;
useFrcEventsApi: boolean;
}

export type SettingsKey = keyof ISettings;
Expand All @@ -20,6 +21,7 @@ export interface ISecretSettings {
theBlueAllianceReadApiKey: string,
theBlueAllianceTrustedApiAuthId: string,
theBlueAllianceTrustedApiAuthSecret: string,
frcEventsApiKey: string,
[key: string]: string,
}

Expand Down
Loading

0 comments on commit 71ffca1

Please sign in to comment.