Spaces:
Runtime error
Runtime error
<script lang="ts"> | |
import { VegaLite } from "svelte-vega"; | |
import type { View } from "svelte-vega"; | |
import DataTable, { | |
Head, | |
Body, | |
Row, | |
Cell, | |
Pagination, | |
} from "@smui/data-table"; | |
import Select, { Option } from "@smui/select"; | |
import IconButton from "@smui/icon-button"; | |
import Button from "@smui/button"; | |
import { Label } from "@smui/common"; | |
import Checkbox from '@smui/checkbox'; | |
import Radio from '@smui/radio'; | |
import FormField from '@smui/form-field'; | |
import Tooltip, { Wrapper } from '@smui/tooltip'; | |
import LayoutGrid, { Cell as LGCell } from "@smui/layout-grid"; | |
import Card, { Content } from '@smui/card'; | |
import HelpTooltip from "./HelpTooltip.svelte"; | |
import { topic_chosen } from './stores/cur_topic_store.js'; | |
import { new_evidence } from './stores/new_evidence_store.js'; | |
import { open_evidence } from './stores/open_evidence_store.js'; | |
export let data; | |
export let cluster; | |
export let clusters = null; | |
export let model; | |
export let show_vis = true; | |
export let show_checkboxes = true; | |
export let table_width_pct = 80; | |
export let rowsPerPage = 10; | |
export let evidence; | |
export let table_id; | |
export let use_model = true; | |
let N_COMMENTS = 500; | |
let show_num_ratings = false; | |
let show_your_decision_ratings = false; | |
let show_step2_info = false; | |
let comment_table_style; | |
if (show_checkboxes) { | |
comment_table_style = "" | |
} else { | |
comment_table_style = "comment_table_small" | |
} | |
// Handle Altair selections | |
let selected_comment_id = 0; | |
window.addEventListener("popstate", function (event) { | |
//your code goes here on location change | |
let cur_url = window.location.href; | |
let cur_url_elems = cur_url.split("#"); | |
// console.log(cur_url_elems) | |
if (cur_url_elems.length > 0) { | |
let path = cur_url_elems[2]; | |
if (path == "comment") { | |
let comment_id = cur_url_elems[1].split("/")[0]; | |
console.log("comment_id", comment_id) | |
selected_comment_id = parseInt(comment_id); | |
let table_ind = null; | |
for (let i = 0; i < items.length; i++) { | |
if (items[i]["id"] == selected_comment_id) { | |
table_ind = i; | |
break; | |
} | |
} | |
currentPage = table_ind / rowsPerPage; | |
} else if (path == "topic") { | |
let topic = cur_url_elems[1].split("/")[0]; | |
topic_chosen.update((value) => topic); // update in store | |
} | |
// window.history.replaceState({}, document.title, "/"); // remove URL parameter | |
} | |
}); | |
// Cluster Overview Plot | |
let cluster_overview_data = null; | |
let cluster_overview_spec = null; | |
let cluster_overview_view = null; | |
if (show_vis) { | |
let cluster_overview_json = data["cluster_overview_plot_json"]; | |
cluster_overview_data = | |
cluster_overview_json["datasets"][ | |
cluster_overview_json["layer"][0]["data"]["name"] | |
]; | |
cluster_overview_spec = cluster_overview_json; | |
cluster_overview_view = null; | |
} | |
type ClusterComment = { | |
id: number; | |
comment: string; | |
user_decision: string; | |
user_rating: number; | |
system_decision: string; | |
system_rating: number; | |
user_color: string; | |
system_color: string; | |
error_type: string; | |
error_color: string; | |
judgment: string; | |
toxicity_category: string; | |
}; | |
let items: ClusterComment[]; | |
let selected = []; | |
// Pagination | |
let currentPage = 0; | |
$: start = currentPage * rowsPerPage; | |
$: end = Math.min(start + rowsPerPage, items.length); | |
$: slice = items.slice(start, end); | |
$: lastPage = Math.max(Math.ceil(items.length / rowsPerPage) - 1, 0); | |
$: if (currentPage > lastPage) { | |
currentPage = lastPage; | |
} | |
let set_length = 0; | |
// if (typeof(data["cluster_comments"] == "string")) { | |
if (show_checkboxes) { | |
items = JSON.parse(data["cluster_comments"]); | |
set_length = data["topic_df_ids"].length; | |
} else { | |
items = data["cluster_comments"]; | |
set_length = items.length; | |
} | |
// console.log(set_length); | |
let cur_open_evidence; | |
open_evidence.subscribe(value => { | |
cur_open_evidence = value; | |
}); | |
function saveToEvidence() { | |
new_evidence.update((value) => []); // clear prior evidence | |
selected.forEach(function(s) { | |
if (!cur_open_evidence.includes(s)) { | |
new_evidence.update((value) => s); // update in store | |
} | |
}); | |
selected = []; | |
// Clear highlighted rows | |
let rows = document.getElementsByTagName("tr"); | |
let row_list = Array.prototype.slice.call(rows); | |
row_list.forEach(function(r) { | |
r.classList.remove("mdc-data-table__row--selected"); | |
}); | |
let checkbox_header_divs = document.getElementsByClassName("mdc-data-table__header-row-checkbox"); | |
let checkbox_header_list = Array.prototype.slice.call(checkbox_header_divs); | |
checkbox_header_list.forEach(function(c) { | |
let c_input = c.getElementsByTagName("input"); | |
for (let i = 0; i < c_input.length; i++) { | |
c_input[i].setAttribute("data-indeterminate", "false"); | |
c_input[i].indeterminate = false; | |
} | |
}); | |
} | |
function handleAdd(comment_to_remove) { | |
new_evidence.update((value) => []); // clear prior evidence | |
if (!cur_open_evidence.includes(comment_to_remove)) { | |
new_evidence.update((value) => comment_to_remove); // update in store | |
} | |
} | |
function handleRemove(comment_to_remove) { | |
// Update local open evidence | |
cur_open_evidence = cur_open_evidence.filter(item => item.comment != comment_to_remove) | |
// Update open evidence in store | |
open_evidence.update((value) => cur_open_evidence); | |
} | |
</script> | |
<div class="padding-top: 30px;"> | |
{#if show_vis} | |
<div> | |
<span class="head_6">Topic overview: {cluster}</span> | |
<IconButton | |
class="material-icons grey_button" | |
size="normal" | |
on:click={() => (show_step2_info = !show_step2_info)} | |
> | |
help_outline | |
</IconButton> | |
</div> | |
{#if N_COMMENTS < set_length} | |
<p>Showing a random sample of {N_COMMENTS} comments (out of {set_length} comments)</p> | |
{:else} | |
<p>Showing all {set_length} comments</p> | |
{/if} | |
{#if show_step2_info} | |
<LayoutGrid> | |
<LGCell span={8}> | |
<div class="card-container"> | |
<Card variant="outlined" padded> | |
<p class="mdc-typography--button"><b>Interpreting this visualization</b></p> | |
<ul> | |
<li> | |
This plot has the same layout as the <b>All Topics</b> visualization, but now, each <b>box</b> in this plot represents an <b>individual comment</b> that belongs to your <b>selected topic area</b>. | |
</li> | |
<li> | |
The <b>x-axis</b> represents our prediction of <b>your</b> toxicity rating for each comment (we'll call these "your ratings") | |
<ul> | |
<li> | |
The <b>left side</b> (white background) is the <b>Non-toxic</b> side (comments that'll be allowed to remain) | |
</li> | |
<li> | |
The <b>right side</b> (grey background) is the <b>Toxic</b> side (comments that will be deleted) | |
</li> | |
<li> | |
Comment boxes are plotted along the x-axis based on our prediction of your toxicity rating for that comment | |
</li> | |
</ul> | |
</li> | |
<li> | |
The <b>color</b> of the box indicates the <b>system's rating</b> for the same comment; you may want to focus on the <b>red-colored boxes</b> that indicate <b>disagreements</b> between "your ratings" and the system's ratings | |
</li> | |
</ul> | |
</Card> | |
</div> | |
</LGCell> | |
</LayoutGrid> | |
{/if} | |
<div class="row"> | |
<div class="col s8"> | |
<VegaLite | |
{cluster_overview_data} | |
spec={cluster_overview_spec} | |
bind:view={cluster_overview_view} | |
/> | |
</div> | |
</div> | |
{/if} | |
{#if show_checkboxes} | |
<h6>Comments</h6> | |
{/if} | |
<!-- Display options --> | |
{#if show_checkboxes} | |
<div> | |
Numerical ratings: | |
<FormField> | |
<Radio bind:group={show_num_ratings} value={true} color="secondary" /> | |
<span slot="label">Show</span> | |
</FormField> | |
<FormField> | |
<Radio bind:group={show_num_ratings} value={false} color="secondary" /> | |
<span slot="label">Hide</span> | |
</FormField> | |
</div> | |
{#if use_model} | |
<div> | |
Our prediction of your decision + ratings: | |
<FormField> | |
<Radio bind:group={show_your_decision_ratings} value={true} color="secondary" /> | |
<span slot="label">Show</span> | |
</FormField> | |
<FormField> | |
<Radio bind:group={show_your_decision_ratings} value={false} color="secondary" /> | |
<span slot="label">Hide</span> | |
</FormField> | |
</div> | |
{/if} | |
<!-- <Wrapper> | |
<IconButton class="material-icons" size="button" disabled>help_outline</IconButton> | |
<Tooltip>White = Non-toxic, Grey = Toxic</Tooltip> | |
</Wrapper> --> | |
{/if} | |
{#key evidence} | |
<div class="comment_table {comment_table_style}"> | |
<DataTable | |
table$aria-label="Comments in the topic cluster" | |
style="width: {table_width_pct}%;" | |
> | |
<Head> | |
<Row> | |
<!-- {#if show_checkboxes} | |
<Cell checkbox> | |
<Checkbox /> | |
</Cell> | |
{/if} --> | |
<Cell style="width: 50%">Comment</Cell> | |
{#if show_your_decision_ratings} | |
<Cell>Our prediction<br>of your decision</Cell> | |
{#if show_num_ratings} | |
<Cell>Our prediction<br>of your rating</Cell> | |
{/if} | |
{/if} | |
<Cell> | |
System<br>decision<br> | |
{#if show_checkboxes} | |
<span style="font-size:12px; max-width:125px">White = Non-toxic, <br>Grey = Toxic</span> | |
{/if} | |
</Cell> | |
{#if show_num_ratings} | |
<Cell>System<br>rating</Cell> | |
{/if} | |
{#if show_checkboxes} | |
{#if use_model} | |
<Cell> | |
Potential error<br>type<br> | |
{#if show_checkboxes} | |
<span style="font-size:12px; max-width:125px">Darker red = Greater <br>potential system error</span> | |
{/if} | |
</Cell> | |
<Cell>Potential toxicity<br>categories</Cell> | |
{/if} | |
{/if} | |
<Cell>Do you agree<br>with the system?</Cell> | |
{#if !show_checkboxes} | |
<Cell>Remove</Cell> | |
{/if} | |
{#if show_checkboxes} | |
<Cell>Add<br>Evidence</Cell> | |
{/if} | |
</Row> | |
</Head> | |
<Body> | |
{#each slice as item (item.id + table_id)} | |
<Row> | |
<!-- {#if show_checkboxes} | |
<Cell checkbox> | |
<Checkbox | |
bind:group={selected} | |
value={{ | |
"comment": item.comment, | |
"user_color": item.user_color, | |
"user_decision": item.user_decision, | |
"user_rating": item.user_rating, | |
"system_color": item.system_color, | |
"system_decision": item.system_decision, | |
"system_rating": item.system_rating, | |
"error_type": item.error_type, | |
"error_color": item.error_color, | |
"toxicity_category": item.toxicity_category, | |
"judgment": item.judgment, | |
"id": item.id | |
}} | |
valueKey={item.comment} | |
/> | |
</Cell> | |
{/if} --> | |
<Cell> | |
{item.comment} | |
</Cell> | |
{#if show_your_decision_ratings} | |
<Cell style="background-color: {item.user_color}; border-left: 1px solid rgba(0,0,0,.12); border-right: 1px solid rgba(0,0,0,.12); border-collapse: collapse;"> | |
{item.user_decision} | |
</Cell> | |
{#if show_num_ratings} | |
<Cell style="background-color: {item.user_color}; border-left: 1px solid rgba(0,0,0,.12); border-right: 1px solid rgba(0,0,0,.12); border-collapse: collapse;"> | |
{item.user_rating} | |
</Cell> | |
{/if} | |
{/if} | |
<Cell style="background-color: {item.system_color}; border-left: 1px solid rgba(0,0,0,.12); border-right: 1px solid rgba(0,0,0,.12); border-collapse: collapse;"> | |
{item.system_decision} | |
</Cell> | |
{#if show_num_ratings} | |
<Cell style="background-color: {item.system_color}; border-left: 1px solid rgba(0,0,0,.12); border-right: 1px solid rgba(0,0,0,.12); border-collapse: collapse;"> | |
{item.system_rating} | |
</Cell> | |
{/if} | |
{#if show_checkboxes} | |
{#if use_model} | |
<Cell style="background-color: {item.error_color}; border-left: 1px solid rgba(0,0,0,.12); border-right: 1px solid rgba(0,0,0,.12); border-collapse: collapse;"> | |
{item.error_type} | |
</Cell> | |
<Cell> | |
{item.toxicity_category} | |
</Cell> | |
{/if} | |
{/if} | |
<Cell> | |
<div> | |
<FormField> | |
<Radio | |
bind:group={item.judgment} | |
value={"Agree"} | |
/> | |
<span slot="label">Agree</span> | |
</FormField> | |
</div> | |
<div> | |
<FormField> | |
<Radio | |
bind:group={item.judgment} | |
value={"Disagree"} | |
/> | |
<span slot="label">Disagree</span> | |
</FormField> | |
</div> | |
</Cell> | |
{#if !show_checkboxes} | |
<Cell> | |
<IconButton class="material-icons grey_button" on:click={() => handleRemove(item.comment)}> | |
remove_circle_outline | |
</IconButton> | |
</Cell> | |
{/if} | |
{#if show_checkboxes} | |
<Cell> | |
<IconButton class="material-icons grey_button" on:click={() => handleAdd(item)}> | |
add_circle_outline | |
</IconButton> | |
</Cell> | |
{/if} | |
</Row> | |
{/each} | |
</Body> | |
<!-- Table pagination --> | |
<Pagination slot="paginate"> | |
<svelte:fragment slot="rowsPerPage"> | |
<Label>Rows Per Page</Label> | |
<Select variant="outlined" bind:value={rowsPerPage} noLabel> | |
<Option value={5}>5</Option> | |
<Option value={10}>10</Option> | |
<Option value={25}>25</Option> | |
<Option value={100}>100</Option> | |
</Select> | |
</svelte:fragment> | |
<svelte:fragment slot="total"> | |
{start + 1}-{end} of {items.length} | |
</svelte:fragment> | |
<IconButton | |
class="material-icons" | |
action="first-page" | |
title="First page" | |
on:click={() => (currentPage = 0)} | |
disabled={currentPage === 0}>first_page</IconButton | |
> | |
<IconButton | |
class="material-icons" | |
action="prev-page" | |
title="Prev page" | |
on:click={() => currentPage--} | |
disabled={currentPage === 0}>chevron_left</IconButton | |
> | |
<IconButton | |
class="material-icons" | |
action="next-page" | |
title="Next page" | |
on:click={() => currentPage++} | |
disabled={currentPage === lastPage} | |
>chevron_right</IconButton | |
> | |
<IconButton | |
class="material-icons" | |
action="last-page" | |
title="Last page" | |
on:click={() => (currentPage = lastPage)} | |
disabled={currentPage === lastPage}>last_page</IconButton | |
> | |
</Pagination> | |
</DataTable> | |
</div> | |
{/key} | |
<!-- {#if show_checkboxes} | |
<div class="spacing_vert"> | |
<Button on:click={saveToEvidence} disabled={selected.length == 0} variant="outlined"> | |
<Label>Save {selected.length} to evidence</Label> | |
</Button> | |
</div> | |
{/if} --> | |
<!-- Old visualization --> | |
<!-- {#if show_vis} | |
<div style="margin-top: 500px"> | |
<table> | |
<tbody> | |
<tr class="custom-blue"> | |
<td class="bold"> | |
Compared to the system, YOUR labels are on average... | |
</td> | |
<td> | |
<span class="bold-large" | |
>{data["user_perf_rounded"]} points | |
{data["user_direction"]}</span | |
> | |
for this cluster | |
</td> | |
</tr> | |
<tr> | |
<td class="bold"> | |
Compared to the system, OTHER USERS' labels are on | |
average... | |
</td> | |
<td> | |
<span class="bold-large" | |
>{data["other_perf_rounded"]} points | |
{data["other_direction"]}</span | |
> | |
for this cluster (based on {data["n_other_users"]} randomly-sampled | |
users) | |
</td> | |
</tr> | |
<tr> | |
<td class="bold"> Odds ratio </td> | |
<td> | |
<span class="bold-large">{data["odds_ratio"]}</span><br /> | |
{data["odds_ratio_explanation"]} | |
</td> | |
</tr> | |
</tbody> | |
</table> | |
<h6>Cluster examples</h6> | |
<div class="row"> | |
<div class="col s12"> | |
<div id="cluster_results_elem"> | |
{@html data["cluster_examples"]} | |
</div> | |
</div> | |
</div> | |
</div> | |
{/if} --> | |
</div> | |
<style> | |
/* Styles for table */ | |
:global(html) { | |
height: auto; | |
width: auto; | |
position: static; | |
} | |
:global(#sapper), | |
:global(body) { | |
display: block; | |
height: auto; | |
} | |
</style> | |