blob: 21264f2036471d185eb34af09f2f2a912265ebd6 [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 Kujawa7ddd5652023-03-07 19:56:15 -080043 sketchyPickup: boolean;
44 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 {
51 teamNumber: number;
52 notesData: string;
53 keywordsData: Keywords;
54}
55
56const KEYWORD_CHECKBOX_LABELS = {
57 goodDriving: 'Good Driving',
58 badDriving: 'Bad Driving',
Filip Kujawa7ddd5652023-03-07 19:56:15 -080059 sketchyPickup: 'Solid Pickup',
60 sketchyPlacing: 'Sketchy Placing',
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 = '';
Filip Kujawaf947cb42022-11-21 10:00:30 -080082 teamNumberSelection: number = 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 Kujawaba55bb32022-12-02 14:08:32 -0800114 notesData: 'Auto: \nTeleop: \nEngame: ',
Filip Kujawaf947cb42022-11-21 10:00:30 -0800115 keywordsData: {
116 goodDriving: false,
117 badDriving: false,
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800118 sketchyPickup: false,
119 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
Filip Kujawaf947cb42022-11-21 10:00:30 -0800148 builder.finish(
149 SubmitNotes.createSubmitNotes(
150 builder,
151 this.newData[i].teamNumber,
152 dataFb,
153 this.newData[i].keywordsData.goodDriving,
154 this.newData[i].keywordsData.badDriving,
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800155 this.newData[i].keywordsData.sketchyPickup,
156 this.newData[i].keywordsData.sketchyPlacing,
Filip Kujawaf947cb42022-11-21 10:00:30 -0800157 this.newData[i].keywordsData.goodDefense,
Filip Kujawa7ddd5652023-03-07 19:56:15 -0800158 this.newData[i].keywordsData.badDefense,
159 this.newData[i].keywordsData.easilyDefended
Filip Kujawaf947cb42022-11-21 10:00:30 -0800160 )
161 );
Alex Perrybb901052022-03-23 19:46:15 -0700162
Filip Kujawaf947cb42022-11-21 10:00:30 -0800163 const buffer = builder.asUint8Array();
164 const res = await fetch('/requests/submit/submit_notes', {
165 method: 'POST',
166 body: buffer,
167 });
Alex Perrybb901052022-03-23 19:46:15 -0700168
Filip Kujawaf947cb42022-11-21 10:00:30 -0800169 if (!res.ok) {
170 const resBuffer = await res.arrayBuffer();
171 const fbBuffer = new ByteBuffer(new Uint8Array(resBuffer));
172 const parsedResponse = ErrorResponse.getRootAsErrorResponse(fbBuffer);
173 const errorMessage = parsedResponse.errorMessage();
174 this.errorMessage = `Received ${res.status} ${res.statusText}: "${errorMessage}"`;
175 }
Alex Perrybb901052022-03-23 19:46:15 -0700176 }
Filip Kujawaf947cb42022-11-21 10:00:30 -0800177
178 this.newData = [];
179 this.errorMessage = '';
180 this.section = 'TeamSelection';
Alex Perrybb901052022-03-23 19:46:15 -0700181 }
Philipp Schrader02db74b2023-02-17 20:36:58 -0800182
183 labelToId(label: String): String {
184 return label.replaceAll(' ', '_').toLowerCase();
185 }
Alex Perrybb901052022-03-23 19:46:15 -0700186}