blob: 26e816b3ba8f520b124b5a2e2a7167c354466106 [file] [log] [blame]
Ishan Katpallydad5f1a2022-03-23 21:06:36 -07001import {Component, OnInit} from '@angular/core';
Filip Kujawac1ded372023-05-27 14:33:43 -07002import {Builder, ByteBuffer} from 'flatbuffers';
3import {ErrorResponse} from '../../webserver/requests/messages/error_response_generated';
Filip Kujawab5a16e32022-12-14 13:21:56 -08004import {
5 Ranking,
6 RequestAllDriverRankingsResponse,
Philipp Schraderd7efa2b2023-02-17 21:15:13 -08007} from '../../webserver/requests/messages/request_all_driver_rankings_response_generated';
Filip Kujawab5a16e32022-12-14 13:21:56 -08008import {
Emily Markova132e5be2023-03-25 13:43:05 -07009 Stats2023,
10 Request2023DataScoutingResponse,
11} from '../../webserver/requests/messages/request_2023_data_scouting_response_generated';
Filip Kujawab5a16e32022-12-14 13:21:56 -080012import {
13 Note,
14 RequestAllNotesResponse,
Philipp Schraderd7efa2b2023-02-17 21:15:13 -080015} from '../../webserver/requests/messages/request_all_notes_response_generated';
Filip Kujawac1ded372023-05-27 14:33:43 -070016import {Delete2023DataScouting} from '../../webserver/requests/messages/delete_2023_data_scouting_generated';
17import {Delete2023DataScoutingResponse} from '../../webserver/requests/messages/delete_2023_data_scouting_response_generated';
Filip Kujawab5a16e32022-12-14 13:21:56 -080018
Philipp Schrader175a93c2023-02-19 13:13:40 -080019import {ViewDataRequestor} from '../rpc';
Filip Kujawab5a16e32022-12-14 13:21:56 -080020
Emily Markova132e5be2023-03-25 13:43:05 -070021type Source = 'Notes' | 'Stats2023' | 'DriverRanking';
Filip Kujawab5a16e32022-12-14 13:21:56 -080022
23//TODO(Filip): Deduplicate
24const COMP_LEVEL_LABELS = {
25 qm: 'Qualifications',
26 ef: 'Eighth Finals',
27 qf: 'Quarter Finals',
28 sf: 'Semi Finals',
29 f: 'Finals',
30};
Ishan Katpallydad5f1a2022-03-23 21:06:36 -070031
32@Component({
33 selector: 'app-view',
34 templateUrl: './view.ng.html',
Philipp Schrader175a93c2023-02-19 13:13:40 -080035 styleUrls: ['../app/common.css', './view.component.css'],
Ishan Katpallydad5f1a2022-03-23 21:06:36 -070036})
Filip Kujawab5a16e32022-12-14 13:21:56 -080037export class ViewComponent {
38 constructor(private readonly viewDataRequestor: ViewDataRequestor) {}
39
40 // Make COMP_LEVEL_LABELS available in view.ng.html.
41 readonly COMP_LEVEL_LABELS = COMP_LEVEL_LABELS;
42
43 // Progress and error messages to display to
44 // the user when fetching data.
45 progressMessage: string = '';
46 errorMessage: string = '';
47
48 // The current data source being displayed.
49 currentSource: Source = 'Notes';
50
51 // Current sort (ascending/descending match numbers).
52 // noteList is sorted based on team number until match
53 // number is added for note scouting.
54 ascendingSort = true;
55
56 // Stores the corresponding data.
57 noteList: Note[] = [];
58 driverRankingList: Ranking[] = [];
Emily Markova132e5be2023-03-25 13:43:05 -070059 statList: Stats2023[] = [];
Filip Kujawab5a16e32022-12-14 13:21:56 -080060
61 // Fetch notes on initialization.
62 ngOnInit() {
63 this.fetchCurrentSource();
64 }
65
66 // Called when a user changes the sort direction.
67 // Changes the data order between ascending/descending.
68 sortData() {
69 this.ascendingSort = !this.ascendingSort;
70 if (!this.ascendingSort) {
71 this.driverRankingList.sort((a, b) => b.matchNumber() - a.matchNumber());
Emily Markovae68b7632023-12-30 14:17:55 -080072 this.noteList.sort(function (a, b) {
73 return b[0]
74 .team()
75 .localeCompare(a[0].team(), undefined, {numeric: true});
76 });
Emily Markova132e5be2023-03-25 13:43:05 -070077 this.statList.sort((a, b) => b.matchNumber() - a.matchNumber());
Filip Kujawab5a16e32022-12-14 13:21:56 -080078 } else {
79 this.driverRankingList.sort((a, b) => a.matchNumber() - b.matchNumber());
Emily Markovae68b7632023-12-30 14:17:55 -080080 this.noteList.sort(function (a, b) {
81 return a[0]
82 .team()
83 .localeCompare(b[0].team(), undefined, {numeric: true});
84 });
Emily Markova132e5be2023-03-25 13:43:05 -070085 this.statList.sort((a, b) => a.matchNumber() - b.matchNumber());
Filip Kujawab5a16e32022-12-14 13:21:56 -080086 }
87 }
88
89 // Called when a user selects a new data source
90 // from the dropdown.
91 switchDataSource(target: Source) {
92 this.currentSource = target;
93 this.progressMessage = '';
94 this.errorMessage = '';
95 this.noteList = [];
96 this.driverRankingList = [];
97 this.statList = [];
98 this.fetchCurrentSource();
99 }
100
101 // Call the method to fetch data for the current source.
102 fetchCurrentSource() {
103 switch (this.currentSource) {
104 case 'Notes': {
105 this.fetchNotes();
106 }
107
Emily Markova132e5be2023-03-25 13:43:05 -0700108 case 'Stats2023': {
109 this.fetchStats2023();
Filip Kujawab5a16e32022-12-14 13:21:56 -0800110 }
111
112 case 'DriverRanking': {
113 this.fetchDriverRanking();
114 }
115 }
116 }
117
118 // TODO(Filip): Add delete functionality.
Filip Kujawac1ded372023-05-27 14:33:43 -0700119 // Gets called when a user clicks the delete icon (note scouting).
120 async deleteNoteData() {
Filip Kujawab5a16e32022-12-14 13:21:56 -0800121 const block_alerts = document.getElementById(
122 'block_alerts'
123 ) as HTMLInputElement;
Filip Kujawac1ded372023-05-27 14:33:43 -0700124 if (block_alerts.checked || window.confirm('Actually delete data?')) {
125 this.errorMessage = 'Deleting data has not been implemented yet.';
126 return;
127 }
128 }
129
130 // TODO(Filip): Add delete functionality.
131 // Gets called when a user clicks the delete icon (driver ranking).
132 async deleteDriverRankingData() {
133 const block_alerts = document.getElementById(
134 'block_alerts'
135 ) as HTMLInputElement;
136 if (block_alerts.checked || window.confirm('Actually delete data?')) {
137 this.errorMessage = 'Deleting data has not been implemented yet.';
138 return;
139 }
140 }
141
142 // Gets called when a user clicks the delete icon.
143 async deleteDataScouting(
144 compLevel: string,
145 matchNumber: number,
146 setNumber: number,
147 teamNumber: string
148 ) {
149 const block_alerts = document.getElementById(
150 'block_alerts'
151 ) as HTMLInputElement;
152 if (block_alerts.checked || window.confirm('Actually delete data?')) {
153 await this.requestDeleteDataScouting(
154 compLevel,
155 matchNumber,
156 setNumber,
157 teamNumber
158 );
159 await this.fetchStats2023();
160 }
161 }
162
163 async requestDeleteDataScouting(
164 compLevel: string,
165 matchNumber: number,
166 setNumber: number,
167 teamNumber: string
168 ) {
169 this.progressMessage = 'Deleting data. Please be patient.';
170 const builder = new Builder();
171 const compLevelData = builder.createString(compLevel);
172 const teamNumberData = builder.createString(teamNumber);
173
174 builder.finish(
175 Delete2023DataScouting.createDelete2023DataScouting(
176 builder,
177 compLevelData,
178 matchNumber,
179 setNumber,
180 teamNumberData
181 )
182 );
183
184 const buffer = builder.asUint8Array();
185 const res = await fetch('/requests/delete/delete_2023_data_scouting', {
186 method: 'POST',
187 body: buffer,
188 });
189
190 if (!res.ok) {
191 const resBuffer = await res.arrayBuffer();
192 const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
193 const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
194 const errorMessage = parsedResponse.errorMessage();
195 this.errorMessage = `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
Filip Kujawab5a16e32022-12-14 13:21:56 -0800196 }
197 }
198
199 // Fetch all driver ranking data and store in driverRankingList.
200 async fetchDriverRanking() {
201 this.progressMessage = 'Fetching driver ranking data. Please be patient.';
202 this.errorMessage = '';
203
204 try {
205 this.driverRankingList =
206 await this.viewDataRequestor.fetchDriverRankingList();
207 this.progressMessage = 'Successfully fetched driver ranking data.';
208 } catch (e) {
209 this.errorMessage = e;
210 this.progressMessage = '';
211 }
212 }
213
214 // Fetch all data scouting (stats) data and store in statList.
Emily Markova132e5be2023-03-25 13:43:05 -0700215 async fetchStats2023() {
Filip Kujawab5a16e32022-12-14 13:21:56 -0800216 this.progressMessage = 'Fetching stats list. Please be patient.';
217 this.errorMessage = '';
218
219 try {
Emily Markova132e5be2023-03-25 13:43:05 -0700220 this.statList = await this.viewDataRequestor.fetchStats2023List();
Filip Kujawab5a16e32022-12-14 13:21:56 -0800221 this.progressMessage = 'Successfully fetched stats list.';
222 } catch (e) {
223 this.errorMessage = e;
224 this.progressMessage = '';
225 }
226 }
227
228 // Fetch all notes data and store in noteList.
229 async fetchNotes() {
230 this.progressMessage = 'Fetching notes list. Please be patient.';
231 this.errorMessage = '';
232
233 try {
234 this.noteList = await this.viewDataRequestor.fetchNoteList();
235 this.progressMessage = 'Successfully fetched note list.';
236 } catch (e) {
237 this.errorMessage = e;
238 this.progressMessage = '';
239 }
240 }
241
242 // Parse all selected keywords for a note entry
243 // into one string to be displayed in the table.
244 parseKeywords(entry: Note) {
245 let parsedKeywords = '';
246
247 if (entry.goodDriving()) {
248 parsedKeywords += 'Good Driving ';
249 }
250 if (entry.badDriving()) {
251 parsedKeywords += 'Bad Driving ';
252 }
Filip Kujawa11dc4c92023-04-13 08:55:43 -0700253 if (entry.solidPlacing()) {
254 parsedKeywords += 'Solid Placing ';
Filip Kujawab5a16e32022-12-14 13:21:56 -0800255 }
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800256 if (entry.sketchyPlacing()) {
Filip Kujawa6f7f0b32023-03-30 13:26:08 -0700257 parsedKeywords += 'Sketchy Placing ';
Filip Kujawab5a16e32022-12-14 13:21:56 -0800258 }
259 if (entry.goodDefense()) {
260 parsedKeywords += 'Good Defense ';
261 }
262 if (entry.badDefense()) {
263 parsedKeywords += 'Bad Defense ';
264 }
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800265 if (entry.easilyDefended()) {
266 parsedKeywords += 'Easily Defended';
267 }
Filip Kujawab5a16e32022-12-14 13:21:56 -0800268
269 return parsedKeywords;
270 }
271}