|
(function ($) {
|
|
|
|
|
|
const datasetInfo = {
|
|
"femicides/rai": "This dataset consists of news reports and metadata about femicides (i.e., murders in which the perpetrator, often a partner or ex-partner, kills a woman because of her gender) perpetrated in Italy between 2015-2017. The dataset has been compiled by the research unit of national public broadcaster RAI in collaboration with a team of sociologists. The news reports come from a variety of national and regional news outlets. Because of the rich set of available metadata (only a small part of which is, as of now, available in this tool), newspaper reports that report the same femicide can be linked, allowing for investigating how one and the same event is framed in different news reports, and how framing changes over time.",
|
|
"femicides/olv": "This dataset consists of news reports and metadata about femicides (i.e., murders in which the perpetrator, often a partner or ex-partner, kills a woman because of her gender) perpetrated in Italy between 2012-2019. The dataset consists of a set of case summaries and metadata (see https://27esimaora.corriere.it/la-strage-delle-donne/) compiled by journalists of Corriere della Sera, a major national newspaper, and a set of newspaper articles from several national newspapers mentioning these cases (automatically matched based on victim names).",
|
|
"migration/pavia": "This dataset consists of immigration-related news articles in Italy published between 2013-2021, collected by the Osservatorio di Pavia media research organization (https://www.osservatorio.it/) ",
|
|
"crashes/thecrashes": "This dataset consists of news articles in Dutch-language news outlets (mostly regional Dutch and Belgian newspapers) describing traffic crashes, together with metadata about the events. This dataset is developed by researchers at the University of Amsterdam, in with journalists at The Correspondent, and is publically available at https://nl.roaddanger.org/."
|
|
};
|
|
const datasetFrameInfo = {
|
|
"femicides/rai": "For investigating the conceptualization of femicides in the media, we have created three clusters of frames. The most important one, 'murder', comprises frames that describe the act of murder itself in various degrees of agentivity (KILLING implies an active agent, DEATH and DEAD_OR_ALIVE describe the death of the victim, and CATASTROPHE and EVENT present the murders as abstract events or tragedies).",
|
|
"femicides/olv": "For investigating the conceptualization of femicides in the media, we have created three clusters of frames. The most important one, 'murder', comprises frames that describe the act of murder itself in various degrees of agentivity (KILLING implies an active agent, DEATH and DEAD_OR_ALIVE describe the death of the victim, and CATASTROPHE and EVENT present the murders as abstract events or tragedies).",
|
|
"migration/pavia": "Several clusters of frames were created, reflecting different aspects of media coverage on migration, including the depiction of migrants as quantities ('a tsunami of migrants'), political aspects of migration, and integration of migrants into society.",
|
|
"crashes/thecrashes": "We defined a single cluster of frames that conceptualize several salient properties of traffic crashes, both from an agent/cause-centered perspective (KILLING, CAUSE_HARM) and from a non-agentive perspective (EVENT, EXPERIENCE_BODILY_HARM)"
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
let activeDataset = "femicides/olv";
|
|
let activeLomeModel = "lome_0shot";
|
|
|
|
|
|
let relevantFrameCluster = "ALL";
|
|
let relevantFrames = {};
|
|
|
|
|
|
let activeEventFilters = [];
|
|
let activeDocumentFilters = [];
|
|
|
|
|
|
let tableFilters = new Set();
|
|
|
|
|
|
|
|
const $framesOut = $("#frames-out");
|
|
|
|
|
|
const $frameList = $("#frame-list");
|
|
|
|
|
|
const $documentList = $("#document-list");
|
|
|
|
|
|
const $tableModalRows = $("#table-modal-rows");
|
|
|
|
|
|
const filtersToDatasets = {
|
|
"#doc-filter-days-after": ["rai", "olv", "thecrashes"],
|
|
"#doc-filter-provider": ["rai", "olv", "thecrashes", "pavia"],
|
|
"#doc-filter-politics-unipv": ["rai"],
|
|
"#doc-filter-politics-tc": ["rai"],
|
|
"#doc-filter-politics-agg": ["rai"],
|
|
"#doc-filter-politics-sc": ["pavia"],
|
|
"#doc-filter-religion-sc": ["pavia"],
|
|
"#doc-filter-type": ["rai"],
|
|
"#doc-filter-area": ["rai", "thecrashes"],
|
|
"#doc-filter-country": ["thecrashes"],
|
|
"#doc-filter-province": ["thecrashes"],
|
|
"#doc-filter-content-type": ["thecrashes"],
|
|
"#doc-filter-medium-type": ["thecrashes"],
|
|
"#doc-filter-owner": ["thecrashes"],
|
|
"#ev-filter-event-cat": ["rai"],
|
|
"#ev-filter-region": ["rai", "olv"],
|
|
"#ev-filter-semloc": ["rai"],
|
|
"#ev-filter-vict-age": ["rai", "olv"],
|
|
"#ev-filter-vict-nat": ["rai"],
|
|
"#ev-filter-injured-total": ["thecrashes"],
|
|
"#ev-filter-injured-child": ["thecrashes"],
|
|
"#ev-filter-injured-pedestrian": ["thecrashes"],
|
|
"#ev-filter-injured-cyclist": ["thecrashes"],
|
|
"#ev-filter-injured-cyclistpedestrian": ["thecrashes"],
|
|
"#ev-filter-injured-vehicle": ["thecrashes"],
|
|
"#ev-filter-dead-total": ["thecrashes"],
|
|
"#ev-filter-dead-child": ["thecrashes"],
|
|
"#ev-filter-dead-pedestrian": ["thecrashes"],
|
|
"#ev-filter-dead-cyclist": ["thecrashes"],
|
|
"#ev-filter-dead-cyclistpedestrian": ["thecrashes"],
|
|
"#ev-filter-dead-vehicle": ["thecrashes"],
|
|
"#ev-filter-deadinjured-total": ["thecrashes"],
|
|
"#ev-filter-deadinjured-child": ["thecrashes"],
|
|
"#ev-filter-deadinjured-pedestrian": ["thecrashes"],
|
|
"#ev-filter-deadinjured-cyclist": ["thecrashes"],
|
|
"#ev-filter-deadinjured-cyclistpedestrian": ["thecrashes"],
|
|
"#ev-filter-deadinjured-vehicle": ["thecrashes"],
|
|
"#ev-filter-imbalanced": ["thecrashes"],
|
|
"#ev-filter-att-nat": ["rai"],
|
|
"#ev-filter-vict-occ": ["rai"],
|
|
"#ev-filter-att-occ": ["rai"]
|
|
}
|
|
|
|
|
|
|
|
|
|
function getPossibleConstructions() {
|
|
const $constructionSampleList = $("#construction-sample-list, #construction-freq-select");
|
|
$constructionSampleList.empty();
|
|
$constructionSampleList.append($("<option>").text("ANY CONSTRUCTION").attr("value", "*"));
|
|
$.get("constructions").done((data) => {
|
|
data.forEach((construction) => {
|
|
$constructionSampleList
|
|
.append($("<option>")
|
|
.text(construction)
|
|
.attr("value", construction)
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
function getPossibleDependencies() {
|
|
const $depSampleList = $("#dep-sample-list");
|
|
$depSampleList.empty();
|
|
$depSampleList.append($("<option>").text("ANY DEPENDENCY").attr("value", "*"));
|
|
$.get("dep_labels").done((data) => {
|
|
data.forEach((depLabel) => {
|
|
$depSampleList.append($("<option>")
|
|
.text(depLabel)
|
|
.attr("value", depLabel)
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
function updateRoleSampleList(roles) {
|
|
const $roleSampleList = $("#role-sample-list");
|
|
$roleSampleList.empty();
|
|
$roleSampleList.append($("<option>").text("ANY ROLE").attr("value", "*"));
|
|
roles.forEach((role) => {
|
|
$roleSampleList.append($("<option>").text(role).attr("value", role));
|
|
})
|
|
}
|
|
|
|
function processFrames(frameList) {
|
|
let framesOut = {"ALL": [], "OTHER": []};
|
|
frameList.forEach((frame) => {
|
|
if (frame.includes("#")) {
|
|
let frameName = frame.split("#")[0];
|
|
let clusterName = frame.split("#")[1].toLowerCase();
|
|
if (!(clusterName in framesOut)) {
|
|
framesOut[clusterName] = [frameName];
|
|
}
|
|
else {
|
|
framesOut[clusterName].push(frameName);
|
|
}
|
|
framesOut["ALL"].push(frameName);
|
|
}
|
|
else {
|
|
framesOut["OTHER"].push(frame);
|
|
framesOut["ALL"].push(frame);
|
|
}
|
|
});
|
|
return framesOut;
|
|
}
|
|
|
|
function getRelevantFrames() {
|
|
$frameList.empty();
|
|
$.get("frames", { "dataset": activeDataset }).done(function (data) {
|
|
relevantFrames = processFrames(data);
|
|
|
|
|
|
$frameClusters = $("#frame-clusters");
|
|
$frameClusters.empty();
|
|
let keys = Object.keys(relevantFrames);
|
|
keys.sort();
|
|
keys.forEach((cluster) => {
|
|
$frameClusters.append($("<option>").attr("value", cluster).text(cluster));
|
|
});
|
|
|
|
relevantFrames[relevantFrameCluster].forEach(function (frame) {
|
|
$frameList.append(addFrameButton(frame))
|
|
});
|
|
updateFrameSelectors();
|
|
})
|
|
}
|
|
|
|
function getEventFilters() {
|
|
$.get("event_filters").done((data) => {
|
|
|
|
$("#ev-filters select").empty();
|
|
|
|
if (activeDataset === "femicides/rai") {
|
|
data["event_categories"].forEach((category) => {
|
|
$("#ev-filter-event-cat").append(makeFilterOption("e", "event:category", category));
|
|
});
|
|
data["regions"].forEach((region) => {
|
|
$("#ev-filter-region").append(makeFilterOption("e", "event:region", region));
|
|
});
|
|
data["sem_location"].forEach((location) => {
|
|
$("#ev-filter-semloc").append(makeFilterOption("e", "event:semantic_location", location));
|
|
});
|
|
data["victim_age"].forEach((age) => {
|
|
$("#ev-filter-vict-age").append(makeFilterOption("e", "victim:age", age));
|
|
});
|
|
data["victim_nationality"].forEach((nationality) => {
|
|
$("#ev-filter-vict-nat").append(makeFilterOption("e", "victim:nationality", nationality));
|
|
});
|
|
data["attacker_nationality"].forEach((nationality) => {
|
|
$("#ev-filter-att-nat").append(makeFilterOption("e", "attacker:nationality", nationality));
|
|
});
|
|
data["victim_occupation"].forEach((occupation) => {
|
|
$("#ev-filter-vict-occ").append(makeFilterOption("e", "victim:occupation", occupation));
|
|
});
|
|
data["attacker_occupation"].forEach((occupation) => {
|
|
$("#ev-filter-att-occ").append(makeFilterOption("e", "attacker:occupation", occupation));
|
|
});
|
|
} else if (activeDataset === "femicides/olv") {
|
|
data["regions"].forEach((region) => {
|
|
$("#ev-filter-region").append(makeFilterOption("e", "event:region", region));
|
|
});
|
|
data["victim_age"].forEach((age) => {
|
|
$("#ev-filter-vict-age").append(makeFilterOption("e", "victim:age", age));
|
|
});
|
|
} else if (activeDataset === "crashes/thecrashes") {
|
|
data["outcomes"].forEach((option) => {
|
|
Object.keys(filtersToDatasets)
|
|
.filter((key) => key.startsWith("#ev-filter-injured-") || key.startsWith("#ev-filter-dead-") || key.startsWith("#ev-filter-deadinjured-"))
|
|
.forEach((key) => {
|
|
const outcome = key.split("-")[2];
|
|
const people = key.split("-")[3];
|
|
$(key).append(makeFilterOption("e", `outcomes:${outcome}:${people}`, option));
|
|
})
|
|
});
|
|
|
|
data["imbalanced"].forEach((option) => {
|
|
$("#ev-filter-imbalanced").append(makeFilterOption("e", "imbalanced", option));
|
|
});
|
|
}
|
|
})
|
|
}
|
|
|
|
function getDocumentFilters(callback = null) {
|
|
$.get("doc_filters").done((data) => {
|
|
|
|
|
|
$("#doc-filters select").empty();
|
|
|
|
const daysAfterOptions = [
|
|
["within 1 day", "day"],
|
|
["between 1 day and 1 week", "week"],
|
|
["between 1 week and 1 month", "month"],
|
|
["between 1 month and 1 year", "year"],
|
|
["later", "later"]
|
|
]
|
|
daysAfterOptions.forEach((option) => {
|
|
$("#doc-filter-days-after").append(makeFilterOption("d", "days_after", option[1], option[0]));
|
|
})
|
|
|
|
data["providers"].forEach((provider) => {
|
|
$("#doc-filter-provider")
|
|
.append(makeFilterOption("d", "provider", provider));
|
|
});
|
|
|
|
|
|
if (activeDataset === "femicides/rai") {
|
|
["left", "right", "neutral"].forEach((stance) => {
|
|
|
|
$("#doc-filter-politics-tc").append(makeFilterOption("d", "politics:tc", stance));
|
|
$("#doc-filter-politics-agg").append(makeFilterOption("d", "politics:agg", stance));
|
|
});
|
|
["national", "regional"].forEach((area) => {
|
|
$("#doc-filter-area").append(makeFilterOption("d", "area", area));
|
|
});
|
|
["agency", "outlet"].forEach((provType) => {
|
|
$("#doc-filter-type").append(makeFilterOption("d", "type", provType));
|
|
});
|
|
} else if (activeDataset === "crashes/thecrashes") {
|
|
["National", "Regional", "Local"].forEach((area) => {
|
|
$("#doc-filter-area").append(makeFilterOption("d", "area", area));
|
|
});
|
|
|
|
["Netherlands", "Belgium"].forEach((country) => {
|
|
$("#doc-filter-country").append(makeFilterOption("d", "country", country));
|
|
});
|
|
|
|
data["provider_provinces"].forEach((province) => {
|
|
$("#doc-filter-province").append(makeFilterOption("d", "province", province));
|
|
});
|
|
|
|
data["provider_content_types"].forEach((contentType) => {
|
|
$("#doc-filter-content-type").append(makeFilterOption("d", "content_type", contentType));
|
|
});
|
|
|
|
data["provider_medium_types"].forEach((mediumType) => {
|
|
$("#doc-filter-medium-type").append(makeFilterOption("d", "medium_type", mediumType));
|
|
})
|
|
|
|
data["provider_owners"].forEach((owner) => {
|
|
$("#doc-filter-owner").append(makeFilterOption("d", "owner", owner));
|
|
})
|
|
|
|
} else if (activeDataset === "migration/pavia") {
|
|
["left", "right", "neutral"].forEach((stance) => {
|
|
$("#doc-filter-politics-sc").append(makeFilterOption("d", "politics:sc", stance));
|
|
});
|
|
["catholic", "non_catholic"].forEach((stance) => {
|
|
$("#doc-filter-religion-sc").append(makeFilterOption("d", "religion:sc", stance));
|
|
});
|
|
|
|
}
|
|
|
|
if (callback !== null) {
|
|
callback();
|
|
}
|
|
|
|
});
|
|
}
|
|
|
|
function getDocumentList() {
|
|
let params = {
|
|
"event_filters": activeEventFilters.join("+"),
|
|
"doc_filters": activeDocumentFilters.join("+")
|
|
};
|
|
$.get("documents", params).done(function (data) {
|
|
const eventIdSet = new Set();
|
|
const docIdSet = new Set();
|
|
$documentList.empty();
|
|
data.forEach((doc) => {
|
|
eventIdSet.add(doc["event_id"])
|
|
docIdSet.add(doc["doc_id"])
|
|
$documentList.append($("<option>")
|
|
.text(`event ${doc["event_id"]} / doc ${doc["doc_id"]}`)
|
|
.attr("value", `${doc["event_id"]}:${doc["doc_id"]}`)
|
|
);
|
|
});
|
|
$(".event-doc-count").text(`(found: ${eventIdSet.size} events and ${docIdSet.size} documents)`)
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
function hideOrShowFilterOptions() {
|
|
let shortKey;
|
|
if (activeDataset === "femicides/rai") {
|
|
shortKey = "rai";
|
|
} else if (activeDataset === "femicides/olv") {
|
|
shortKey = "olv";
|
|
} else if (activeDataset === "crashes/thecrashes") {
|
|
shortKey = "thecrashes";
|
|
} else if (activeDataset === "migration/pavia") {
|
|
shortKey = "pavia";
|
|
} else {
|
|
throw Error("Unsupported dataset");
|
|
}
|
|
|
|
Object.entries(filtersToDatasets).forEach((entry) => {
|
|
let [filter, datasets] = entry;
|
|
if (datasets.includes(shortKey)) {
|
|
$(filter).parent().parent().show();
|
|
} else {
|
|
$(filter).parent().parent().hide();
|
|
}
|
|
})
|
|
}
|
|
|
|
|
|
function makeFilterOption(type, attr, value, valueText = null) {
|
|
|
|
if (valueText === null) {
|
|
valueText = value;
|
|
}
|
|
|
|
return $("<option>").attr("value", `${type}::${attr}::${value}`).text(valueText);
|
|
}
|
|
|
|
|
|
function addFrameButton(frame) {
|
|
return $("<button>")
|
|
.addClass("btn btn-info")
|
|
.text(frame)
|
|
.click(function (event) {
|
|
let $button = $(event.target);
|
|
|
|
|
|
relevantFrames[relevantFrameCluster].splice(relevantFrames[relevantFrameCluster].indexOf($button.text()), 1);
|
|
if (relevantFrameCluster !== "ALL") {
|
|
relevantFrames["ALL"].splice(relevantFrames["ALL"].indexOf($button.text()), 1);
|
|
}
|
|
updateFrameSelectors();
|
|
console.log(relevantFrames);
|
|
$button.remove();
|
|
})
|
|
}
|
|
|
|
function updateFrameSelectors() {
|
|
let $frameSelectors = $("#frame-sample-list, #frame-freq-select");
|
|
$frameSelectors.empty();
|
|
$("#frame-sample-list").append($("<option>").text("ALL FRAMES").attr("value", "*"));
|
|
relevantFrames[relevantFrameCluster].forEach((frame) => {
|
|
$frameSelectors.append($("<option>")
|
|
.text(frame)
|
|
.attr("value", frame)
|
|
);
|
|
});
|
|
$.get("role_labels", { "frame": $("#frame-sample-list").val() }).done((roles) => {
|
|
updateRoleSampleList(roles);
|
|
})
|
|
}
|
|
|
|
|
|
function activateFilter(filterValue) {
|
|
|
|
const $filterList = filterValue.startsWith("e::") ? $("#event-filter-list") : $("#doc-filter-list");
|
|
const activeFilters = filterValue.startsWith("e::") ? activeEventFilters : activeDocumentFilters;
|
|
|
|
if (!activeFilters.includes(filterValue)) {
|
|
activeFilters.push(filterValue);
|
|
console.log(activeFilters);
|
|
|
|
|
|
getDocumentList();
|
|
|
|
|
|
$filterList.append(
|
|
$("<button>")
|
|
.addClass("btn btn-secondary btn-sm mr-3")
|
|
.append($("<samp>").text(filterValue))
|
|
.click((event) => {
|
|
const $target = $(event.target);
|
|
const $button = $target.prop("nodeName") === "SAMP" ? $target.parent() : $target;
|
|
activeFilters.splice(activeFilters.indexOf($button.text()), 1);
|
|
getDocumentList();
|
|
console.log(activeFilters);
|
|
$button.remove();
|
|
})
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
function formatFrameStructure(structure, deep, roleMapping) {
|
|
const targetString = `<b>Target:</b> ${structure["target"]["tokens_str"].join(" ")}`;
|
|
|
|
const targetIdx = structure["target"]["tokens_idx"][0].toString();
|
|
var synRoleDeps;
|
|
if (Object.keys(roleMapping).includes(targetIdx)) {
|
|
synRoleDeps = roleMapping[targetIdx];
|
|
} else {
|
|
synRoleDeps = {};
|
|
}
|
|
|
|
const roles = deep ? structure["deep_roles"] : structure["roles"]
|
|
|
|
const filteredRoles = roles.filter((role) => role[0] !== "Target")
|
|
const roleStrings = filteredRoles.map((role) => {
|
|
let roleName = role[0];
|
|
let roleValue = role[1]["tokens_str"].join(" ");
|
|
let roleDep = "";
|
|
if (!deep) {
|
|
roleDep = Object.keys(synRoleDeps).includes(roleName) ? ` (<code>${synRoleDeps[roleName][0]}</code>)` : ""
|
|
}
|
|
return `<b>${roleName}${roleDep}:</b> ${roleValue}`;
|
|
});
|
|
return targetString + '<br>' + roleStrings.join("<br>");
|
|
}
|
|
|
|
function processSentence(sentenceData, index) {
|
|
let tokens = sentenceData["sentence"];
|
|
let tokenString = tokens.join(" ");
|
|
|
|
function getSyntaxInfo(struct) {
|
|
const targetIdx = struct["target"]["tokens_idx"][0].toString();
|
|
const syntaxForTarget = sentenceData["syntax"][targetIdx];
|
|
return syntaxForTarget[syntaxForTarget.length - 1];
|
|
}
|
|
|
|
function makeFrameButtons(filterRelevant, deep) {
|
|
|
|
let frameButtons = sentenceData["fn_structures"]
|
|
.filter(function (struct) {
|
|
const frameInRelevant = relevantFrames[relevantFrameCluster].includes(struct["frame"]);
|
|
return filterRelevant ? frameInRelevant : !frameInRelevant;
|
|
})
|
|
.map(function (struct) {
|
|
const syntaxInfo = getSyntaxInfo(struct);
|
|
const syntaxCat = syntaxInfo["syn_category"];
|
|
const syntaxConstr = syntaxInfo["syn_construction"];
|
|
const roleMapping = sentenceData["roles"];
|
|
|
|
return $("<button>")
|
|
.addClass(filterRelevant ? "btn btn-info text-white" : "btn btn-dark text-white btn-sm")
|
|
.text(deep ? struct["deep_frame"] : struct["frame"])
|
|
.attr("data-toggle", "popover")
|
|
.attr("data-html", true)
|
|
.attr("title", struct["frame"])
|
|
.attr("data-content", formatFrameStructure(struct, deep, roleMapping))
|
|
.append(
|
|
$("<span>")
|
|
.addClass("badge badge-light ml-1 text-primary")
|
|
.text(syntaxCat)
|
|
)
|
|
.append(
|
|
$("<span>")
|
|
.addClass("badge badge-light ml-1 text-danger")
|
|
.text(syntaxConstr)
|
|
)
|
|
.popover();
|
|
});
|
|
|
|
if (frameButtons.length === 0) {
|
|
frameButtons = [
|
|
$("<button>").addClass("btn btn-info text-white").prop("disabled", true).text("No frames found")
|
|
];
|
|
}
|
|
return frameButtons;
|
|
}
|
|
|
|
let relevantFrameButtons = makeFrameButtons(true, false);
|
|
let deepFrameButtons = makeFrameButtons(true, true);
|
|
let otherFrameButtons = makeFrameButtons(false, false);
|
|
|
|
function formatTextMeta(textMeta) {
|
|
return `<b>Provider:</b> ${textMeta["provider"]}`
|
|
+ `<br/><b>Title:</b> ${textMeta["title"]}`
|
|
+ `<br/><b>Published:</b> ${textMeta["pubdate"]}`
|
|
+ `<br/><b>Days after event:</b> ${textMeta["days_after_event"]}`
|
|
}
|
|
|
|
$framesOut
|
|
.append(
|
|
$("<div>")
|
|
.addClass("card bg-light mb-3")
|
|
.append(
|
|
$("<div>")
|
|
.addClass("card-header")
|
|
.text(`Sentence #${index} `)
|
|
.append($("<small>")
|
|
.text(`(event ${sentenceData["meta"]["event_id"]} / doc ${sentenceData["meta"]["doc_id"]})`))
|
|
.append($("<badge>")
|
|
.addClass("badge badge-dark text-white ml-2")
|
|
.text("text info")
|
|
.attr("data-toggle", "popover")
|
|
.attr("data-html", true)
|
|
.attr("data-content", formatTextMeta(sentenceData["meta"]["text_meta"]))
|
|
.popover()
|
|
)
|
|
)
|
|
.append(
|
|
|
|
$("<div>")
|
|
.addClass("card-body")
|
|
|
|
.append($("<ul>")
|
|
.addClass("list-group list-group-flush")
|
|
|
|
.append($("<li>")
|
|
.addClass("list-group-item")
|
|
.append($("<h6>").text("Sentence:"))
|
|
.append($("<p>").text(tokenString)))
|
|
|
|
.append($("<li>")
|
|
.addClass("list-group-item")
|
|
.append($("<h6>").text("Frames of interest:"))
|
|
.append($("<div>")
|
|
.addClass("btn-group flex-wrap")
|
|
.attr({ "role": "group" }).append(relevantFrameButtons)
|
|
)
|
|
)
|
|
|
|
.append($("<li>")
|
|
.addClass("list-group-item")
|
|
.append($("<h6>").text("Deep Frames of interest:"))
|
|
.append($("<div>")
|
|
.addClass("btn-group flex-wrap")
|
|
.attr({ "role": "group" }).append(deepFrameButtons)
|
|
)
|
|
)
|
|
|
|
.append($("<li>")
|
|
.addClass("list-group-item")
|
|
.append($("<h6>").text("Other frames:"))
|
|
.append($("<div>")
|
|
.addClass("btn-group flex-wrap")
|
|
.attr({ "role": "group" }).append(otherFrameButtons)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
);
|
|
}
|
|
|
|
function processDocument(eventId, docId) {
|
|
$framesOut.empty();
|
|
$.get("analyze", { "event": eventId, "document": docId, "model": activeLomeModel }).done(function (data) {
|
|
data.forEach((element, index) => processSentence(element, index));
|
|
});
|
|
}
|
|
|
|
function processSamples(frame, construction, dependency, role) {
|
|
$framesOut.empty();
|
|
$framesOut.append($("<small>").text("Loading..."));
|
|
$.get("sample_frame", {
|
|
"model": activeLomeModel,
|
|
"frame": frame,
|
|
"construction": construction,
|
|
"dependency": dependency,
|
|
"role": role,
|
|
"event_filters": activeEventFilters.join("+"),
|
|
"doc_filters": activeDocumentFilters.join("+")
|
|
}).done((data) => {
|
|
$framesOut.empty();
|
|
if (data.length === 0) {
|
|
$framesOut.append($("<small>").text("No samples were found"))
|
|
} else {
|
|
data.forEach((element, index) => processSentence(element, index));
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
function showFrameFrequencies($button, frameFilter, constrFilter, groupByTgt, groupByCat, groupByConstr, groupByRoot, groupByRoleExpr) {
|
|
const oldText = $button.text();
|
|
$button.text("Loading...");
|
|
|
|
const useRelativeFreqs = $("#stats-relative").prop("checked");
|
|
const plotOverDaysPost = groupByRoleExpr ? false : $("#plot-over-days-post").prop("checked");
|
|
const plotByYear = groupByRoleExpr ? false : $("#plot-by-year").prop("checked");
|
|
const countOnlyHeadlines = $("#count-only-headlines").prop("checked");
|
|
|
|
if (frameFilter === "FROM_FORM") {
|
|
const frameToAnalyze = $("#frame-freq-select").val();
|
|
console.log(frameToAnalyze);
|
|
frameFilter = [frameToAnalyze];
|
|
}
|
|
|
|
if (constrFilter === "FROM_FORM") {
|
|
const formValue = $("#construction-freq-select").val();
|
|
constrFilter = formValue === "*" ? [] : [formValue];
|
|
} else if (constrFilter === "*") {
|
|
constrFilter = [];
|
|
}
|
|
|
|
const params = {
|
|
"model": activeLomeModel,
|
|
"frames": frameFilter.join("+"),
|
|
"constructions": constrFilter.join("+"),
|
|
"event_filters": activeEventFilters.join("+"),
|
|
"doc_filters": activeDocumentFilters.join("+"),
|
|
"group_by_cat": groupByCat ? "y" : "n",
|
|
"group_by_tgt": groupByTgt ? "y" : "n",
|
|
"group_by_constr": groupByConstr ? "y" : "n",
|
|
"group_by_root": groupByRoot ? "y" : "n",
|
|
"group_by_role_expr": groupByRoleExpr,
|
|
"relative": useRelativeFreqs ? "y" : "n",
|
|
"plot_over_days_post": plotOverDaysPost ? "y" : "n",
|
|
"headlines": countOnlyHeadlines ? "y" : "n",
|
|
"plot_by_year": plotByYear ? "y" : "n",
|
|
"days_time_window": $("#time-window").val()
|
|
};
|
|
$.get("frame_freq", params).done((data) => {
|
|
console.log(data);
|
|
|
|
$button.text(oldText);
|
|
|
|
const plotlyLayout = {
|
|
autosize: true,
|
|
|
|
|
|
showlegend: true,
|
|
legend: {"orientation": "v", "x": 1.0, "y": 0},
|
|
font: {"size": 20}
|
|
};
|
|
|
|
let relevantFramesPlotData = [];
|
|
let allFramesPlotData = [];
|
|
let deepFramesPlotData = [];
|
|
|
|
let frameCountParts = ["relevant_frame_counts"];
|
|
|
|
if (plotOverDaysPost || plotByYear) {
|
|
|
|
plotlyLayout["xaxis"] = {
|
|
"automargin": true,
|
|
"title": {
|
|
"text": plotOverDaysPost ? "days post event" : "years"
|
|
}
|
|
}
|
|
plotlyLayout["yaxis"] = {
|
|
"title": {
|
|
"text": "frame frequency"
|
|
}
|
|
}
|
|
|
|
frameCountParts.forEach((series) => {
|
|
Object.entries(data[series]).forEach((entry) => {
|
|
const [key, value] = entry;
|
|
let targetArray;
|
|
if (series === "relevant_frame_counts") {
|
|
targetArray = relevantFramesPlotData;
|
|
} else if (series === "all_frame_counts") {
|
|
targetArray = allFramesPlotData;
|
|
} else {
|
|
targetArray = deepFramesPlotData;
|
|
}
|
|
targetArray.push({
|
|
"x": value["x"],
|
|
"y": value["y"],
|
|
"stackgroup": "one",
|
|
"name": key,
|
|
});
|
|
})
|
|
})
|
|
|
|
} else {
|
|
const relevant = data["relevant_frame_counts"];
|
|
|
|
let numLabelParts = 2;
|
|
if (groupByRoleExpr === 1) {
|
|
numLabelParts = 1;
|
|
plotlyLayout["showlegend"] = false;
|
|
} else if (groupByRoleExpr > 1) {
|
|
numLabelParts = 3
|
|
}
|
|
|
|
plotlyLayout["barmode"] = "stack";
|
|
plotlyLayout["xaxis"] = {
|
|
"automargin": true,
|
|
"tickangle": 30,
|
|
"categoryorder": "total descending",
|
|
"title": {
|
|
"text": groupByRoleExpr > 0 ? "role" : "frame"
|
|
}
|
|
}
|
|
plotlyLayout["yaxis"] = {
|
|
|
|
"title": {
|
|
"text": "frequency"
|
|
}
|
|
}
|
|
|
|
|
|
const roleSubLabels = new Set(relevant["x"].map((x) => x.split("::")[numLabelParts-1]));
|
|
const roleDepLabelArr = Array.from(roleSubLabels);
|
|
roleDepLabelArr.sort().reverse();
|
|
relevantFramesPlotData = roleDepLabelArr.map((label) => {
|
|
const xs = [];
|
|
const ys = [];
|
|
relevant["x"].forEach((x, idx) => {
|
|
if (numLabelParts > 1 && x.split("::")[numLabelParts-1] === label) {
|
|
xs.push(x.split("::").splice(0, numLabelParts-1).join("::"));
|
|
ys.push(relevant["y"][idx]);
|
|
} else if (numLabelParts == 1) {
|
|
xs.push(x.split("::")[1]);
|
|
ys.push(relevant["y"][idx]);
|
|
}
|
|
})
|
|
return {
|
|
x: xs,
|
|
y: ys,
|
|
name: label,
|
|
type: "bar"
|
|
}
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
let plotlyRelevantFrames = "frame-freq-relevant-plotly";
|
|
|
|
|
|
Plotly.newPlot(plotlyRelevantFrames, relevantFramesPlotData, plotlyLayout);
|
|
|
|
|
|
$("#frame-freq-modal").modal().on("shown.bs.modal", () => {
|
|
Plotly.relayout(plotlyRelevantFrames, { autosize: true })
|
|
|
|
|
|
});
|
|
});
|
|
}
|
|
|
|
|
|
function showStatsTable(frame) {
|
|
|
|
if (frame === "FROM_FORM") {
|
|
frame = $("#frame-freq-select").val();
|
|
}
|
|
|
|
if (frame === "*") {
|
|
frame = "";
|
|
}
|
|
|
|
const $tableModal = $("#table-modal");
|
|
|
|
function roundToDecimals(num, decimals) {
|
|
const multiplier = Math.pow(10, decimals)
|
|
|
|
|
|
return Math.round((num + Number.EPSILON) * multiplier) / multiplier;
|
|
}
|
|
|
|
|
|
const allFilters = activeEventFilters.concat(activeDocumentFilters);
|
|
allFilters.sort();
|
|
const filterString = (
|
|
frame + "__"
|
|
+ (allFilters.length > 0 ? allFilters.join("+") : "no filters")
|
|
+ "__" + activeLomeModel
|
|
);
|
|
|
|
|
|
if (tableFilters.has(filterString)) {
|
|
$tableModal.modal();
|
|
return;
|
|
}
|
|
const $tableModalButton = $("#constr-table-btn");
|
|
const oldText = $tableModalButton.text();
|
|
$tableModalButton.text("Loading...");
|
|
|
|
|
|
tableFilters.add(filterString);
|
|
const rowID = tableFilters.size;
|
|
|
|
let options = {
|
|
"model": activeLomeModel,
|
|
"frames": frame,
|
|
"constructions": "",
|
|
"event_filters": activeEventFilters.join("+"),
|
|
"doc_filters": activeDocumentFilters.join("+"),
|
|
"group_by_cat": "n",
|
|
"group_by_constr": "y",
|
|
"group_by_role_expr": 0,
|
|
"plot_over_days_post": "n",
|
|
"relative": "y"
|
|
};
|
|
|
|
console.log(options);
|
|
|
|
$.get("frame_freq", options).done((data) => {
|
|
$tableModalButton.text(oldText);
|
|
let countMapping = {};
|
|
data["relevant_frame_counts"]["x"].forEach((construction, idx) => {
|
|
countMapping[construction] = roundToDecimals(data["relevant_frame_counts"]["y"][idx], 3);
|
|
});
|
|
$tableModal.modal()
|
|
|
|
const filterStringDisplay = filterString.split("__")[1].replace("+", "<br>")
|
|
const $tableRows = $("<tr>")
|
|
.append($(`<th scope='row'>${rowID}</th>`))
|
|
.append($(`<td><code>${activeLomeModel}</code></td>`))
|
|
.append($(`<td><code>${frame}</code></td>`))
|
|
.append($(`<td><code>${filterStringDisplay}</code></td>`));
|
|
|
|
const countKeys = [
|
|
`${frame}::nonverbal`,
|
|
`${frame}::verbal:active`,
|
|
`${frame}::verbal:passive_unaccusative`,
|
|
`${frame}::verbal:reflexive`,
|
|
`${frame}::other`
|
|
]
|
|
countKeys.forEach((key) => {
|
|
const countValue = key in countMapping ? countMapping[key] : 0.0;
|
|
$tableRows.append(`<td>${countValue}</td>`);
|
|
})
|
|
|
|
$tableModalRows.append($tableRows);
|
|
})
|
|
}
|
|
|
|
function switchInitializeDataset() {
|
|
|
|
const $switchButtons = $("#dataset-switch a");
|
|
$switchButtons.addClass("disabled");
|
|
|
|
|
|
$("#dataset-info").text(datasetInfo[activeDataset]);
|
|
$("#dataset-frame-info").text(datasetFrameInfo[activeDataset]);
|
|
|
|
$.get("switch_dataset", { "dataset": activeDataset }).done(() => {
|
|
console.log(`SERVER: switched dataset to ${activeDataset}`);
|
|
|
|
|
|
|
|
|
|
$("#lome-model-0shot").click();
|
|
|
|
|
|
$("#lome-model-switch a").addClass("disabled");
|
|
if (activeDataset === "femicides/rai") {
|
|
$("#lome-model-0shot").removeClass("disabled");
|
|
$("#lome-model-evalita").removeClass("disabled");
|
|
}
|
|
if (activeDataset === "migration/pavia") {
|
|
$("#lome-model-0shot").removeClass("disabled");
|
|
}
|
|
else {
|
|
$("#lome-model-0shot").removeClass("disabled");
|
|
}
|
|
|
|
|
|
tableFilters.clear();
|
|
$tableModalRows.empty();
|
|
|
|
|
|
$("#doc-filter-list button, #ev-filter-list-button").click();
|
|
|
|
|
|
getRelevantFrames();
|
|
getPossibleConstructions();
|
|
getPossibleDependencies();
|
|
getEventFilters();
|
|
getDocumentFilters(() => {
|
|
|
|
$switchButtons.removeClass("disabled");
|
|
});
|
|
getDocumentList();
|
|
|
|
})
|
|
}
|
|
|
|
|
|
|
|
|
|
$(function () {
|
|
hideOrShowFilterOptions();
|
|
switchInitializeDataset();
|
|
});
|
|
$(".pwd-prompt").hide();
|
|
|
|
|
|
$("#dataset-switch a").click((event) => {
|
|
console.log("Switching datasets...");
|
|
|
|
relevantFrameCluster = "ALL";
|
|
|
|
const datasetKeys = [
|
|
"femicides/olv",
|
|
"femicides/rai",
|
|
"crashes/thecrashes",
|
|
"migration/pavia"
|
|
];
|
|
|
|
const $datasetOptions = [
|
|
$("#femicides-olv"),
|
|
$("#femicides-rai"),
|
|
$("#crashes-thecrashes"),
|
|
$("#migration-pavia")
|
|
];
|
|
|
|
const protectedDatasets = [
|
|
|
|
];
|
|
|
|
$datasetOptions.forEach(($option, idx) => {
|
|
if ($(event.target).is($option)) {
|
|
activeDataset = datasetKeys[idx];
|
|
$("#dataset-switch a").removeClass("active");
|
|
$option.addClass("active");
|
|
}
|
|
})
|
|
|
|
if (protectedDatasets.includes(activeDataset)) {
|
|
console.log("hiding protected elements");
|
|
$(".pwd-protected").hide();
|
|
$(".pwd-prompt").show();
|
|
} else {
|
|
$(".pwd-protected").show();
|
|
$(".pwd-prompt").hide();
|
|
}
|
|
|
|
|
|
hideOrShowFilterOptions();
|
|
switchInitializeDataset();
|
|
|
|
});
|
|
|
|
$("#pwd-submit").click(() => {
|
|
let pwd = $("#password-input").val();
|
|
$.post("/check_password", {"password": pwd}).done((result) => {
|
|
if (result["success"]) {
|
|
$(".pwd-protected").show();
|
|
$(".pwd-prompt").hide();
|
|
hideOrShowFilterOptions();
|
|
switchInitializeDataset();
|
|
}
|
|
})
|
|
});
|
|
|
|
$("#lome-model-switch a").click((event) => {
|
|
console.log("Switching LOME models...");
|
|
|
|
const modelKeys = [
|
|
"lome_0shot",
|
|
"lome_evalita_plus_fn",
|
|
"lome_zs-tgt_ev-frm"
|
|
]
|
|
|
|
const $modelOptions = [
|
|
$("#lome-model-0shot"),
|
|
$("#lome-model-evalita"),
|
|
$("#lome-model-combined"),
|
|
]
|
|
|
|
$modelOptions.forEach(($option, idx) => {
|
|
if ($(event.target).is($option)) {
|
|
activeLomeModel = modelKeys[idx];
|
|
$("#lome-model-switch a").removeClass("active");
|
|
$option.addClass("active");
|
|
}
|
|
})
|
|
|
|
})
|
|
|
|
$("#add-frame-button").click(() => {
|
|
let $addFrameInput = $("#add-frame-input");
|
|
let newFrame = $addFrameInput.val();
|
|
|
|
if (!relevantFrames[relevantFrameCluster].includes(newFrame)) {
|
|
relevantFrames[relevantFrameCluster].push(newFrame);
|
|
}
|
|
if (!relevantFrames["ALL"].includes(newFrame)) {
|
|
relevantFrames["ALL"].push(newFrame);
|
|
}
|
|
console.log(relevantFrames);
|
|
updateFrameSelectors();
|
|
$addFrameInput.val("");
|
|
$frameList.append(addFrameButton(newFrame));
|
|
});
|
|
|
|
$("#frame-clusters").change((event) => {
|
|
relevantFrameCluster = $(event.target).val();
|
|
$frameList.empty();
|
|
relevantFrames[relevantFrameCluster].forEach(function (frame) {
|
|
$frameList.append(addFrameButton(frame))
|
|
});
|
|
});
|
|
|
|
$("#filter-options button").click((event) => {
|
|
const $select = ($(event.target).parent().siblings("select"));
|
|
const value = $select.val();
|
|
activateFilter(value);
|
|
});
|
|
|
|
$("#analyze-doc-button").click(() => {
|
|
const docVal = $documentList.val();
|
|
const eventId = docVal.split(":")[0];
|
|
const docId = docVal.split(":")[1];
|
|
processDocument(eventId, docId);
|
|
});
|
|
|
|
$("#frame-sample-list").change((event) => {
|
|
const newFrame = $(event.target).val();
|
|
$.get("role_labels", { "frame": newFrame }).done((data) => {
|
|
updateRoleSampleList(data);
|
|
});
|
|
});
|
|
|
|
$("#sample-frame-button").click(() => {
|
|
const frameVal = $("#frame-sample-list").val();
|
|
const constructionVal = $("#construction-sample-list").val();
|
|
const dependencyVal = $("#dep-sample-list").val();
|
|
const roleVal = $("#role-sample-list").val();
|
|
processSamples(frameVal, constructionVal, dependencyVal, roleVal);
|
|
});
|
|
|
|
const $frameFrequencies = $("#frame-frequencies");
|
|
const $frameTgtFrequencies = $("#frame-tgt-frequencies");
|
|
const $frameCatFrequencies = $("#frame-syncat-frequencies");
|
|
const $frameConstrFrequencies = $("#frame-cx-frequencies");
|
|
const $frameRootFrequencies = $("#frame-root-frequencies");
|
|
const $frameConstrRootFrequencies = $("#frame-cx-root-frequencies");
|
|
$frameFrequencies.click(() => showFrameFrequencies($frameFrequencies, relevantFrames[relevantFrameCluster], "*", false, false, false, false, 0));
|
|
$frameTgtFrequencies.click(() => showFrameFrequencies($frameTgtFrequencies, relevantFrames[relevantFrameCluster], "*", true, false, false, false, 0));
|
|
$frameCatFrequencies.click(() => showFrameFrequencies($frameCatFrequencies, relevantFrames[relevantFrameCluster], "*", false, true, false, false, 0));
|
|
$frameConstrFrequencies.click(() => showFrameFrequencies($frameConstrFrequencies, relevantFrames[relevantFrameCluster], "*", false, false, true, false, 0));
|
|
$frameRootFrequencies.click(() => showFrameFrequencies($frameRootFrequencies, relevantFrames[relevantFrameCluster], "*", false, false, false, true, 0));
|
|
$frameConstrRootFrequencies.click(() => showFrameFrequencies($frameConstrRootFrequencies, relevantFrames[relevantFrameCluster], "*", false, false, true, true, 0));
|
|
|
|
const $roleFrequencies = $("#role-frequencies");
|
|
const $roleExpFrequencies = $("#role-exp-frequencies");
|
|
const $rolePlExpFrequencies = $("#role-pl-exp-frequencies");
|
|
const $roleExpDepthFrequencies = $("#role-exp-depth-frequencies");
|
|
const $killingStatsTable = $("#constr-table-btn");
|
|
$roleFrequencies.click(() => showFrameFrequencies($roleFrequencies, "FROM_FORM", "FROM_FORM", false, false, false, false, 1));
|
|
$roleExpFrequencies.click(() => showFrameFrequencies($roleExpFrequencies, "FROM_FORM", "FROM_FORM", false, false, false, false, 2));
|
|
$rolePlExpFrequencies.click(() => showFrameFrequencies($rolePlExpFrequencies, "FROM_FORM", "FROM_FORM", false, false, false, false, 3));
|
|
$roleExpDepthFrequencies.click(() => showFrameFrequencies($roleExpDepthFrequencies, "FROM_FORM", "FROM_FORM", false, false, false, false, 4));
|
|
$killingStatsTable.click(() => showStatsTable("FROM_FORM"));
|
|
|
|
})
|
|
(jQuery);
|
|
|