<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>