Scouting: Add ability to scout offline
Signed-off-by: Filip Kujawa <filip.j.kujawa@gmail.com>
Change-Id: I0e0c0be033824c05b6c239d38eb79d95745fceec
diff --git a/scouting/www/rpc/BUILD b/scouting/www/rpc/BUILD
index d1367ea..592735c 100644
--- a/scouting/www/rpc/BUILD
+++ b/scouting/www/rpc/BUILD
@@ -10,6 +10,7 @@
],
generate_public_api = False,
deps = [
+ "//:node_modules/dexie",
"//scouting/webserver/requests/messages:error_response_ts_fbs",
"//scouting/webserver/requests/messages:request_2024_data_scouting_response_ts_fbs",
"//scouting/webserver/requests/messages:request_2024_data_scouting_ts_fbs",
diff --git a/scouting/www/rpc/db.ts b/scouting/www/rpc/db.ts
new file mode 100644
index 0000000..789ac0e
--- /dev/null
+++ b/scouting/www/rpc/db.ts
@@ -0,0 +1,18 @@
+import Dexie, {Table} from 'dexie';
+
+export interface MatchListData {
+ id?: number;
+ data: Uint8Array;
+}
+
+export class AppDB extends Dexie {
+ matchListData!: Table<MatchListData, number>;
+
+ constructor() {
+ super('ngdexieliveQuery');
+ this.version(1).stores({
+ matchListData: 'id,data',
+ });
+ }
+}
+export const db = new AppDB();
diff --git a/scouting/www/rpc/match_list_requestor.ts b/scouting/www/rpc/match_list_requestor.ts
index fa2dcbd..0a812ce 100644
--- a/scouting/www/rpc/match_list_requestor.ts
+++ b/scouting/www/rpc/match_list_requestor.ts
@@ -6,78 +6,82 @@
Match,
RequestAllMatchesResponse,
} from '../../webserver/requests/messages/request_all_matches_response_generated';
-
+import {db, MatchListData} from './db';
const MATCH_TYPE_ORDERING = ['qm', 'ef', 'qf', 'sf', 'f'];
-
@Injectable({providedIn: 'root'})
export class MatchListRequestor {
async fetchMatchList(): Promise<Match[]> {
const builder = new Builder();
RequestAllMatches.startRequestAllMatches(builder);
builder.finish(RequestAllMatches.endRequestAllMatches(builder));
-
const buffer = builder.asUint8Array();
const res = await fetch('/requests/request/all_matches', {
method: 'POST',
body: buffer,
});
-
if (res.ok) {
const resBuffer = await res.arrayBuffer();
- const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
- const parsedResponse =
- RequestAllMatchesResponse.getRootAsRequestAllMatchesResponse(fbBuffer);
-
- // Convert the flatbuffer list into an array. That's more useful.
- const matchList = [];
- for (let i = 0; i < parsedResponse.matchListLength(); i++) {
- matchList.push(parsedResponse.matchList(i));
- }
-
- // Sort the list so it is in chronological order.
- matchList.sort((a, b) => {
- // First sort by match type. E.g. finals are last.
- const aMatchTypeIndex = MATCH_TYPE_ORDERING.indexOf(a.compLevel());
- const bMatchTypeIndex = MATCH_TYPE_ORDERING.indexOf(b.compLevel());
- if (aMatchTypeIndex < bMatchTypeIndex) {
- return -1;
- }
- if (aMatchTypeIndex > bMatchTypeIndex) {
- return 1;
- }
- // Then sort by match number. E.g. in semi finals, all match 1 rounds
- // are done first. Then come match 2 rounds. And then, if necessary,
- // the match 3 rounds.
- const aMatchNumber = a.matchNumber();
- const bMatchNumber = b.matchNumber();
- if (aMatchNumber < bMatchNumber) {
- return -1;
- }
- if (aMatchNumber > bMatchNumber) {
- return 1;
- }
- // Lastly, sort by set number. I.e. Semi Final 1 Match 1 happens first.
- // Then comes Semi Final 2 Match 1. Then comes Semi Final 1 Match 2. Then
- // Semi Final 2 Match 2.
- const aSetNumber = a.setNumber();
- const bSetNumber = b.setNumber();
- if (aSetNumber < bSetNumber) {
- return -1;
- }
- if (aSetNumber > bSetNumber) {
- return 1;
- }
- return 0;
- });
-
- return matchList;
+ const u8Buffer = new Uint8Array(resBuffer);
+ // Cache the response.
+ await db.matchListData.put({id: 1, data: u8Buffer});
+ return this.parseMatchList(u8Buffer);
} else {
+ const cachedResult = await db.matchListData.where({id: 1}).toArray();
+ if (cachedResult && cachedResult.length == 1) {
+ const u8Buffer = cachedResult[0].data;
+ return this.parseMatchList(u8Buffer);
+ }
const resBuffer = await res.arrayBuffer();
const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
-
const errorMessage = parsedResponse.errorMessage();
throw `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
}
}
+ parseMatchList(u8Buffer: Uint8Array): Match[] {
+ const fbBuffer = new ByteBuffer(u8Buffer);
+ const parsedResponse =
+ RequestAllMatchesResponse.getRootAsRequestAllMatchesResponse(fbBuffer);
+ // Convert the flatbuffer list into an array. That's more useful.
+ const matchList = [];
+ for (let i = 0; i < parsedResponse.matchListLength(); i++) {
+ matchList.push(parsedResponse.matchList(i));
+ }
+ // Sort the list so it is in chronological order.
+ matchList.sort((a, b) => {
+ // First sort by match type. E.g. finals are last.
+ const aMatchTypeIndex = MATCH_TYPE_ORDERING.indexOf(a.compLevel());
+ const bMatchTypeIndex = MATCH_TYPE_ORDERING.indexOf(b.compLevel());
+ if (aMatchTypeIndex < bMatchTypeIndex) {
+ return -1;
+ }
+ if (aMatchTypeIndex > bMatchTypeIndex) {
+ return 1;
+ }
+ // Then sort by match number. E.g. in semi finals, all match 1 rounds
+ // are done first. Then come match 2 rounds. And then, if necessary,
+ // the match 3 rounds.
+ const aMatchNumber = a.matchNumber();
+ const bMatchNumber = b.matchNumber();
+ if (aMatchNumber < bMatchNumber) {
+ return -1;
+ }
+ if (aMatchNumber > bMatchNumber) {
+ return 1;
+ }
+ // Lastly, sort by set number. I.e. Semi Final 1 Match 1 happens first.
+ // Then comes Semi Final 2 Match 1. Then comes Semi Final 1 Match 2. Then
+ // Semi Final 2 Match 2.
+ const aSetNumber = a.setNumber();
+ const bSetNumber = b.setNumber();
+ if (aSetNumber < bSetNumber) {
+ return -1;
+ }
+ if (aSetNumber > bSetNumber) {
+ return 1;
+ }
+ return 0;
+ });
+ return matchList;
+ }
}