Spaces:
Runtime error
Runtime error
Michelle Lam
commited on
Commit
•
1882b75
1
Parent(s):
9fa8307
Adds interaction flow to formally send reports to AVID (reviewing reports and adding additional info); puts print statements behind debug flag
Browse files- audit_utils.py +31 -3
- indie_label_svelte/src/HypothesisPanel.svelte +270 -291
- indie_label_svelte/src/SubmitReportDialog.svelte +120 -0
- server.py +42 -12
audit_utils.py
CHANGED
@@ -317,6 +317,25 @@ def get_grp_model_labels(n_label_per_bin, score_bins, grp_ids):
|
|
317 |
|
318 |
########################################
|
319 |
# SAVE_REPORT utils
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
320 |
# Convert indielabel json to AVID json format.
|
321 |
# See the AVID format in https://avidml.org/avidtools/reference/report
|
322 |
#
|
@@ -330,7 +349,7 @@ def get_grp_model_labels(n_label_per_bin, score_bins, grp_ids):
|
|
330 |
# user_rating personal_model_score 0.92
|
331 |
# user_decision user_decision "Non-toxic"
|
332 |
# Note that this is at the individual report level.
|
333 |
-
def convert_indie_label_json_to_avid_json(indie_label_json):
|
334 |
|
335 |
# Setting up the structure with a dict to enable programmatic additions
|
336 |
avid_json_dict = {
|
@@ -379,12 +398,21 @@ def convert_indie_label_json_to_avid_json(indie_label_json):
|
|
379 |
"taxonomy_version": "0.2"
|
380 |
}
|
381 |
},
|
382 |
-
"credit":
|
383 |
"reported_date": "" # Leaving empty so that it can be dynamically filled in
|
384 |
}
|
385 |
|
386 |
-
avid_json_dict["description"] = indie_label_json
|
387 |
avid_json_dict["reported_date"] = str(date.today())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
388 |
for e in indie_label_json["evidence"]:
|
389 |
curr_metric = {}
|
390 |
curr_metric["name"] = "Perspective API"
|
|
|
317 |
|
318 |
########################################
|
319 |
# SAVE_REPORT utils
|
320 |
+
|
321 |
+
# Convert the SEP field selection from the UI to the SEP enum value
|
322 |
+
def get_sep_enum(sep_selection):
|
323 |
+
if sep_selection == "Adversarial Example":
|
324 |
+
return "S0403: Adversarial Example"
|
325 |
+
elif sep_selection == "Accuracy":
|
326 |
+
return "P0204: Accuracy"
|
327 |
+
elif sep_selection == "Bias/Discrimination":
|
328 |
+
return "E0100: Bias/ Discrimination"
|
329 |
+
else:
|
330 |
+
return "P0200: Model issues"
|
331 |
+
|
332 |
+
# Format the description for the report including the provided title, error type, and text entry field ("Summary/Suggestions" text box)
|
333 |
+
def format_description(indie_label_json):
|
334 |
+
title = indie_label_json["title"]
|
335 |
+
error_type = indie_label_json["error_type"]
|
336 |
+
text_entry = indie_label_json["text_entry"]
|
337 |
+
return f"Title: {title}\nError Type: {error_type}\nSummary/Suggestions: {text_entry}"
|
338 |
+
|
339 |
# Convert indielabel json to AVID json format.
|
340 |
# See the AVID format in https://avidml.org/avidtools/reference/report
|
341 |
#
|
|
|
349 |
# user_rating personal_model_score 0.92
|
350 |
# user_decision user_decision "Non-toxic"
|
351 |
# Note that this is at the individual report level.
|
352 |
+
def convert_indie_label_json_to_avid_json(indie_label_json, cur_user, email, sep_selection):
|
353 |
|
354 |
# Setting up the structure with a dict to enable programmatic additions
|
355 |
avid_json_dict = {
|
|
|
398 |
"taxonomy_version": "0.2"
|
399 |
}
|
400 |
},
|
401 |
+
"credit": "", # Leaving empty so that credit can be assigned
|
402 |
"reported_date": "" # Leaving empty so that it can be dynamically filled in
|
403 |
}
|
404 |
|
405 |
+
avid_json_dict["description"] = format_description(indie_label_json)
|
406 |
avid_json_dict["reported_date"] = str(date.today())
|
407 |
+
# Assign credit to email if provided, otherwise default to randomly assigned username
|
408 |
+
if email != "":
|
409 |
+
avid_json_dict["credit"] = email
|
410 |
+
else:
|
411 |
+
avid_json_dict["credit"] = cur_user
|
412 |
+
|
413 |
+
sep_enum = get_sep_enum(sep_selection)
|
414 |
+
avid_json_dict["impact"]["avid"]["sep_view"] = [sep_enum]
|
415 |
+
|
416 |
for e in indie_label_json["evidence"]:
|
417 |
curr_metric = {}
|
418 |
curr_metric["name"] = "Perspective API"
|
indie_label_svelte/src/HypothesisPanel.svelte
CHANGED
@@ -1,6 +1,7 @@
|
|
1 |
<script lang="ts">
|
2 |
import { onMount } from "svelte";
|
3 |
import ClusterResults from "./ClusterResults.svelte";
|
|
|
4 |
|
5 |
import Button, { Label } from "@smui/button";
|
6 |
import Textfield from '@smui/textfield';
|
@@ -31,6 +32,8 @@
|
|
31 |
topic_chosen.subscribe(value => {
|
32 |
cur_topic = value;
|
33 |
});
|
|
|
|
|
34 |
|
35 |
// Handle routing
|
36 |
let searchParams = new URLSearchParams(window.location.search);
|
@@ -202,333 +205,309 @@
|
|
202 |
// Save current error type
|
203 |
async function updateErrorType() {
|
204 |
// Update error type on main page to be the selected error type
|
205 |
-
// error_type.update((value) => cur_error_type);
|
206 |
-
// selected["error_type"] = cur_error_type;
|
207 |
editErrorType = false;
|
208 |
}
|
209 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
210 |
</script>
|
211 |
|
212 |
-
<div
|
213 |
-
|
214 |
-
<
|
215 |
-
|
216 |
-
|
217 |
-
|
218 |
-
|
219 |
-
|
220 |
-
|
221 |
-
|
222 |
-
|
223 |
-
<
|
224 |
-
|
225 |
-
color="
|
226 |
-
|
227 |
-
|
228 |
-
>
|
229 |
-
|
230 |
-
<
|
231 |
-
|
232 |
-
{
|
233 |
-
|
234 |
-
{
|
235 |
-
|
236 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
237 |
</div>
|
238 |
</div>
|
239 |
-
</div>
|
240 |
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
246 |
-
|
247 |
-
|
248 |
-
|
249 |
-
|
250 |
-
|
251 |
-
|
252 |
-
|
253 |
-
|
254 |
-
|
255 |
-
|
256 |
-
|
257 |
-
|
258 |
-
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
|
268 |
-
|
269 |
-
|
270 |
-
|
271 |
-
{:else}
|
272 |
-
<Graphic class="material-icons" aria-hidden="true">radio_button_unchecked</Graphic>
|
273 |
-
{/if}
|
274 |
-
<Text>
|
275 |
-
<PrimaryText>
|
276 |
-
{report["title"]}
|
277 |
-
</PrimaryText>
|
278 |
-
<SecondaryText>
|
279 |
-
{report["error_type"]}
|
280 |
-
</SecondaryText>
|
281 |
-
</Text>
|
282 |
-
</Item>
|
283 |
-
{/each}
|
284 |
-
</List>
|
285 |
-
</Content>
|
286 |
-
</Drawer>
|
287 |
-
{/key}
|
288 |
-
<AppContent class="app-content">
|
289 |
-
<main class="main-content">
|
290 |
-
{#if selected}
|
291 |
-
<div class="head_6_highlight">
|
292 |
-
Current Report
|
293 |
-
</div>
|
294 |
-
<div class="panel_contents2">
|
295 |
-
<!-- Title -->
|
296 |
-
<div class="spacing_vert">
|
297 |
-
<div class="edit_button_row">
|
298 |
-
{#if editTitle}
|
299 |
-
<div class="edit_button_row_input">
|
300 |
-
<Textfield
|
301 |
-
bind:value={selected["title"]}
|
302 |
-
label="Your report title"
|
303 |
-
input$rows={4}
|
304 |
-
textarea
|
305 |
-
variant="outlined"
|
306 |
-
style="width: 100%;"
|
307 |
-
helperLine$style="width: 100%;"
|
308 |
-
/>
|
309 |
-
</div>
|
310 |
-
<div>
|
311 |
-
<IconButton class="material-icons grey_button" size="button" on:click={() => (editTitle = false)}>
|
312 |
-
check
|
313 |
-
</IconButton>
|
314 |
-
</div>
|
315 |
-
{:else}
|
316 |
-
{#if selected["title"] != ""}
|
317 |
-
<div class="head_5">
|
318 |
-
{selected["title"]}
|
319 |
-
</div>
|
320 |
{:else}
|
321 |
-
|
322 |
{/if}
|
323 |
-
|
324 |
-
|
325 |
-
|
326 |
-
|
327 |
-
|
328 |
-
|
329 |
-
|
330 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
331 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
332 |
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
|
|
|
|
|
|
337 |
</div>
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
|
|
|
|
|
|
|
|
|
|
344 |
<FormField>
|
345 |
<Radio bind:group={selected["error_type"]} value={e.opt} on:change={updateErrorType} color="secondary" />
|
346 |
<span slot="label">
|
347 |
-
{e.opt}
|
348 |
-
<IconButton class="material-icons" size="button" disabled>help_outline</IconButton>
|
349 |
</span>
|
350 |
</FormField>
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
</span>
|
359 |
-
</FormField>
|
360 |
</div>
|
361 |
-
{
|
362 |
-
|
363 |
-
|
364 |
-
|
365 |
-
check
|
366 |
-
</IconButton>
|
367 |
-
</div> -->
|
368 |
-
{:else}
|
369 |
-
{#if selected["error_type"] != ""}
|
370 |
<div>
|
371 |
-
<
|
|
|
|
|
372 |
</div>
|
373 |
-
{:else}
|
374 |
-
<div class="grey_text">Select an error type</div>
|
375 |
{/if}
|
376 |
-
|
377 |
-
<div>
|
378 |
-
<IconButton class="material-icons grey_button" size="button" on:click={() => (editErrorType = true)}>
|
379 |
-
create
|
380 |
-
</IconButton>
|
381 |
-
</div>
|
382 |
-
{/if}
|
383 |
-
</div>
|
384 |
-
</div>
|
385 |
-
|
386 |
-
<!-- Evidence -->
|
387 |
-
<div class="spacing_vert_40">
|
388 |
-
<div class="head_6">
|
389 |
-
<b>Evidence</b>
|
390 |
</div>
|
391 |
-
|
392 |
-
|
393 |
-
|
394 |
-
<
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
407 |
-
|
408 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
409 |
</div>
|
410 |
-
{/key}
|
411 |
-
</div>
|
412 |
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
|
|
|
|
427 |
</div>
|
428 |
-
|
429 |
-
</div>
|
430 |
|
431 |
-
|
432 |
-
|
433 |
-
|
434 |
-
|
435 |
-
|
436 |
-
|
|
|
|
|
437 |
</div>
|
438 |
-
|
439 |
</div>
|
440 |
-
|
441 |
-
|
442 |
-
</
|
443 |
-
</
|
444 |
-
|
445 |
-
{
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
450 |
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
459 |
-
|
460 |
-
|
461 |
-
|
462 |
-
|
463 |
-
|
464 |
-
|
465 |
-
|
466 |
-
|
467 |
-
|
468 |
-
|
469 |
-
|
470 |
-
|
471 |
-
|
472 |
-
|
473 |
-
|
474 |
-
on:click={handleSaveReport}
|
475 |
-
variant="outlined"
|
476 |
-
color="secondary"
|
477 |
-
>
|
478 |
-
<Label>Save</Label>
|
479 |
-
</Button>
|
480 |
-
|
481 |
-
<div>
|
482 |
-
<span style="color: grey"><i>Last saved:
|
483 |
-
{#await promise_save}
|
484 |
-
<CircularProgress style="height: 32px; width: 32px;" indeterminate />
|
485 |
-
{:then result}
|
486 |
-
{#if result}
|
487 |
-
{new Date().toLocaleTimeString()}
|
488 |
-
{:else}
|
489 |
-
—
|
490 |
-
{/if}
|
491 |
-
{:catch error}
|
492 |
-
<p style="color: red">{error.message}</p>
|
493 |
-
{/await}
|
494 |
-
</i></span>
|
495 |
</div>
|
496 |
</div>
|
|
|
497 |
</div>
|
498 |
-
{/if}
|
499 |
-
<!-- TEMP -->
|
500 |
-
<!-- {#key model}
|
501 |
-
<div>Model: {model}</div>
|
502 |
-
{/key} -->
|
503 |
</div>
|
504 |
|
505 |
<style>
|
506 |
-
/* Drawer */
|
507 |
-
/* .drawer-container {
|
508 |
-
position: relative;
|
509 |
-
display: flex;
|
510 |
-
height: 350px;
|
511 |
-
max-width: 600px;
|
512 |
-
border: 1px solid
|
513 |
-
var(--mdc-theme-text-hint-on-background, rgba(0, 0, 0, 0.1));
|
514 |
-
overflow: hidden;
|
515 |
-
z-index: 0;
|
516 |
-
}
|
517 |
-
|
518 |
-
* :global(.app-content) {
|
519 |
-
flex: auto;
|
520 |
-
overflow: auto;
|
521 |
-
position: relative;
|
522 |
-
flex-grow: 1;
|
523 |
-
}
|
524 |
-
|
525 |
-
.main-content {
|
526 |
-
overflow: auto;
|
527 |
-
padding: 16px;
|
528 |
-
height: 100%;
|
529 |
-
box-sizing: border-box;
|
530 |
-
} */
|
531 |
-
|
532 |
.panel_contents {
|
533 |
padding: 0 20px;
|
534 |
overflow-y: auto;
|
|
|
1 |
<script lang="ts">
|
2 |
import { onMount } from "svelte";
|
3 |
import ClusterResults from "./ClusterResults.svelte";
|
4 |
+
import SubmitReportDialog from "./SubmitReportDialog.svelte";
|
5 |
|
6 |
import Button, { Label } from "@smui/button";
|
7 |
import Textfield from '@smui/textfield';
|
|
|
32 |
topic_chosen.subscribe(value => {
|
33 |
cur_topic = value;
|
34 |
});
|
35 |
+
// Handle submit report dialog
|
36 |
+
let submit_dialog_open = false;
|
37 |
|
38 |
// Handle routing
|
39 |
let searchParams = new URLSearchParams(window.location.search);
|
|
|
205 |
// Save current error type
|
206 |
async function updateErrorType() {
|
207 |
// Update error type on main page to be the selected error type
|
|
|
|
|
208 |
editErrorType = false;
|
209 |
}
|
210 |
|
211 |
+
let promise_submit = Promise.resolve(null);
|
212 |
+
function handleSubmitReport() {
|
213 |
+
promise_submit = submitReport();
|
214 |
+
}
|
215 |
+
async function submitReport() {
|
216 |
+
submit_dialog_open = true;
|
217 |
+
return true;
|
218 |
+
}
|
219 |
+
|
220 |
</script>
|
221 |
|
222 |
+
<div>
|
223 |
+
{#await promise_submit}
|
224 |
+
<CircularProgress style="height: 32px; width: 32px;" indeterminate />
|
225 |
+
{:then}
|
226 |
+
<SubmitReportDialog bind:open={submit_dialog_open} cur_user={cur_user} all_reports={all_reports}/>
|
227 |
+
{:catch error}
|
228 |
+
<p style="color: red">{error.message}</p>
|
229 |
+
{/await}
|
230 |
+
<div class="hypothesis_panel">
|
231 |
+
<div class="panel_header">
|
232 |
+
<div class="panel_header_content">
|
233 |
+
<div class="page_header">
|
234 |
+
<img src="/logo.png" style="height: 50px; padding: 0px 20px;" alt="IndieLabel" />
|
235 |
+
<Button class="user_button" color="secondary" style="margin: 12px 10px;" >
|
236 |
+
<Label>User: {cur_user}</Label>
|
237 |
+
</Button>
|
238 |
+
</div>
|
239 |
+
<div class="hypotheses_header">
|
240 |
+
<h5 style="float: left; margin: 0; padding: 5px 20px;">Your Audit Reports</h5>
|
241 |
+
<Button
|
242 |
+
on:click={() => (open = !open)}
|
243 |
+
color="primary"
|
244 |
+
disabled={model == null}
|
245 |
+
style="float: right; padding: 10px; margin-right: 10px;"
|
246 |
+
>
|
247 |
+
{#if open}
|
248 |
+
<Label>Close</Label>
|
249 |
+
{:else}
|
250 |
+
{#key unfinished_count}
|
251 |
+
<Label>Unfinished reports ({unfinished_count})</Label>
|
252 |
+
{/key}
|
253 |
+
{/if}
|
254 |
+
</Button>
|
255 |
+
</div>
|
256 |
</div>
|
257 |
</div>
|
|
|
258 |
|
259 |
+
{#if model == null}
|
260 |
+
<div class="panel_contents">
|
261 |
+
<p>You can start to author audit reports in this panel after you've trained your personalized model in the "Labeling" tab.</p>
|
262 |
+
</div>
|
263 |
+
{:else}
|
264 |
+
<div class="panel_contents">
|
265 |
+
<!-- Drawer -->
|
266 |
+
{#await promise}
|
267 |
+
<div class="app_loading_fullwidth">
|
268 |
+
<LinearProgress indeterminate />
|
269 |
+
</div>
|
270 |
+
{:then reports}
|
271 |
+
{#if reports}
|
272 |
+
<div class="drawer-container">
|
273 |
+
{#key open}
|
274 |
+
<Drawer variant="dismissible" bind:open>
|
275 |
+
<Header>
|
276 |
+
<Title>Your Reports</Title>
|
277 |
+
<Subtitle>Select a report to view.</Subtitle>
|
278 |
+
</Header>
|
279 |
+
<Content>
|
280 |
+
<List twoLine>
|
281 |
+
{#each reports as report}
|
282 |
+
<Item
|
283 |
+
href="javascript:void(0)"
|
284 |
+
on:click={() => setActive(report)}
|
285 |
+
activated={selected === report}
|
286 |
+
>
|
287 |
+
{#if report["complete_status"]}
|
288 |
+
<Graphic class="material-icons" aria-hidden="true">task_alt</Graphic>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
289 |
{:else}
|
290 |
+
<Graphic class="material-icons" aria-hidden="true">radio_button_unchecked</Graphic>
|
291 |
{/if}
|
292 |
+
<Text>
|
293 |
+
<PrimaryText>
|
294 |
+
{report["title"]}
|
295 |
+
</PrimaryText>
|
296 |
+
<SecondaryText>
|
297 |
+
{report["error_type"]}
|
298 |
+
</SecondaryText>
|
299 |
+
</Text>
|
300 |
+
</Item>
|
301 |
+
{/each}
|
302 |
+
</List>
|
303 |
+
</Content>
|
304 |
+
</Drawer>
|
305 |
+
{/key}
|
306 |
+
<AppContent class="app-content">
|
307 |
+
<main class="main-content">
|
308 |
+
{#if selected}
|
309 |
+
<div class="head_6_highlight">
|
310 |
+
Current Report
|
311 |
</div>
|
312 |
+
<div class="panel_contents2">
|
313 |
+
<!-- Title -->
|
314 |
+
<div class="spacing_vert">
|
315 |
+
<div class="edit_button_row">
|
316 |
+
{#if editTitle}
|
317 |
+
<div class="edit_button_row_input">
|
318 |
+
<Textfield
|
319 |
+
bind:value={selected["title"]}
|
320 |
+
label="Your report title"
|
321 |
+
input$rows={4}
|
322 |
+
textarea
|
323 |
+
variant="outlined"
|
324 |
+
style="width: 100%;"
|
325 |
+
helperLine$style="width: 100%;"
|
326 |
+
/>
|
327 |
+
</div>
|
328 |
+
<div>
|
329 |
+
<IconButton class="material-icons grey_button" size="button" on:click={() => (editTitle = false)}>
|
330 |
+
check
|
331 |
+
</IconButton>
|
332 |
+
</div>
|
333 |
+
{:else}
|
334 |
+
{#if selected["title"] != ""}
|
335 |
+
<div class="head_5">
|
336 |
+
{selected["title"]}
|
337 |
+
</div>
|
338 |
+
{:else}
|
339 |
+
<div class="grey_text">Enter a report title</div>
|
340 |
+
{/if}
|
341 |
|
342 |
+
<div>
|
343 |
+
<IconButton class="material-icons grey_button" size="button" on:click={() => (editTitle = true)}>
|
344 |
+
create
|
345 |
+
</IconButton>
|
346 |
+
</div>
|
347 |
+
{/if}
|
348 |
+
</div>
|
349 |
</div>
|
350 |
+
|
351 |
+
<!-- Error type -->
|
352 |
+
<div class="spacing_vert_40">
|
353 |
+
<div class="head_6">
|
354 |
+
<b>Error Type</b>
|
355 |
+
</div>
|
356 |
+
<div class="edit_button_row">
|
357 |
+
{#if editErrorType}
|
358 |
+
<div>
|
359 |
+
{#each error_type_options as e}
|
360 |
+
<div style="display: flex; align-items: center;">
|
361 |
<FormField>
|
362 |
<Radio bind:group={selected["error_type"]} value={e.opt} on:change={updateErrorType} color="secondary" />
|
363 |
<span slot="label">
|
364 |
+
<b>{e.opt}</b> {e.descr}
|
|
|
365 |
</span>
|
366 |
</FormField>
|
367 |
+
</div>
|
368 |
+
{/each}
|
369 |
+
</div>
|
370 |
+
{:else}
|
371 |
+
{#if selected["error_type"] != ""}
|
372 |
+
<div>
|
373 |
+
<p>{selected["error_type"]}</p>
|
|
|
|
|
374 |
</div>
|
375 |
+
{:else}
|
376 |
+
<div class="grey_text">Select an error type</div>
|
377 |
+
{/if}
|
378 |
+
|
|
|
|
|
|
|
|
|
|
|
379 |
<div>
|
380 |
+
<IconButton class="material-icons grey_button" size="button" on:click={() => (editErrorType = true)}>
|
381 |
+
create
|
382 |
+
</IconButton>
|
383 |
</div>
|
|
|
|
|
384 |
{/if}
|
385 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
386 |
</div>
|
387 |
+
|
388 |
+
<!-- Evidence -->
|
389 |
+
<div class="spacing_vert_40">
|
390 |
+
<div class="head_6">
|
391 |
+
<b>Evidence</b>
|
392 |
+
</div>
|
393 |
+
{#key cur_open_evidence}
|
394 |
+
<div>
|
395 |
+
{#if cur_open_evidence.length > 0}
|
396 |
+
<ClusterResults
|
397 |
+
cluster={cur_topic}
|
398 |
+
model={model}
|
399 |
+
data={{"cluster_comments": cur_open_evidence}}
|
400 |
+
show_vis={false}
|
401 |
+
show_checkboxes={false}
|
402 |
+
table_width_pct={100}
|
403 |
+
rowsPerPage={25}
|
404 |
+
table_id={"panel"}
|
405 |
+
/>
|
406 |
+
{:else}
|
407 |
+
<p class="grey_text">
|
408 |
+
Add examples from the main panel to see them here!
|
409 |
+
</p>
|
410 |
+
{/if}
|
411 |
+
</div>
|
412 |
+
{/key}
|
413 |
</div>
|
|
|
|
|
414 |
|
415 |
+
<div class="spacing_vert_60">
|
416 |
+
<div class="head_6">
|
417 |
+
<b>Summary/Suggestions</b>
|
418 |
+
</div>
|
419 |
+
<div class="spacing_vert">
|
420 |
+
<Textfield
|
421 |
+
style="width: 100%;"
|
422 |
+
helperLine$style="width: 100%;"
|
423 |
+
input$rows={8}
|
424 |
+
textarea
|
425 |
+
bind:value={selected["text_entry"]}
|
426 |
+
label="My current hunch is that..."
|
427 |
+
>
|
428 |
+
</Textfield>
|
429 |
+
</div>
|
430 |
+
|
431 |
</div>
|
|
|
|
|
432 |
|
433 |
+
<div class="spacing_vert_40">
|
434 |
+
<div class="head_6">
|
435 |
+
<b>Mark report as complete?</b>
|
436 |
+
<FormField>
|
437 |
+
<Checkbox checked={selected["complete_status"]} on:change={handleMarkComplete} />
|
438 |
+
</FormField>
|
439 |
+
</div>
|
440 |
+
|
441 |
</div>
|
|
|
442 |
</div>
|
443 |
+
{/if}
|
444 |
+
</main>
|
445 |
+
</AppContent>
|
446 |
+
</div>
|
447 |
+
{/if}
|
448 |
+
{:catch error}
|
449 |
+
<p style="color: red">{error.message}</p>
|
450 |
+
{/await}
|
451 |
+
</div>
|
452 |
+
|
453 |
+
<div class="panel_footer">
|
454 |
+
<div class="panel_footer_contents">
|
455 |
+
<Button
|
456 |
+
on:click={handleNewReport}
|
457 |
+
variant="outlined"
|
458 |
+
color="secondary"
|
459 |
+
style=""
|
460 |
+
>
|
461 |
+
<Label>New</Label>
|
462 |
+
</Button>
|
463 |
+
|
464 |
+
<!-- <Button
|
465 |
+
on:click={handleDeleteReport}
|
466 |
+
variant="outlined"
|
467 |
+
color="secondary"
|
468 |
+
style=""
|
469 |
+
>
|
470 |
+
<Label>Delete</Label>
|
471 |
+
</Button> -->
|
472 |
+
|
473 |
+
<Button
|
474 |
+
on:click={handleSaveReport}
|
475 |
+
variant="outlined"
|
476 |
+
color="secondary"
|
477 |
+
>
|
478 |
+
<Label>Save</Label>
|
479 |
+
</Button>
|
480 |
|
481 |
+
<Button
|
482 |
+
on:click={handleSubmitReport}
|
483 |
+
variant="outlined"
|
484 |
+
color="secondary"
|
485 |
+
>
|
486 |
+
<Label>Send Reports</Label>
|
487 |
+
</Button>
|
488 |
+
|
489 |
+
<div>
|
490 |
+
<span style="color: grey"><i>Last saved:
|
491 |
+
{#await promise_save}
|
492 |
+
<CircularProgress style="height: 32px; width: 32px;" indeterminate />
|
493 |
+
{:then result}
|
494 |
+
{#if result}
|
495 |
+
{new Date().toLocaleTimeString()}
|
496 |
+
{:else}
|
497 |
+
—
|
498 |
+
{/if}
|
499 |
+
{:catch error}
|
500 |
+
<p style="color: red">{error.message}</p>
|
501 |
+
{/await}
|
502 |
+
</i></span>
|
503 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
504 |
</div>
|
505 |
</div>
|
506 |
+
{/if}
|
507 |
</div>
|
|
|
|
|
|
|
|
|
|
|
508 |
</div>
|
509 |
|
510 |
<style>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
511 |
.panel_contents {
|
512 |
padding: 0 20px;
|
513 |
overflow-y: auto;
|
indie_label_svelte/src/SubmitReportDialog.svelte
ADDED
@@ -0,0 +1,120 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import Dialog, { Title, Content, Actions } from "@smui/dialog";
|
3 |
+
import Button, { Label } from "@smui/button";
|
4 |
+
import Textfield from "@smui/textfield";
|
5 |
+
import Select, { Option } from "@smui/select";
|
6 |
+
import CircularProgress from '@smui/circular-progress';
|
7 |
+
|
8 |
+
export let open;
|
9 |
+
export let cur_user;
|
10 |
+
export let all_reports;
|
11 |
+
let email = "";
|
12 |
+
let all_sep_options = [
|
13 |
+
"Accuracy",
|
14 |
+
"Bias/Discrimination",
|
15 |
+
"Adversarial Example",
|
16 |
+
"Other",
|
17 |
+
];
|
18 |
+
let sep_selection = "";
|
19 |
+
|
20 |
+
let promise_submit = Promise.resolve(null);
|
21 |
+
function handleSubmitReport() {
|
22 |
+
promise_submit = submitReport();
|
23 |
+
}
|
24 |
+
|
25 |
+
async function submitReport() {
|
26 |
+
let req_params = {
|
27 |
+
cur_user: cur_user,
|
28 |
+
reports: JSON.stringify(all_reports),
|
29 |
+
email: email,
|
30 |
+
sep_selection: sep_selection,
|
31 |
+
};
|
32 |
+
|
33 |
+
let params = new URLSearchParams(req_params).toString();
|
34 |
+
const response = await fetch("./submit_avid_report?" + params);
|
35 |
+
const text = await response.text();
|
36 |
+
const data = JSON.parse(text);
|
37 |
+
return data;
|
38 |
+
}
|
39 |
+
|
40 |
+
</script>
|
41 |
+
|
42 |
+
<div>
|
43 |
+
<Dialog
|
44 |
+
bind:open
|
45 |
+
aria-labelledby="simple-title"
|
46 |
+
aria-describedby="simple-content"
|
47 |
+
>
|
48 |
+
<!-- Title cannot contain leading whitespace due to mdc-typography-baseline-top() -->
|
49 |
+
<Title id="simple-title">Send All Audit Reports</Title>
|
50 |
+
<Content id="simple-content">
|
51 |
+
<!-- Description -->
|
52 |
+
<div>
|
53 |
+
<b>When you are ready to send all of your audit reports to the <a href="https://avidml.org/" target="_blank">AI Vulnerability Database</a> (AVID), please fill out the following information.</b>
|
54 |
+
Only your submitted reports will be stored in the database for further analysis. While you can submit reports anonymously, we encourage you to provide your email so that we can contact you if we have any questions.
|
55 |
+
</div>
|
56 |
+
|
57 |
+
<!-- Summary of complete reports -->
|
58 |
+
<div>
|
59 |
+
<p><b>Summary of Reports to Send</b> (Reports that include evidence and are marked as complete)</p>
|
60 |
+
<ul>
|
61 |
+
{#each all_reports as report}
|
62 |
+
{#if report["complete_status"] && (report["evidence"].length > 0)}
|
63 |
+
<li>{report["title"]}</li>
|
64 |
+
<ul>
|
65 |
+
<li>Error Type: {report["error_type"]}</li>
|
66 |
+
<li>Evidence: Includes {report["evidence"].length} example{(report["evidence"].length > 1) ? 's' : ''}</li>
|
67 |
+
<li>Summary/Suggestions: {report["text_entry"]}</li>
|
68 |
+
</ul>
|
69 |
+
{/if}
|
70 |
+
{/each}
|
71 |
+
</ul>
|
72 |
+
</div>
|
73 |
+
|
74 |
+
<!-- Form fields -->
|
75 |
+
<div>
|
76 |
+
<Select bind:value={sep_selection} label="Audit category" style="width: 90%">
|
77 |
+
{#each all_sep_options as opt}
|
78 |
+
<Option value={opt}>{opt}</Option>
|
79 |
+
{/each}
|
80 |
+
</Select>
|
81 |
+
</div>
|
82 |
+
<div>
|
83 |
+
<Textfield bind:value={email} label="(Optional) Contact email" style="width: 90%" />
|
84 |
+
</div>
|
85 |
+
|
86 |
+
<!-- Submission and status message -->
|
87 |
+
<div class="dialog_footer">
|
88 |
+
<Button on:click={handleSubmitReport} variant="outlined">
|
89 |
+
<Label>Submit Report to AVID</Label>
|
90 |
+
</Button>
|
91 |
+
|
92 |
+
<div>
|
93 |
+
<span style="color: grey"><i>
|
94 |
+
{#await promise_submit}
|
95 |
+
<CircularProgress style="height: 32px; width: 32px;" indeterminate />
|
96 |
+
{:then result}
|
97 |
+
{#if result}
|
98 |
+
Successfully sent reports! You may close this window.
|
99 |
+
{/if}
|
100 |
+
{:catch error}
|
101 |
+
<p style="color: red">{error.message}</p>
|
102 |
+
{/await}
|
103 |
+
</i></span>
|
104 |
+
</div>
|
105 |
+
</div>
|
106 |
+
</Content>
|
107 |
+
</Dialog>
|
108 |
+
</div>
|
109 |
+
|
110 |
+
<style>
|
111 |
+
:global(.mdc-dialog__surface) {
|
112 |
+
min-width: 50%;
|
113 |
+
min-height: 50%;
|
114 |
+
margin-left: 30%;
|
115 |
+
}
|
116 |
+
|
117 |
+
.dialog_footer {
|
118 |
+
padding: 20px 0px;
|
119 |
+
}
|
120 |
+
</style>
|
server.py
CHANGED
@@ -638,34 +638,44 @@ def get_prompts_scaffold():
|
|
638 |
},
|
639 |
]
|
640 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
641 |
# Submit all reports to AVID
|
642 |
# Logs the responses
|
643 |
-
def submit_reports_to_AVID(reports):
|
644 |
-
#Set up the connection to AVID
|
645 |
-
root = environ.get('AVID_API_URL')
|
646 |
-
api_key = environ.get('AVID_API_KEY')
|
647 |
key = {"Authorization": api_key}
|
648 |
|
|
|
|
|
|
|
|
|
649 |
for r in reports:
|
650 |
-
new_report = utils.convert_indie_label_json_to_avid_json(r)
|
651 |
url = root + "submit"
|
652 |
response = requests.post(url, json=json.loads(new_report), headers=key) # The loads ensures type compliance
|
653 |
uuid = response.json()
|
654 |
-
|
|
|
|
|
655 |
|
656 |
########################################
|
657 |
# ROUTE: /SAVE_REPORTS
|
658 |
@app.route("/save_reports")
|
659 |
-
def save_reports():
|
660 |
cur_user = request.args.get("cur_user")
|
661 |
reports_json = request.args.get("reports")
|
662 |
reports = json.loads(reports_json)
|
663 |
-
scaffold_method = request.args.get("scaffold_method")
|
664 |
model = request.args.get("model")
|
665 |
|
666 |
-
# Submit reports to AVID
|
667 |
-
submit_reports_to_AVID(reports)
|
668 |
-
|
669 |
# Save reports for current user to file
|
670 |
reports_file = utils.get_reports_file(cur_user, model)
|
671 |
with open(reports_file, "w", encoding ='utf8') as f:
|
@@ -674,7 +684,27 @@ def save_reports():
|
|
674 |
results = {
|
675 |
"status": "success",
|
676 |
}
|
677 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
678 |
return json.dumps(results)
|
679 |
|
680 |
########################################
|
|
|
638 |
},
|
639 |
]
|
640 |
|
641 |
+
# Filter to eligible reports: those that have been marked complete and include at least one piece of evidence.
|
642 |
+
def get_eligible_reports(reports):
|
643 |
+
eligible_reports = []
|
644 |
+
for r in reports:
|
645 |
+
if (r["complete_status"] == True) and (len(r["evidence"]) > 0):
|
646 |
+
eligible_reports.append(r)
|
647 |
+
return eligible_reports
|
648 |
+
|
649 |
# Submit all reports to AVID
|
650 |
# Logs the responses
|
651 |
+
def submit_reports_to_AVID(reports, cur_user, email, sep_selection, debug=DEBUG):
|
652 |
+
# Set up the connection to AVID
|
653 |
+
root = os.environ.get('AVID_API_URL')
|
654 |
+
api_key = os.environ.get('AVID_API_KEY')
|
655 |
key = {"Authorization": api_key}
|
656 |
|
657 |
+
reports = get_eligible_reports(reports)
|
658 |
+
if debug:
|
659 |
+
print("Num eligible reports:", len(reports))
|
660 |
+
|
661 |
for r in reports:
|
662 |
+
new_report = utils.convert_indie_label_json_to_avid_json(r, cur_user, email, sep_selection)
|
663 |
url = root + "submit"
|
664 |
response = requests.post(url, json=json.loads(new_report), headers=key) # The loads ensures type compliance
|
665 |
uuid = response.json()
|
666 |
+
if debug:
|
667 |
+
print("Report", new_report)
|
668 |
+
print("AVID API response:", response, uuid)
|
669 |
|
670 |
########################################
|
671 |
# ROUTE: /SAVE_REPORTS
|
672 |
@app.route("/save_reports")
|
673 |
+
def save_reports(debug=DEBUG):
|
674 |
cur_user = request.args.get("cur_user")
|
675 |
reports_json = request.args.get("reports")
|
676 |
reports = json.loads(reports_json)
|
|
|
677 |
model = request.args.get("model")
|
678 |
|
|
|
|
|
|
|
679 |
# Save reports for current user to file
|
680 |
reports_file = utils.get_reports_file(cur_user, model)
|
681 |
with open(reports_file, "w", encoding ='utf8') as f:
|
|
|
684 |
results = {
|
685 |
"status": "success",
|
686 |
}
|
687 |
+
if debug:
|
688 |
+
print(results)
|
689 |
+
return json.dumps(results)
|
690 |
+
|
691 |
+
########################################
|
692 |
+
# ROUTE: /SUBMIT_AVID_REPORT
|
693 |
+
@app.route("/submit_avid_report")
|
694 |
+
def submit_avid_report():
|
695 |
+
cur_user = request.args.get("cur_user")
|
696 |
+
email = request.args.get("email")
|
697 |
+
sep_selection = request.args.get("sep_selection")
|
698 |
+
reports_json = request.args.get("reports")
|
699 |
+
|
700 |
+
reports = json.loads(reports_json)
|
701 |
+
|
702 |
+
# Submit reports to AVID
|
703 |
+
submit_reports_to_AVID(reports, cur_user, email, sep_selection)
|
704 |
+
|
705 |
+
results = {
|
706 |
+
"status": "success",
|
707 |
+
}
|
708 |
return json.dumps(results)
|
709 |
|
710 |
########################################
|