carolanderson commited on
Commit
3b065d6
2 Parent(s): e425cca 5cc3773

Merge remote-tracking branch 'upstream/main'

Browse files
README.md CHANGED
@@ -15,7 +15,7 @@ This repo shares our implementation of **IndieLabel**—an interactive web appli
15
  ```
16
  $ pip install -r requirements.txt
17
  ```
18
- - Download and unzip the `data` sub-directory from [this Drive folder](https://drive.google.com/file/d/1js0HAwBpEcoRdT9nkGY0Mc5Falkpsonl/view?usp=sharing) and place it in the repo directory (39.7MB zipped, 124.6MB unzipped).
19
 
20
 
21
  - Start the Flask server:
 
15
  ```
16
  $ pip install -r requirements.txt
17
  ```
18
+ - Download and unzip the `data` sub-directory from [this Drive folder](https://drive.google.com/file/d/1iYueqzG9qIB45HT_5iwJp-44Dhfm2XbR/view?usp=sharing) and place it in the repo directory (39.7MB zipped, 124.3MB unzipped).
19
 
20
 
21
  - Start the Flask server:
audit_utils.py CHANGED
@@ -336,6 +336,10 @@ def format_description(indie_label_json):
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,7 +353,7 @@ def format_description(indie_label_json):
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,17 +402,16 @@ def convert_indie_label_json_to_avid_json(indie_label_json, cur_user, email, sep
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]
 
336
  text_entry = indie_label_json["text_entry"]
337
  return f"Title: {title}\nError Type: {error_type}\nSummary/Suggestions: {text_entry}"
338
 
339
+ # Format the credit field for the report with the current user's username, optional name, and optional email address
340
+ def format_credit(cur_user, name, email):
341
+ return f"Username: {cur_user}, Name: {name}, Email: {email}"
342
+
343
  # Convert indielabel json to AVID json format.
344
  # See the AVID format in https://avidml.org/avidtools/reference/report
345
  #
 
353
  # user_rating personal_model_score 0.92
354
  # user_decision user_decision "Non-toxic"
355
  # Note that this is at the individual report level.
356
+ def convert_indie_label_json_to_avid_json(indie_label_json, cur_user, name, email, sep_selection):
357
 
358
  # Setting up the structure with a dict to enable programmatic additions
359
  avid_json_dict = {
 
402
  "taxonomy_version": "0.2"
403
  }
404
  },
405
+ "credit": {
406
+ "lang": "eng", # TODO: Make language selectable
407
+ "value": "" # Leaving empty so that credit can be assigned
408
+ },
409
  "reported_date": "" # Leaving empty so that it can be dynamically filled in
410
  }
411
 
412
+ avid_json_dict["description"]["value"] = format_description(indie_label_json)
413
+ avid_json_dict["reported_date"] = str(date.today())
414
+ avid_json_dict["credit"]["value"] = format_credit(cur_user, name, email)
 
 
 
 
415
 
416
  sep_enum = get_sep_enum(sep_selection)
417
  avid_json_dict["impact"]["avid"]["sep_view"] = [sep_enum]
indie_label_svelte/package-lock.json CHANGED
The diff for this file is too large to render. See raw diff
 
