blob: 431ec711848c8ce1439c08fc6dd56681ab9ad0b6 [file] [log] [blame]
Filip Kujawa7dd49952022-12-02 12:09:13 -08001import {Component, HostListener} from '@angular/core';
Alex Perrybb901052022-03-23 19:46:15 -07002import {Builder, ByteBuffer} from 'flatbuffers';
Philipp Schraderd7efa2b2023-02-17 21:15:13 -08003import {ErrorResponse} from '../../webserver/requests/messages/error_response_generated';
4import {RequestNotesForTeam} from '../../webserver/requests/messages/request_notes_for_team_generated';
Philipp Schrader817cce32022-03-26 15:00:00 -07005import {
6 Note as NoteFb,
7 RequestNotesForTeamResponse,
Philipp Schraderd7efa2b2023-02-17 21:15:13 -08008} from '../../webserver/requests/messages/request_notes_for_team_response_generated';
9import {SubmitNotes} from '../../webserver/requests/messages/submit_notes_generated';
10import {SubmitNotesResponse} from '../../webserver/requests/messages/submit_notes_response_generated';
Alex Perrybb901052022-03-23 19:46:15 -070011
Filip Kujawaf947cb42022-11-21 10:00:30 -080012/*
13For new games, the keywords being used will likely need to be updated.
14To update the keywords complete the following:
15 1) Update the Keywords Interface and KEYWORD_CHECKBOX_LABELS in notes.component.ts
16 The keys of Keywords and KEYWORD_CHECKBOX_LABELS should match.
17 2) In notes.component.ts, update the setTeamNumber() method with the new keywords.
18 3) Add/Edit the new keywords in /scouting/webserver/requests/messages/submit_notes.fbs.
19 4) In notes.component.ts, update the submitData() method with the newKeywords
20 so that it matches the updated flatbuffer
21 5) In db.go, update the NotesData struct and the
22 AddNotes method with the new keywords
23 6) In db_test.go update the TestNotes method so the test uses the keywords
24 7) Update the submitNoteScoutingHandler in requests.go with the new keywords
25 8) Finally, update the corresponding test in requests_test.go (TestSubmitNotes)
26
27 Note: If you change the number of keywords you might need to
28 update how they are displayed in notes.ng.html
29*/
30
31// TeamSelection: Display form to add a team to the teams being scouted.
32// Data: Display the note textbox and keyword selection form
33// for all the teams being scouted.
Philipp Schrader817cce32022-03-26 15:00:00 -070034type Section = 'TeamSelection' | 'Data';
Alex Perrybb901052022-03-23 19:46:15 -070035
Filip Kujawaf947cb42022-11-21 10:00:30 -080036// Every keyword checkbox corresponds to a boolean.
37// If the boolean is True, the checkbox is selected
38// and the note scout saw that the robot being scouted
39// displayed said property (ex. Driving really well -> goodDriving)
40interface Keywords {
41 goodDriving: boolean;
42 badDriving: boolean;
Filip Kujawa11dc4c92023-04-13 08:55:43 -070043 solidPlacing: boolean;
Filip Kujawa7ddd5652023-03-07 19:56:15 -080044 sketchyPlacing: boolean;
Filip Kujawaf947cb42022-11-21 10:00:30 -080045 goodDefense: boolean;
46 badDefense: boolean;
Filip Kujawa7ddd5652023-03-07 19:56:15 -080047 easilyDefended: boolean;
Alex Perrybb901052022-03-23 19:46:15 -070048}
49
Filip Kujawaf947cb42022-11-21 10:00:30 -080050interface Input {
Emily Markovae68b7632023-12-30 14:17:55 -080051 teamNumber: string;
Filip Kujawaf947cb42022-11-21 10:00:30 -080052 notesData: string;
53 keywordsData: Keywords;
54}
55
56const KEYWORD_CHECKBOX_LABELS = {
57 goodDriving: 'Good Driving',
58 badDriving: 'Bad Driving',
Emily Markovaa287ac52024-03-07 19:28:58 -080059 solidPlacing: 'Solid Shooting',
60 sketchyPlacing: 'Sketchy Shooting',
Filip Kujawaf947cb42022-11-21 10:00:30 -080061 goodDefense: 'Good Defense',
62 badDefense: 'Bad Defense',
Filip Kujawa7ddd5652023-03-07 19:56:15 -080063 easilyDefended: 'Easily Defended',
Filip Kujawaf947cb42022-11-21 10:00:30 -080064} as const;
65
Alex Perrybb901052022-03-23 19:46:15 -070066@Component({
67 selector: 'frc971-notes',
68 templateUrl: './notes.ng.html',
Philipp Schrader175a93c2023-02-19 13:13:40 -080069 styleUrls: ['../app/common.css', './notes.component.css'],
Alex Perrybb901052022-03-23 19:46:15 -070070})
71export class Notes {
Filip Kujawaf947cb42022-11-21 10:00:30 -080072 // Re-export KEYWORD_CHECKBOX_LABELS so that we can
73 // use it in the checkbox properties.
74 readonly KEYWORD_CHECKBOX_LABELS = KEYWORD_CHECKBOX_LABELS;
75
76 // Necessary in order to iterate the keys of KEYWORD_CHECKBOX_LABELS.
77 Object = Object;
78
Alex Perrybb901052022-03-23 19:46:15 -070079 section: Section = 'TeamSelection';
Alex Perrybb901052022-03-23 19:46:15 -070080
81 errorMessage = '';
Emily Markovae68b7632023-12-30 14:17:55 -080082 teamNumberSelection: string = '971';
Alex Perrybb901052022-03-23 19:46:15 -070083
Filip Kujawaf947cb42022-11-21 10:00:30 -080084 // Data inputted by user is stored in this array.
85 // Includes the team number, notes, and keyword selection.
86 newData: Input[] = [];
Alex Perrybb901052022-03-23 19:46:15 -070087
Filip Kujawa7dd49952022-12-02 12:09:13 -080088 // Keyboard shortcuts to switch between text areas.
89 // Listens for Ctrl + number and focuses on the
90 // corresponding textbox.
91 // More Info: https://angular.io/api/core/HostListener
92
93 @HostListener('window:keyup', ['$event'])
94 onEvent(event: KeyboardEvent) {
95 if (event.ctrlKey) {
96 if (event.code.includes('Digit')) {
97 this.handleFocus(event.key);
98 }
99 }
100 }
101
102 handleFocus(digit: string) {
103 let textArea = <HTMLInputElement>(
104 document.getElementById('text-input-' + digit)
105 );
106 if (textArea != null) {
107 textArea.focus();
108 }
109 }
110
Filip Kujawaf947cb42022-11-21 10:00:30 -0800111 setTeamNumber() {
112 let data: Input = {
113 teamNumber: this.teamNumberSelection,
Filip Kujawa6534a582023-03-31 16:19:42 -0700114 notesData: 'Match: \nAuto: \nTeleop: \nEndgame: ',
Filip Kujawaf947cb42022-11-21 10:00:30 -0800115 keywordsData: {
116 goodDriving: false,
117 badDriving: false,
Filip Kujawa11dc4c92023-04-13 08:55:43 -0700118 solidPlacing: false,
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800119 sketchyPlacing: false,
Filip Kujawaf947cb42022-11-21 10:00:30 -0800120 goodDefense: false,
121 badDefense: false,
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800122 easilyDefended: false,
Filip Kujawaf947cb42022-11-21 10:00:30 -0800123 },
124 };
Alex Perrybb901052022-03-23 19:46:15 -0700125
Filip Kujawaf947cb42022-11-21 10:00:30 -0800126 this.newData.push(data);
127 this.section = 'Data';
128 }
Alex Perrybb901052022-03-23 19:46:15 -0700129
Filip Kujawaf947cb42022-11-21 10:00:30 -0800130 removeTeam(index: number) {
131 this.newData.splice(index, 1);
132 if (this.newData.length == 0) {
133 this.section = 'TeamSelection';
Alex Perrybb901052022-03-23 19:46:15 -0700134 } else {
Filip Kujawaf947cb42022-11-21 10:00:30 -0800135 this.section = 'Data';
Alex Perrybb901052022-03-23 19:46:15 -0700136 }
137 }
138
Filip Kujawaf947cb42022-11-21 10:00:30 -0800139 addTeam() {
Philipp Schrader817cce32022-03-26 15:00:00 -0700140 this.section = 'TeamSelection';
Alex Perrybb901052022-03-23 19:46:15 -0700141 }
Philipp Schrader817cce32022-03-26 15:00:00 -0700142
Alex Perrybb901052022-03-23 19:46:15 -0700143 async submitData() {
Filip Kujawaf947cb42022-11-21 10:00:30 -0800144 for (let i = 0; i < this.newData.length; i++) {
145 const builder = new Builder();
146 const dataFb = builder.createString(this.newData[i].notesData);
Filip Kujawaba55bb32022-12-02 14:08:32 -0800147
Emily Markovae68b7632023-12-30 14:17:55 -0800148 const teamNumber = builder.createString(this.newData[i].teamNumber);
149
Filip Kujawaf947cb42022-11-21 10:00:30 -0800150 builder.finish(
151 SubmitNotes.createSubmitNotes(
152 builder,
Emily Markovae68b7632023-12-30 14:17:55 -0800153 teamNumber,
Filip Kujawaf947cb42022-11-21 10:00:30 -0800154 dataFb,
155 this.newData[i].keywordsData.goodDriving,
156 this.newData[i].keywordsData.badDriving,
Filip Kujawa11dc4c92023-04-13 08:55:43 -0700157 this.newData[i].keywordsData.solidPlacing,
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800158 this.newData[i].keywordsData.sketchyPlacing,
Filip Kujawaf947cb42022-11-21 10:00:30 -0800159 this.newData[i].keywordsData.goodDefense,
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800160 this.newData[i].keywordsData.badDefense,
161 this.newData[i].keywordsData.easilyDefended
Filip Kujawaf947cb42022-11-21 10:00:30 -0800162 )
163 );
Alex Perrybb901052022-03-23 19:46:15 -0700164
Filip Kujawaf947cb42022-11-21 10:00:30 -0800165 const buffer = builder.asUint8Array();
166 const res = await fetch('/requests/submit/submit_notes', {
167 method: 'POST',
168 body: buffer,
169 });
Alex Perrybb901052022-03-23 19:46:15 -0700170
Filip Kujawaf947cb42022-11-21 10:00:30 -0800171 if (!res.ok) {
172 const resBuffer = await res.arrayBuffer();
173 const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
174 const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
175 const errorMessage = parsedResponse.errorMessage();
176 this.errorMessage = `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
177 }
Alex Perrybb901052022-03-23 19:46:15 -0700178 }
Filip Kujawaf947cb42022-11-21 10:00:30 -0800179
180 this.newData = [];
181 this.errorMessage = '';
182 this.section = 'TeamSelection';
Alex Perrybb901052022-03-23 19:46:15 -0700183 }
Philipp Schrader02db74b2023-02-17 20:36:58 -0800184
185 labelToId(label: String): String {
186 return label.replaceAll(' ', '_').toLowerCase();
187 }
Alex Perrybb901052022-03-23 19:46:15 -0700188}