indie_label_svelte/package.json CHANGED
@@ -1,10 +1,10 @@
1
  {
2
- "name": "svelte-vega-sample",
3
  "version": "0.0.0",
4
- "author": "Alex Bäuerle <bauerlealex@gmail.com> (https://a13x.io)",
5
- "repository": "git@github.com:vega/svelte-vega.git",
6
  "bugs": {
7
- "url": "https://github.com/vega/svelte-vega/issues"
8
  },
9
  "private": true,
10
  "scripts": {
@@ -25,7 +25,7 @@
25
  "@smui/checkbox": "^6.0.0-beta.2",
26
  "@smui/chips": "^6.0.0-beta.2",
27
  "@smui/circular-progress": "^6.0.0-beta.4",
28
- "@smui/common": "^6.0.0-beta.2",
29
  "@smui/data-table": "^6.0.0-beta.2",
30
  "@smui/drawer": "^6.0.0-beta.4",
31
  "@smui/form-field": "^6.0.0-beta.2",
 
1
  {
2
+ "name": "indie-label",
3
  "version": "0.0.0",
4
+ "author": "Michelle Lam <michelle123lam@gmail.com> (http://michelle123lam.github.io)",
5
+ "repository": "git@github.com:StanfordHCI/indie-label.git",
6
  "bugs": {
7
+ "url": "https://github.com/StanfordHCI/indie-label/issues"
8
  },
9
  "private": true,
10
  "scripts": {
 
25
  "@smui/checkbox": "^6.0.0-beta.2",
26
  "@smui/chips": "^6.0.0-beta.2",
27
  "@smui/circular-progress": "^6.0.0-beta.4",
28
+ "@smui/common": "^6.1.4",
29
  "@smui/data-table": "^6.0.0-beta.2",
30
  "@smui/drawer": "^6.0.0-beta.4",
31
  "@smui/form-field": "^6.0.0-beta.2",
indie_label_svelte/public/global.css CHANGED
@@ -120,7 +120,7 @@ h6 {
120
  font-weight: bold;
121
  font-size: 16px;
122
  margin-top: 25px;
123
- margin-bottom: 25px;
124
  }
125
 
126
  .head_6_non_cap {
@@ -204,6 +204,11 @@ table {
204
  margin: 60px 0;
205
  }
206
 
 
 
 
 
 
207
  .page_title {
208
  font-size: 30px;
209
  font-weight: bold;
@@ -319,4 +324,21 @@ table {
319
 
320
  .mdc-drawer .mdc-deprecated-list-item {
321
  height: 64px !important;
322
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  font-weight: bold;
121
  font-size: 16px;
122
  margin-top: 25px;
123
+ margin-bottom: 15px;
124
  }
125
 
126
  .head_6_non_cap {
 
204
  margin: 60px 0;
205
  }
206
 
207
+ .spacing_vert_100_bottom {
208
+ margin: 0 0 100px 0;
209
+ }
210
+
211
+
212
  .page_title {
213
  font-size: 30px;
214
  font-weight: bold;
 
324
 
325
  .mdc-drawer .mdc-deprecated-list-item {
326
  height: 64px !important;
327
+ }
328
+
329
+ /* Material UI secondary button formatting */
330
+ :root {
331
+ --mdc-theme-secondary: #676778;
332
+ }
333
+ .smui-button--color-secondary:not(:disabled) {
334
+ color: var(--mdc-theme-secondary) !important;
335
+ }
336
+ .smui-button--color-secondary.mdc-button--outlined:not(:disabled) {
337
+ border-color: var(--mdc-theme-secondary) !important;
338
+ }
339
+ .smui-button--color-secondary .mdc-button__ripple::before, .smui-button--color-secondary .mdc-button__ripple::after {
340
+ background-color: var(--mdc-theme-secondary) !important;
341
+ }
342
+ .smui-button--color-secondary:not(:disabled) .mdc-button__icon {
343
+ color: var(--mdc-theme-secondary) !important;
344
+ }
indie_label_svelte/public/img/anderson.jpeg ADDED
indie_label_svelte/public/img/bernstein.jpeg ADDED
indie_label_svelte/public/img/blili_hamelin.jpeg ADDED
indie_label_svelte/public/img/butters.jpeg ADDED
indie_label_svelte/public/img/gordon.jpeg ADDED
indie_label_svelte/public/img/hancock.png ADDED
indie_label_svelte/public/img/lam.jpg ADDED
indie_label_svelte/public/img/landay.jpeg ADDED
indie_label_svelte/public/img/metaxa.jpg ADDED
indie_label_svelte/public/img/pan.jpg ADDED
indie_label_svelte/public/img/temp.png ADDED
indie_label_svelte/public/index.html CHANGED
@@ -10,9 +10,6 @@
10
  <link rel='icon' type='image/png' href='/favicon.png'>
11
  <link rel='stylesheet' href='/global.css'>
12
  <link rel='stylesheet' href='/build/bundle.css'>
13
- <link rel='stylesheet' href='/build/extra.css'>
14
- <!-- <link rel="stylesheet" href="../node_modules/svelte-material-ui/bare.css" /> -->
15
- <!-- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/svelte-material-ui@5.0.0/bare.min.css" /> -->
16
  <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
17
 
18
  <link
@@ -22,6 +19,15 @@
22
 
23
  <script defer src='/build/bundle.js'></script>
24
  </head>
 
 
 
 
 
 
 
 
 
25
 
26
  <body>
27
  </body>
 
10
  <link rel='icon' type='image/png' href='/favicon.png'>
11
  <link rel='stylesheet' href='/global.css'>
12
  <link rel='stylesheet' href='/build/bundle.css'>
 
 
 
13
  <link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons" />
14
 
15
  <link
 
19
 
20
  <script defer src='/build/bundle.js'></script>
21
  </head>
22
+ <!-- Google tag (gtag.js) -->
23
+ <script async src="https://www.googletagmanager.com/gtag/js?id=G-GFB1D1ZXRZ"></script>
24
+ <script>
25
+ window.dataLayer = window.dataLayer || [];
26
+ function gtag(){dataLayer.push(arguments);}
27
+ gtag('js', new Date());
28
+
29
+ gtag('config', 'G-GFB1D1ZXRZ');
30
+ </script>
31
 
32
  <body>
33
  </body>
indie_label_svelte/rollup.config.js CHANGED
@@ -51,7 +51,6 @@ export default {
51
  // we'll extract any component CSS out into
52
  // a separate file - better for performance
53
  css({ output: "bundle.css" }),
54
- // css({ output: 'public/build/extra.css' }),
55
 
56
  // If you have external dependencies installed from
57
  // npm, you'll most likely need these plugins. In
 
51
  // we'll extract any component CSS out into
52
  // a separate file - better for performance
53
  css({ output: "bundle.css" }),
 
54
 
55
  // If you have external dependencies installed from
56
  // npm, you'll most likely need these plugins. In
indie_label_svelte/src/About.svelte ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ </script>
3
+
4
+ <svelte:head>
5
+ <title>About</title>
6
+ </svelte:head>
7
+
8
+ <div class="panel">
9
+ <div class="panel_contents">
10
+ <div>
11
+ <h3>IndieLabel Tutorial</h3>
12
+ <p class="body">
13
+ Check out this 5-minute video tutorial for a quick overview of the IndieLabel tool. If you prefer a written document, you can also find a help article <a href="https://docs.google.com/document/d/1BTo8LFUriiZcSWCcQy0lel9VxGl4KZtlFnu7wAFaiu4/edit?usp=sharing" target="_blank">here</a>.
14
+ </p>
15
+ <div class="video-container">
16
+ <iframe src="https://www.youtube.com/embed/Je0DCDnJ6KQ?si=H1dVy-oe6PSP0QYM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
17
+ </div>
18
+ </div>
19
+ <div>
20
+ <h3>About IndieLabel</h3>
21
+ <p class="body">
22
+ The IndieLabel deployment on this Hugging Face Space is a collaboration between researchers at the <a href="https://hci.stanford.edu/" target="_blank">Stanford HCI Group</a> and <a href="https://avidml.org/arva/" target="_blank">ARVA (AI Risk and Vulnerability Alliance)</a>. The IndieLabel tool is a research prototype designed to empower everyday users to lead large-scale investigations of harmful algorithmic behavior, and this Space is intended as a public-facing deployment of this prototype.
23
+ </p>
24
+
25
+ <div class="card-container">
26
+ <p class="head_5">IndieLabel & the End-User Audits paper</p>
27
+ <p class="body">
28
+ The <a href="https://github.com/StanfordHCI/indie-label" target="_blank">IndieLabel</a> tool was initially presented as a research prototype in an academic publication called <b><i>End-User Audits: A System Empowering Communities to Lead Large-Scale Investigations of Harmful Algorithmic Behavior</i></b>. IndieLabel aids users in rapidly identifying where they disagree with a system's behavior with the help of a personalized recommender system model.
29
+ </p>
30
+ <p class="body">
31
+ The key question underlying the End-User Audits paper is: <i>how do we enable everyday users to lead large-scale algorithm audits</i>? The full paper was presented at CSCW 2022 and is available on the <a href="https://dl.acm.org/doi/10.1145/3555625" target="_blank">ACM Digital Library</a> or via <a href="https://hci.stanford.edu/publications/2022/Lam_EndUserAudits_CSCW22.pdf" target="_blank">Stanford HCI</a>. The End-User Audits paper abstract summarizes our core motivation, approach, and findings:
32
+ </p>
33
+ <p class="body blockquote">
34
+ "Because algorithm audits are conducted by technical experts, audits are necessarily limited to the hypotheses that experts think to test. End users hold the promise to expand this purview, as they inhabit spaces and witness algorithmic impacts that auditors do not. In pursuit of this goal, we propose end-user audits—system-scale audits led by non-technical users—and present an approach that scaffolds end users in hypothesis generation, evidence identification, and results communication. Today, performing a system-scale audit requires substantial user effort to label thousands of system outputs, so we introduce a collaborative filtering technique that leverages the algorithmic system's own disaggregated training data to project from a small number of end user labels onto the full test set. Our end-user auditing tool, IndieLabel, employs these predicted labels so that users can rapidly explore where their opinions diverge from the algorithmic system's outputs. By highlighting topic areas where the system is under-performing for the user and surfacing sets of likely error cases, the tool guides the user in authoring an audit report. In an evaluation of end-user audits on a popular comment toxicity model with 17 non-technical participants, participants both replicated issues that formal audits had previously identified and also raised previously underreported issues such as under-flagging on veiled forms of hate that perpetuate stigma and over-flagging of slurs that have been reclaimed by marginalized communities."
35
+ </p>
36
+
37
+
38
+ <p class="body">
39
+ The End-User Audits work was led by Stanford PhD student Michelle Lam along with co-authors Mitchell Gordon, Danaë Metaxa, Jeffrey Hancock, James Landay, and Michael Bernstein.
40
+ </p>
41
+
42
+ <div class="image-container">
43
+ <div>
44
+ <img src="./img/lam.jpg" alt="Michelle Lam">
45
+ <p>
46
+ <a href="http://michelle123lam.github.io" target="_blank"><b>Michelle Lam</b></a><br>
47
+ PhD Student, Stanford CS
48
+ </p>
49
+ </div>
50
+ <div>
51
+ <img src="./img/gordon.jpeg" alt="Mitchell Gordon">
52
+ <p>
53
+ <a href="https://mgordon.me/" target="_blank"><b>Mitchell Gordon</b></a><br/>
54
+ Incoming Asst Professor, MIT EECS
55
+ </p>
56
+ </div>
57
+ <div>
58
+ <img src="./img/metaxa.jpg" alt="Danaë Metaxa">
59
+ <p>
60
+ <a href="https://metaxa.net/" target="_blank"><b>Danaë Metaxa</b></a><br/>
61
+ Asst Professor, UPenn CIS
62
+ </p>
63
+ </div>
64
+ <div>
65
+ <img src="./img/hancock.png" alt="Jeffrey Hancock">
66
+ <p>
67
+ <a href="https://sml.stanford.edu/people/jeff-hancock" target="_blank"><b>Jeffrey Hancock</b></a><br/>
68
+ Professor, Stanford Comm
69
+ </p>
70
+ </div>
71
+ <div>
72
+ <img src="./img/landay.jpeg" alt="James Landay">
73
+ <p>
74
+ <a href="https://www.landay.org/" target="_blank"><b>James Landay</b></a><br/>
75
+ Professor, Stanford CS
76
+ </p>
77
+ </div>
78
+ <div>
79
+ <img src="./img/bernstein.jpeg" alt="Michael Bernstein">
80
+ <p>
81
+ <a href="https://hci.stanford.edu/msb/" target="_blank"><b>Michael Bernstein</b></a><br/>
82
+ Assoc Professor, Stanford CS
83
+ </p>
84
+ </div>
85
+ </div>
86
+ </div>
87
+
88
+ <div class="card-container">
89
+ <p class="head_5">ARVA</p>
90
+ <p class="body">
91
+ The <a href="https://avidml.org/arva/" target="_blank">AI Risk and Vulnerability Alliance (ARVA)</a> is a nonprofit organization focused on making AI safer for everyone. Our mission is to empower communities to recognize, diagnose, and manage vulnerabilities in AI that affects them. Our flagship project is the <a href="https://avidml.org" target="_blank">AI Vulnerability Database (AVID)</a>, is an open-source knowledge base of failure modes for AI models, datasets, and systems.
92
+ </p>
93
+ </div>
94
+
95
+ <div class="card-container">
96
+ <p class="head_5">The Team</p>
97
+ <p class="body">
98
+ The IndieLabel Hugging Face deployment was made possible by a wonderful team of volunteers who worked on adapting IndieLabel for use by a general audience, connecting its reports to AVID (AI Vulnerability Database), and deploying it on Hugging Face Spaces. The team includes:
99
+ </p>
100
+ <div>
101
+ <div class="image-container-wide">
102
+ <div class="image-wide">
103
+ <img src="./img/lam.jpg" alt="Michelle Lam">
104
+ <p>
105
+ <a href="http://michelle123lam.github.io" target="_blank"><b>Michelle Lam</b></a><br>
106
+ Michelle Lam is a PhD Candidate at Stanford University in the HCI Group. Her research focuses on building systems that empower everyday users to surface their expertise to design and evaluate AI systems.
107
+ </p>
108
+ </div>
109
+ <div class="image-wide">
110
+ <img src="./img/anderson.jpeg" alt="Carol Anderson">
111
+ <p>
112
+ <a href="https://www.carol-anderson.com/" target="_blank"><b>Carol Anderson</b></a><br>
113
+ Carol Anderson is a data scientist and machine learning practitioner with expertise in natural language processing (NLP), biological data, and AI ethics. She serves as AVID’s machine learning lead.
114
+ </p>
115
+ </div>
116
+ <div class="image-wide">
117
+ <img src="./img/pan.jpg" alt="Christina Pan">
118
+ <p>
119
+ <a href="https://www.christinaapan.com/" target="_blank"><b>Christina Pan</b></a><br>
120
+ Christina Pan started her career building machine learning (ML) models at Google, which inspired her passion for design thinking and AI ethics.
121
+ </p>
122
+ </div>
123
+ <div class="image-wide">
124
+ <img src="./img/butters.jpeg" alt="Nathan Butters">
125
+ <p>
126
+ <b>Nathan Butters</b><br>
127
+ Nathan Butters is a product manager in the Office of Ethical and Humane Use at Salesforce. He is a cofounder of the AI Risk and Vulnerability Alliance (ARVA).
128
+ </p>
129
+ </div>
130
+ <div class="image-wide">
131
+ <img src="./img/blili_hamelin.jpeg" alt="Borhane Blili-Hamelin">
132
+ <p>
133
+ <a href="https://borhane.xyz/" target="_blank"><b>Borhane Blili-Hamelin</b></a><br>
134
+ Borhane Blili-Hamelin is an ethicist, researcher and AI risk management consultant. He is an officer at the AI Risk and Vulnerability Alliance (ARVA), an affiliate at Data & Society, and a senior consultant at BABL AI.
135
+ </p>
136
+ </div>
137
+ </div>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ </div>
143
+
144
+ <style>
145
+ a {
146
+ color: #333;
147
+ text-decoration: underline;
148
+ }
149
+
150
+ .panel {
151
+ margin: 0 40px;
152
+ }
153
+
154
+ .blockquote {
155
+ margin: 0 40px;
156
+ }
157
+
158
+ .head_5 {
159
+ margin: 10px 0;
160
+ }
161
+
162
+ .card-container {
163
+ margin: 40px 0;
164
+ }
165
+
166
+ .image-container {
167
+ margin: 20px 0;
168
+ display: flex;
169
+ flex-wrap: wrap;
170
+ justify-content: center;
171
+ gap: 20px; /* Adjust the gap between images */
172
+ }
173
+
174
+ .image-container img {
175
+ max-width: 110px; /* Adjust the maximum width of images */
176
+ height: 110px;
177
+ border-radius: 50%;
178
+ display: block;
179
+ }
180
+
181
+ .image-container p {
182
+ text-align: center;
183
+ margin-top: 5px; /* Adjust the margin between image and name */
184
+ max-width: 110px;
185
+ font-size: 11px;
186
+ }
187
+
188
+ .image-container-wide {
189
+ margin: 20px 0;
190
+ display: flex;
191
+ flex-direction: column;
192
+ justify-content: center;
193
+ gap: 20px;
194
+ }
195
+
196
+ .image-wide {
197
+ display: flex;
198
+ flex-direction: row;
199
+ align-items: center;
200
+ gap: 10px;
201
+ }
202
+
203
+ .image-container-wide img {
204
+ max-width: 110px; /* Adjust the maximum width of images */
205
+ height: 110px;
206
+ border-radius: 50%;
207
+ display: block;
208
+ }
209
+
210
+ .video-container {
211
+ position: relative;
212
+ padding-bottom: 10px;
213
+ padding-top: 10px;
214
+ height: 40vh;
215
+ overflow: hidden;
216
+ }
217
+
218
+ .video-container iframe {
219
+ position: absolute;
220
+ top: 0;
221
+ left: 0;
222
+ width: 100%;
223
+ height: 100%;
224
+ }
225
+ </style>
indie_label_svelte/src/Auditing.svelte CHANGED
@@ -293,9 +293,6 @@
293
  {#if audit_results}
294
  <OverallResults
295
  data={audit_results}
296
- clusters={clusters}
297
- personalized_model={personalized_model}
298
- cluster={topic}
299
  />
300
  {/if}
301
  {:catch error}
 
293
  {#if audit_results}
294
  <OverallResults
295
  data={audit_results}
 
 
 
296
  />
297
  {/if}
298
  {:catch error}
indie_label_svelte/src/ClusterResults.svelte CHANGED
@@ -32,7 +32,7 @@
32
  export let show_checkboxes = true;
33
  export let table_width_pct = 80;
34
  export let rowsPerPage = 10;
35
- export let evidence;
36
  export let table_id;
37
  export let use_model = true;
38
  export let show_agree_disagree = false;
@@ -232,7 +232,7 @@
232
  <div class="row">
233
  <div class="col s8">
234
  <VegaLite
235
- {cluster_overview_data}
236
  spec={cluster_overview_spec}
237
  bind:view={cluster_overview_view}
238
  />
@@ -302,7 +302,7 @@
302
  <Cell>
303
  System<br>decision<br>
304
  {#if show_checkboxes}
305
- <span style="font-size:12px; max-width:125px">White = Non-toxic, <br>Grey = Toxic</span>
306
  {/if}
307
  </Cell>
308
  {#if show_num_ratings}
@@ -314,7 +314,7 @@
314
  <Cell>
315
  Potential error<br>type<br>
316
  {#if show_checkboxes}
317
- <span style="font-size:12px; max-width:125px">Darker red = Greater <br>potential system error</span>
318
  {/if}
319
  </Cell>
320
 
 
32
  export let show_checkboxes = true;
33
  export let table_width_pct = 80;
34
  export let rowsPerPage = 10;
35
+ export let evidence = null;
36
  export let table_id;
37
  export let use_model = true;
38
  export let show_agree_disagree = false;
 
232
  <div class="row">
233
  <div class="col s8">
234
  <VegaLite
235
+ data={cluster_overview_data}
236
  spec={cluster_overview_spec}
237
  bind:view={cluster_overview_view}
238
  />
 
302
  <Cell>
303
  System<br>decision<br>
304
  {#if show_checkboxes}
305
+ <span style="font-size:9px; max-width:125px">White = Non-toxic, <br>Grey = Toxic</span>
306
  {/if}
307
  </Cell>
308
  {#if show_num_ratings}
 
314
  <Cell>
315
  Potential error<br>type<br>
316
  {#if show_checkboxes}
317
+ <span style="font-size:9px; max-width:125px">Darker red = Greater <br>potential system error</span>
318
  {/if}
319
  </Cell>
320
 
indie_label_svelte/src/HypothesisPanel.svelte CHANGED
@@ -1,10 +1,12 @@
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';
 
8
  import { new_evidence } from './stores/new_evidence_store.js';
9
  import { open_evidence } from './stores/open_evidence_store.js';
10
  import { topic_chosen } from './stores/cur_topic_store.js';
@@ -22,6 +24,7 @@
22
  import Checkbox from '@smui/checkbox';
23
  import FormField from '@smui/form-field';
24
  import IconButton from "@smui/icon-button";
 
25
  import Radio from '@smui/radio';
26
 
27
  export let model;
@@ -50,6 +53,8 @@
50
  let editTitle = false;
51
  let editErrorType = false;
52
  let unfinished_count = 0;
 
 
53
 
54
  function setActive(value: string) {
55
  selected = value;
@@ -90,7 +95,8 @@
90
  selected = all_reports[0];
91
  setActive(selected);
92
  cur_open_evidence = selected["evidence"];
93
- unfinished_count = all_reports.filter(item => !item.complete_status).length
 
94
  return all_reports;
95
  }
96
 
@@ -129,7 +135,11 @@
129
 
130
  let promise_save = Promise.resolve(null);
131
  function handleSaveReport() {
 
132
  promise_save = saveReport();
 
 
 
133
  }
134
 
135
  async function saveReport() {
@@ -143,6 +153,8 @@
143
  const response = await fetch("./save_reports?" + params);
144
  const text = await response.text();
145
  const data = JSON.parse(text);
 
 
146
  return data;
147
  }
148
 
@@ -152,15 +164,14 @@
152
  error_type: "",
153
  evidence: [],
154
  text_entry: "",
155
- complete_status: false,
156
  };
157
  all_reports = all_reports.concat(new_report);
158
  promise = Promise.resolve(all_reports);
159
  // Open this new report
160
  selected = all_reports[all_reports.length - 1];
161
  cur_open_evidence = selected["evidence"];
162
- selected["complete_status"] = false;
163
- unfinished_count = all_reports.filter(item => !item.complete_status).length
164
  }
165
 
166
  function handleDeleteReport() {
@@ -169,13 +180,11 @@
169
  promise = Promise.resolve(all_reports);
170
  selected = all_reports[0];
171
  cur_open_evidence = selected["evidence"];
172
- unfinished_count = all_reports.filter(item => !item.complete_status).length
173
  }
174
 
175
- function handleMarkComplete() {
176
- selected["complete_status"] = !selected["complete_status"];
177
- unfinished_count = all_reports.filter(item => !item.complete_status).length
178
- handleSaveReport(); // Auto-save report
179
  }
180
 
181
  // Error type
@@ -208,6 +217,14 @@
208
  editErrorType = false;
209
  }
210
 
 
 
 
 
 
 
 
 
211
  let promise_submit = Promise.resolve(null);
212
  function handleSubmitReport() {
213
  promise_submit = submitReport();
@@ -231,8 +248,8 @@
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>
@@ -242,7 +259,7 @@
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>
@@ -284,7 +301,7 @@
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>
@@ -412,7 +429,7 @@
412
  {/key}
413
  </div>
414
 
415
- <div class="spacing_vert_60">
416
  <div class="head_6">
417
  <b>Summary/Suggestions</b>
418
  </div>
@@ -430,14 +447,17 @@
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}
@@ -452,55 +472,58 @@
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}
@@ -534,13 +557,23 @@
534
  background: #f3f3f3;
535
  z-index: 11;
536
  bottom: 0;
537
- padding: 15px 0px;
538
  }
539
  .panel_footer_contents {
540
- /* padding: 0px 20px; */
541
  display: flex;
542
  justify-content: space-around;
543
  align-items: center;
 
 
 
 
 
 
 
 
 
 
 
544
  }
545
 
546
  :global(.mdc-button.user_button) {
@@ -551,18 +584,17 @@
551
 
552
  :global(.mdc-button.user_button span) {
553
  text-overflow: ellipsis;
554
- white-space: nowrap;
555
  overflow: hidden;
556
  }
557
 
558
  .page_header {
559
  width: 100%;
560
  background: #e3d6fd;
561
- /* padding: 21px 0; */
562
- /* border-bottom: 1px solid #e3d6fd; */
563
  padding: 10.5px 0;
564
  position: relative;
565
- display: inline-block;
 
 
566
  }
567
 
568
  .page_header:before {
@@ -575,9 +607,11 @@
575
  }
576
 
577
  .hypotheses_header {
578
- display: inline-block;
579
  width: 100%;
580
  padding: 10px 0;
581
  vertical-align: middle;
 
 
 
582
  }
583
  </style>
 
1
  <script lang="ts">
2
  import { onMount } from "svelte";
3
+ import { fade } from 'svelte/transition';
4
  import ClusterResults from "./ClusterResults.svelte";
5
  import SubmitReportDialog from "./SubmitReportDialog.svelte";
6
 
7
  import Button, { Label } from "@smui/button";
8
  import Textfield from '@smui/textfield';
9
+ import Select, { Option } from "@smui/select";
10
  import { new_evidence } from './stores/new_evidence_store.js';
11
  import { open_evidence } from './stores/open_evidence_store.js';
12
  import { topic_chosen } from './stores/cur_topic_store.js';
 
24
  import Checkbox from '@smui/checkbox';
25
  import FormField from '@smui/form-field';
26
  import IconButton from "@smui/icon-button";
27
+ import { Icon } from "@smui/common";
28
  import Radio from '@smui/radio';
29
 
30
  export let model;
 
53
  let editTitle = false;
54
  let editErrorType = false;
55
  let unfinished_count = 0;
56
+ let has_complete_report = false;
57
+ let save_check_visible = false; // Whether the save checkmark is visible
58
 
59
  function setActive(value: string) {
60
  selected = value;
 
95
  selected = all_reports[0];
96
  setActive(selected);
97
  cur_open_evidence = selected["evidence"];
98
+ unfinished_count = all_reports.filter(item => (item.evidence.length == 0) || (item.text_entry == "") || !(item.sep_selection)).length
99
+ has_complete_report = hasCompleteReport();
100
  return all_reports;
101
  }
102
 
 
135
 
136
  let promise_save = Promise.resolve(null);
137
  function handleSaveReport() {
138
+ // Briefly display checkmark
139
  promise_save = saveReport();
140
+ save_check_visible = true;
141
+ // Hide save checkmark after 1 second
142
+ setTimeout(() => save_check_visible = false, 1000);
143
  }
144
 
145
  async function saveReport() {
 
153
  const response = await fetch("./save_reports?" + params);
154
  const text = await response.text();
155
  const data = JSON.parse(text);
156
+
157
+ has_complete_report = hasCompleteReport();
158
  return data;
159
  }
160
 
 
164
  error_type: "",
165
  evidence: [],
166
  text_entry: "",
167
+ sep_selection: "",
168
  };
169
  all_reports = all_reports.concat(new_report);
170
  promise = Promise.resolve(all_reports);
171
  // Open this new report
172
  selected = all_reports[all_reports.length - 1];
173
  cur_open_evidence = selected["evidence"];
174
+ unfinished_count = all_reports.filter(item => (item.evidence.length == 0) || (item.text_entry == "") || !(item.sep_selection)).length
 
175
  }
176
 
177
  function handleDeleteReport() {
 
180
  promise = Promise.resolve(all_reports);
181
  selected = all_reports[0];
182
  cur_open_evidence = selected["evidence"];
183
+ unfinished_count = all_reports.filter(item => (item.evidence.length == 0) || (item.text_entry == "") || !(item.sep_selection)).length
184
  }
185
 
186
+ function hasCompleteReport() {
187
+ return all_reports.some(item => (item.evidence.length > 0) && (item.text_entry != "") && (item.sep_selection));
 
 
188
  }
189
 
190
  // Error type
 
217
  editErrorType = false;
218
  }
219
 
220
+ // SEP selection
221
+ let all_sep_options = [
222
+ "Accuracy",
223
+ "Bias/Discrimination",
224
+ "Adversarial Example",
225
+ "Other",
226
+ ];
227
+
228
  let promise_submit = Promise.resolve(null);
229
  function handleSubmitReport() {
230
  promise_submit = submitReport();
 
248
  <div class="panel_header">
249
  <div class="panel_header_content">
250
  <div class="page_header">
251
+ <img src="/logo.png" style="height: 50px;" alt="IndieLabel" />
252
+ <Button class="user_button" color="secondary" style="margin: 0 5px; padding: 0 5px;" >
253
  <Label>User: {cur_user}</Label>
254
  </Button>
255
  </div>
 
259
  on:click={() => (open = !open)}
260
  color="primary"
261
  disabled={model == null}
262
+ style="float: right; padding: 0 5px; margin: 0 5px; max-width: 200px;"
263
  >
264
  {#if open}
265
  <Label>Close</Label>
 
301
  on:click={() => setActive(report)}
302
  activated={selected === report}
303
  >
304
+ {#if (report["evidence"].length > 0) && (report["text_entry"] != "") && (report["sep_selection"])}
305
  <Graphic class="material-icons" aria-hidden="true">task_alt</Graphic>
306
  {:else}
307
  <Graphic class="material-icons" aria-hidden="true">radio_button_unchecked</Graphic>
 
429
  {/key}
430
  </div>
431
 
432
+ <div class="spacing_vert_40">
433
  <div class="head_6">
434
  <b>Summary/Suggestions</b>
435
  </div>
 
447
 
448
  </div>
449
 
450
+ <div class="spacing_vert_40 spacing_vert_100_bottom">
451
  <div class="head_6">
452
+ <b>Audit Category</b>
453
+ </div>
454
+ <div>
455
+ <Select bind:value={selected["sep_selection"]} label="Audit category" style="width: 90%">
456
+ {#each all_sep_options as opt}
457
+ <Option value={opt}>{opt}</Option>
458
+ {/each}
459
+ </Select>
460
  </div>
 
461
  </div>
462
  </div>
463
  {/if}
 
472
 
473
  <div class="panel_footer">
474
  <div class="panel_footer_contents">
475
+ <!-- New button -->
476
  <Button
477
  on:click={handleNewReport}
478
  variant="outlined"
 
 
479
  >
480
  <Label>New</Label>
481
  </Button>
482
 
483
+ <!-- Delete button -->
484
  <!-- <Button
485
  on:click={handleDeleteReport}
486
  variant="outlined"
 
 
487
  >
488
  <Label>Delete</Label>
489
  </Button> -->
490
 
491
+ <!-- Save button -->
492
  <Button
493
  on:click={handleSaveReport}
494
  variant="outlined"
 
495
  >
496
+ <Label>
497
+ {#await promise_save}
498
+ <CircularProgress style="height: 13.5px; width: 13.5px;" indeterminate />
499
+ {:then result}
500
+ {#if result && save_check_visible}
501
+ <span transition:fade>
502
+ <Icon class="material-icons">check</Icon>
503
+ </span>
504
+ {/if}
505
+ {:catch error}
506
+ <span style="color: red">{error.message}</span>
507
+ {/await}
508
+ Save
509
+ </Label>
510
  </Button>
511
 
512
+ <!-- Send to Avid button -->
513
+ {#key has_complete_report}
514
  <Button
515
  on:click={handleSubmitReport}
516
  variant="outlined"
 
517
  >
518
+ <Label>Send Reports to AVID</Label>
519
  </Button>
520
+ {/key}
521
+ </div>
522
 
523
+ <div class="feedback_section">
524
+ <a href="https://forms.gle/vDXchpbBFjDeKjJA6" target="_blank" class="feedback_link">
525
+ Share feedback about the tool
526
+ </a>
 
 
 
 
 
 
 
 
 
 
 
527
  </div>
528
  </div>
529
  {/if}
 
557
  background: #f3f3f3;
558
  z-index: 11;
559
  bottom: 0;
560
+ padding: 5px 0px;
561
  }
562
  .panel_footer_contents {
 
563
  display: flex;
564
  justify-content: space-around;
565
  align-items: center;
566
+ padding: 5px 0px 10px 0px;
567
+ }
568
+ .feedback_section {
569
+ display: flex;
570
+ justify-content: space-around;
571
+ align-items: center;
572
+ }
573
+ .feedback_link {
574
+ color: var(--mdc-theme-secondary);
575
+ font-size: 10px;
576
+ text-decoration: underline;
577
  }
578
 
579
  :global(.mdc-button.user_button) {
 
584
 
585
  :global(.mdc-button.user_button span) {
586
  text-overflow: ellipsis;
 
587
  overflow: hidden;
588
  }
589
 
590
  .page_header {
591
  width: 100%;
592
  background: #e3d6fd;
 
 
593
  padding: 10.5px 0;
594
  position: relative;
595
+ display: flex;
596
+ align-items: center;
597
+ justify-content: space-around;
598
  }
599
 
600
  .page_header:before {
 
607
  }
608
 
609
  .hypotheses_header {
 
610
  width: 100%;
611
  padding: 10px 0;
612
  vertical-align: middle;
613
+ display: flex;
614
+ align-items: center;
615
+ justify-content: space-around;
616
  }
617
  </style>
indie_label_svelte/src/MainPanel.svelte CHANGED
@@ -1,20 +1,26 @@
1
  <script lang="ts">
2
  import Labeling from "./Labeling.svelte";
3
  import Auditing from "./Auditing.svelte";
 
4
 
5
  import Tab, { Label } from "@smui/tab";
6
  import TabBar from "@smui/tab-bar";
 
7
 
8
- export let model;
9
  export let error_type;
10
  export let cur_user;
11
 
12
  // Handle routing
13
- let active = "labeling";
14
  let searchParams = new URLSearchParams(window.location.search);
15
  let tab = searchParams.get("tab");
16
  if (tab == "auditing") {
17
  active = "auditing";
 
 
 
 
18
  }
19
 
20
  </script>
@@ -25,22 +31,31 @@
25
 
26
  <div class="auditing_panel">
27
  <div class="tab_header">
28
- <TabBar tabs={["labeling", "auditing"]} let:tab bind:active>
29
- <Tab {tab}>
30
- <Label>{tab}</Label>
 
 
 
 
 
31
  </Tab>
32
  </TabBar>
33
  </div>
34
 
35
  <div class="panel_contents">
36
  <div>
37
- <div id="labeling" hidden={active == "auditing"} >
38
  <Labeling cur_user={cur_user}/>
39
  </div>
40
 
41
- <div id="auditing" hidden={active == "labeling"} >
42
  <Auditing bind:personalized_model={model} bind:cur_error_type={error_type} cur_user={cur_user} on:change/>
43
  </div>
 
 
 
 
44
  </div>
45
 
46
  </div>
 
1
  <script lang="ts">
2
  import Labeling from "./Labeling.svelte";
3
  import Auditing from "./Auditing.svelte";
4
+ import About from "./About.svelte";
5
 
6
  import Tab, { Label } from "@smui/tab";
7
  import TabBar from "@smui/tab-bar";
8
+ import { Icon } from "@smui/common";
9
 
10
+ export let model = null;
11
  export let error_type;
12
  export let cur_user;
13
 
14
  // Handle routing
15
+ let active = "about"; // default tab on load
16
  let searchParams = new URLSearchParams(window.location.search);
17
  let tab = searchParams.get("tab");
18
  if (tab == "auditing") {
19
  active = "auditing";
20
+ } else if (tab == "about") {
21
+ active = "about";
22
+ } else if (tab == "labeling") {
23
+ active = "labeling";
24
  }
25
 
26
  </script>
 
31
 
32
  <div class="auditing_panel">
33
  <div class="tab_header">
34
+ <TabBar tabs={["labeling", "auditing", "about"]} let:tab bind:active>
35
+ <Tab {tab} minWidth={tab == "about"}>
36
+ {#if tab == "about"}
37
+ <Icon class="material-icons">info_outlined</Icon>
38
+ {:else}
39
+ <Label>{tab}</Label>
40
+ {/if}
41
+
42
  </Tab>
43
  </TabBar>
44
  </div>
45
 
46
  <div class="panel_contents">
47
  <div>
48
+ <div id="labeling" hidden={active != "labeling"} >
49
  <Labeling cur_user={cur_user}/>
50
  </div>
51
 
52
+ <div id="auditing" hidden={active != "auditing"} >
53
  <Auditing bind:personalized_model={model} bind:cur_error_type={error_type} cur_user={cur_user} on:change/>
54
  </div>
55
+
56
+ <div id="about" hidden={active != "about"} >
57
+ <About />
58
+ </div>
59
  </div>
60
 
61
  </div>
indie_label_svelte/src/ModelPerf.svelte CHANGED
@@ -32,7 +32,7 @@
32
  <div>
33
  <!-- Performance visualization -->
34
  <div>
35
- <VegaLite {perf_plot_data} spec={perf_plot_spec} bind:view={perf_plot_view}/>
36
  </div>
37
  </div>
38
  </div>
 
32
  <div>
33
  <!-- Performance visualization -->
34
  <div>
35
+ <VegaLite data={perf_plot_data} spec={perf_plot_spec} bind:view={perf_plot_view}/>
36
  </div>
37
  </div>
38
  </div>
indie_label_svelte/src/OverallResults.svelte CHANGED
@@ -7,9 +7,6 @@
7
  import Card, { Content } from '@smui/card';
8
 
9
  export let data;
10
- export let clusters;
11
- export let personalized_model;
12
- export let cluster = "";
13
 
14
  let show_step1_info = false;
15
 
@@ -68,7 +65,7 @@
68
  {/if}
69
  <div class="row">
70
  <div class="col s8">
71
- <VegaLite {topic_overview_data} spec={topic_overview_spec} bind:view={topic_overview_view}/>
72
  </div>
73
  </div>
74
 
 
7
  import Card, { Content } from '@smui/card';
8
 
9
  export let data;
 
 
 
10
 
11
  let show_step1_info = false;
12
 
 
65
  {/if}
66
  <div class="row">
67
  <div class="col s8">
68
+ <VegaLite data={topic_overview_data} spec={topic_overview_spec} bind:view={topic_overview_view}/>
69
  </div>
70
  </div>
71
 
indie_label_svelte/src/SubmitReportDialog.svelte CHANGED
@@ -2,20 +2,20 @@
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() {
@@ -23,11 +23,19 @@
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();
@@ -51,20 +59,25 @@
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}
@@ -73,11 +86,7 @@
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%" />
@@ -85,7 +94,7 @@
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
 
 
2
  import Dialog, { Title, Content, Actions } from "@smui/dialog";
3
  import Button, { Label } from "@smui/button";
4
  import Textfield from "@smui/textfield";
 
5
  import CircularProgress from '@smui/circular-progress';
6
+ import Checkbox from '@smui/checkbox';
7
 
8
  export let open;
9
  export let cur_user;
10
  export let all_reports;
11
+ let name = "";
12
  let email = "";
13
+ // which_reports_to_submit is an array of booleans that tracks whether the report
14
+ // in the corresponding index of all_reports should be submitted to AVID.
15
+ let which_reports_to_submit = [];
16
+ for (let i = 0; i < all_reports.length; i++) {
17
+ which_reports_to_submit.push(false);
18
+ }
 
19
 
20
  let promise_submit = Promise.resolve(null);
21
  function handleSubmitReport() {
 
23
  }
24
 
25
  async function submitReport() {
26
+ //Get the relevant reports
27
+ let submitted_reports = [];
28
+ for (let i = 0; i < which_reports_to_submit.length; i++) {
29
+ if (which_reports_to_submit[i]) {
30
+ submitted_reports.push(all_reports[i])
31
+ }
32
+ }
33
+
34
  let req_params = {
35
  cur_user: cur_user,
36
+ reports: JSON.stringify(submitted_reports),
37
+ name: name,
38
  email: email,
 
39
  };
40
 
41
  let params = new URLSearchParams(req_params).toString();
 
59
  <!-- Description -->
60
  <div>
61
  <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>
62
+ 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 name and/or email so that we can contact you if we have any questions.
63
  </div>
64
 
65
  <!-- Summary of complete reports -->
66
  <div>
67
+ <p><b>Summary of Reports Eligible to Send</b> (Reports that include all fields)</p>
68
+ <p> Select the reports you want to submit. </p>
69
  <ul>
70
+ {#each all_reports as report, index}
71
+ {#if (report["evidence"].length > 0) && (report["text_entry"] != "") && (report["sep_selection"])}
72
+
73
+ <input type="checkbox" bind:checked={which_reports_to_submit[index]} />
74
+
75
+ <span>{report["title"]}</span>
76
  <ul>
77
  <li>Error Type: {report["error_type"]}</li>
78
  <li>Evidence: Includes {report["evidence"].length} example{(report["evidence"].length > 1) ? 's' : ''}</li>
79
  <li>Summary/Suggestions: {report["text_entry"]}</li>
80
+ <li>Audit Category: {report["sep_selection"] || ''}</li>
81
  </ul>
82
  {/if}
83
  {/each}
 
86
 
87
  <!-- Form fields -->
88
  <div>
89
+ <Textfield bind:value={name} label="(Optional) Name" style="width: 90%" />
 
 
 
 
90
  </div>
91
  <div>
92
  <Textfield bind:value={email} label="(Optional) Contact email" style="width: 90%" />
 
94
 
95
  <!-- Submission and status message -->
96
  <div class="dialog_footer">
97
+ <Button on:click={handleSubmitReport} variant="outlined" disabled={which_reports_to_submit.filter(item => item).length == 0}>
98
  <Label>Submit Report to AVID</Label>
99
  </Button>
100
 
server.py CHANGED
@@ -426,6 +426,7 @@ def get_reports():
426
  "error_type": "",
427
  "evidence": [],
428
  "text_entry": "",
 
429
  "complete_status": False,
430
  }
431
  ]
@@ -446,6 +447,7 @@ def get_fixed_scaffold():
446
  "error_type": "System is under-sensitive",
447
  "evidence": [],
448
  "text_entry": "",
 
449
  "complete_status": False,
450
  },
451
  {
@@ -453,6 +455,7 @@ def get_fixed_scaffold():
453
  "error_type": "System is over-sensitive",
454
  "evidence": [],
455
  "text_entry": "",
 
456
  "complete_status": False,
457
  },
458
  {
@@ -460,6 +463,7 @@ def get_fixed_scaffold():
460
  "error_type": "System is under-sensitive",
461
  "evidence": [],
462
  "text_entry": "",
 
463
  "complete_status": False,
464
  },
465
  {
@@ -467,6 +471,7 @@ def get_fixed_scaffold():
467
  "error_type": "System is over-sensitive",
468
  "evidence": [],
469
  "text_entry": "",
 
470
  "complete_status": False,
471
  },
472
  {
@@ -474,6 +479,7 @@ def get_fixed_scaffold():
474
  "error_type": "System is under-sensitive",
475
  "evidence": [],
476
  "text_entry": "",
 
477
  "complete_status": False,
478
  },
479
  ]
@@ -484,6 +490,7 @@ def get_empty_report(title, error_type):
484
  "error_type": error_type,
485
  "evidence": [],
486
  "text_entry": "",
 
487
  "complete_status": False,
488
  }
489
 
@@ -494,6 +501,7 @@ def get_tutorial_scaffold():
494
  "error_type": "System is over-sensitive",
495
  "evidence": [],
496
  "text_entry": "",
 
497
  "complete_status": False,
498
  },
499
  ]
@@ -606,6 +614,7 @@ def get_prompts_scaffold():
606
  "error_type": "System is over-sensitive",
607
  "evidence": [],
608
  "text_entry": "",
 
609
  "complete_status": False,
610
  },
611
  {
@@ -613,6 +622,7 @@ def get_prompts_scaffold():
613
  "error_type": "System is under-sensitive",
614
  "evidence": [],
615
  "text_entry": "",
 
616
  "complete_status": False,
617
  },
618
  {
@@ -620,6 +630,7 @@ def get_prompts_scaffold():
620
  "error_type": "",
621
  "evidence": [],
622
  "text_entry": "",
 
623
  "complete_status": False,
624
  },
625
  {
@@ -627,6 +638,7 @@ def get_prompts_scaffold():
627
  "error_type": "",
628
  "evidence": [],
629
  "text_entry": "",
 
630
  "complete_status": False,
631
  },
632
  {
@@ -634,6 +646,7 @@ def get_prompts_scaffold():
634
  "error_type": "",
635
  "evidence": [],
636
  "text_entry": "",
 
637
  "complete_status": False,
638
  },
639
  ]
@@ -648,7 +661,7 @@ def get_eligible_reports(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')
@@ -659,7 +672,8 @@ def submit_reports_to_AVID(reports, cur_user, email, sep_selection, debug=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()
@@ -693,14 +707,14 @@ def save_reports(debug=DEBUG):
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",
 
426
  "error_type": "",
427
  "evidence": [],
428
  "text_entry": "",
429
+ "sep_selection": "",
430
  "complete_status": False,
431
  }
432
  ]
 
447
  "error_type": "System is under-sensitive",
448
  "evidence": [],
449
  "text_entry": "",
450
+ "sep_selection": "",
451
  "complete_status": False,
452
  },
453
  {
 
455
  "error_type": "System is over-sensitive",
456
  "evidence": [],
457
  "text_entry": "",
458
+ "sep_selection": "",
459
  "complete_status": False,
460
  },
461
  {
 
463
  "error_type": "System is under-sensitive",
464
  "evidence": [],
465
  "text_entry": "",
466
+ "sep_selection": "",
467
  "complete_status": False,
468
  },
469
  {
 
471
  "error_type": "System is over-sensitive",
472
  "evidence": [],
473
  "text_entry": "",
474
+ "sep_selection": "",
475
  "complete_status": False,
476
  },
477
  {
 
479
  "error_type": "System is under-sensitive",
480
  "evidence": [],
481
  "text_entry": "",
482
+ "sep_selection": "",
483
  "complete_status": False,
484
  },
485
  ]
 
490
  "error_type": error_type,
491
  "evidence": [],
492
  "text_entry": "",
493
+ "sep_selection": "",
494
  "complete_status": False,
495
  }
496
 
 
501
  "error_type": "System is over-sensitive",
502
  "evidence": [],
503
  "text_entry": "",
504
+ "sep_selection": "",
505
  "complete_status": False,
506
  },
507
  ]
 
614
  "error_type": "System is over-sensitive",
615
  "evidence": [],
616
  "text_entry": "",
617
+ "sep_selection": "",
618
  "complete_status": False,
619
  },
620
  {
 
622
  "error_type": "System is under-sensitive",
623
  "evidence": [],
624
  "text_entry": "",
625
+ "sep_selection": "",
626
  "complete_status": False,
627
  },
628
  {
 
630
  "error_type": "",
631
  "evidence": [],
632
  "text_entry": "",
633
+ "sep_selection": "",
634
  "complete_status": False,
635
  },
636
  {
 
638
  "error_type": "",
639
  "evidence": [],
640
  "text_entry": "",
641
+ "sep_selection": "",
642
  "complete_status": False,
643
  },
644
  {
 
646
  "error_type": "",
647
  "evidence": [],
648
  "text_entry": "",
649
+ "sep_selection": "",
650
  "complete_status": False,
651
  },
652
  ]
 
661
 
662
  # Submit all reports to AVID
663
  # Logs the responses
664
+ def submit_reports_to_AVID(reports, cur_user, name, email, debug=DEBUG):
665
  # Set up the connection to AVID
666
  root = os.environ.get('AVID_API_URL')
667
  api_key = os.environ.get('AVID_API_KEY')
 
672
  print("Num eligible reports:", len(reports))
673
 
674
  for r in reports:
675
+ sep_selection = r["sep_selection"]
676
+ new_report = utils.convert_indie_label_json_to_avid_json(r, cur_user, name, email, sep_selection)
677
  url = root + "submit"
678
  response = requests.post(url, json=json.loads(new_report), headers=key) # The loads ensures type compliance
679
  uuid = response.json()
 
707
  @app.route("/submit_avid_report")
708
  def submit_avid_report():
709
  cur_user = request.args.get("cur_user")
710
+ name = request.args.get("name")
711
  email = request.args.get("email")
 
712
  reports_json = request.args.get("reports")
713
 
714
  reports = json.loads(reports_json)
715
 
716
  # Submit reports to AVID
717
+ submit_reports_to_AVID(reports, cur_user, name, email)
718
 
719
  results = {
720
  "status": "success",