Этот коммит содержится в:
Maarten 2024-09-27 10:57:22 +02:00
родитель c9a418f0b7
Коммит 64e741ce69
6 изменённых файлов: 182 добавлений и 115 удалений

Просмотреть файл

@ -1,100 +1,41 @@
<script>
import Dropdown from '$lib/components/Dropdown.svelte'
import {
platformFilter,
} from '../../stores/filters';
let items = [
{
"id": "Facebook",
"name": "Facebook",
"selected": true,
"count": 34,
"liveCount": 34
},
{
"id": "Instagram",
"name": "Instagram",
"selected": true,
"count": 21,
"liveCount": 21
},
{
"id": "Twitter",
"name": "Twitter",
"selected": true,
"count": 34,
"liveCount": 34
},
{
"id": "YouTube",
"name": "YouTube",
"selected": true,
"count": 16,
"liveCount": 16
},
{
"id": "unspecified",
"name": "unspecified",
"selected": true,
"count": 33,
"liveCount": 33
},
{
"id": "Reddit",
"name": "Reddit",
"selected": true,
"count": 13,
"liveCount": 13
},
{
"id": "WhatsApp",
"name": "WhatsApp",
"selected": true,
"count": 2,
"liveCount": 2
},
{
"id": "SMS",
"name": "SMS",
"selected": true,
"count": 2,
"liveCount": 2
},
{
"id": "Telegram",
"name": "Telegram",
"selected": true,
"count": 1,
"liveCount": 1
},
{
"id": "Quora",
"name": "Quora",
"selected": true,
"count": 1,
"liveCount": 1
},
{
"id": "Forum Board",
"name": "Forum Board",
"selected": true,
"count": 1,
"liveCount": 1
},
{
"id": "Parler",
"name": "Parler",
"selected": true,
"count": 1,
"liveCount": 1
},
{
"id": "Gab",
"name": "Gab",
"selected": true,
"count": 1,
"liveCount": 1
export let cases;
function handleButtonClick() {
selectAllFilters();
/*contextData.unselectAll();
$highlightPolarization = false;
$highlightCib = false;
if ($originalTimeDomain) {
$timeScale.domain($originalTimeDomain);
$timeScale = $timeScale;
$originalTimeDomain = null;
}*/
}
]
$: console.log($platformFilter)
function addCount(filter, property, cases) {
return filter.map((d) => ({
...d,
count: cases.map((d) => d[property]).flat().filter((a) => a === d.id).length,
liveCount: cases.filter((d) => d.show).map((d) => d[property]).flat().filter((a) => a === d.id).length
}));
}
$: console.log(addCount($platformFilter, 'platform', cases))
</script>
<Dropdown items={items} label='Platform'></Dropdown>
{#if cases}
<Dropdown items={addCount($platformFilter, 'platform', cases)}
label="Platform"
on:itemsAdded={(e) => platformFilter.select(e.detail)}
on:itemsRemoved={(e) => platformFilter.unselect(e.detail)}>
</Dropdown>
<!--Dropdown items={items} label='Platform'></Dropdown-->
{/if}

Просмотреть файл

@ -12,6 +12,8 @@
export let hideOneHitWonders = false;
export let superior = false;
//$: console.log(items)
const dispatch = createEventDispatcher();
let elem;

Просмотреть файл

@ -38,6 +38,7 @@
<text x={xScale(tick)} y={32} text-anchor={'middle'}>{utcFormat('%b %d')(tick)}</text>
{/each}
{#each cases as attrCase}
{#if attrCase.show}
<a href={'#case-' + attrCase.attribution_id}>
<circle
cx={xScale(new Date(attrCase.attribution_date))}
@ -48,6 +49,7 @@
stroke-width={2}
></circle>
</a>
{/if}
{/each}
</g>
{/if}

Просмотреть файл

@ -19,3 +19,16 @@ export const sortConsistently = (itemA, itemB, property, key) => {
}
return r;
};
// split string into array
export const splitString = (s) => {
if (s === '' || s === ',') return ['unspecified'];
return(s
.split(';')
.map((d) => d.trim())
.filter((d) => d !== ''));
};
// check if there's overlap between array and filter
export const haveOverlap = (filter, arr) =>
filter.filter((d) => d.selected).map((d) => d.id).some((item) => arr.includes(item));

Просмотреть файл

@ -8,6 +8,11 @@
import Timeline from '$lib/components/Timeline.svelte';
//import Select from 'svelte-select';
import Controls from '$lib/components/Controls.svelte';
import { splitString, haveOverlap } from '$lib/utils/misc'
import {
platformFilter,
} from '../stores/filters';
let cases = [];
@ -15,16 +20,20 @@
//const response = await csv(`https://fiat-2024-processed-data.s3.us-west-2.amazonaws.com/Demo_Attribution_Data.csv`);
const response = await csv(`${base}/Demo_Attribution_Data.csv`);
cases = response;
cases.forEach(d => {
d.platform = splitString(d.platform)
d.show = false
})
platformFilter.init(cases, 'platform');
});
$: actorNations =
cases.length > 0 ? [...new Set(cases.map((d) => d.actor_nation.split(', ')).flat())] : [];
$: actorNationsUnique = [...new Set(actorNations)];
let selectedActorNations = null;
$: filteredCases = selectedActorNations
? cases.filter((d) => d.actor_nation.includes(selectedActorNations[0].value))
: cases;
$: if (cases) {
cases = cases.map(d => ({
...d,
show: haveOverlap($platformFilter, d.platform)
}))
}
</script>
<section class="section">
@ -39,30 +48,24 @@
</section>
<section class="section">
<Controls></Controls>
<!--div class="select-container">
<Select
items={actorNationsUnique}
multiple={true}
bind:value={selectedActorNations}
placeholder={'Select 1 or more actor countries'}
></Select>
</div-->
<Controls {cases}></Controls>
</section>
<section class="section">
<div>
<Timeline cases={filteredCases}></Timeline>
<Timeline {cases}></Timeline>
</div>
</section>
<section class="section">
<div class="container">
<div class="grid is-col-min-12">
{#each filteredCases as attrCase}
{#each cases as attrCase}
{#if attrCase.show}
<div class="cell">
<CaseCard cardData={attrCase}></CaseCard>
</div>
{/if}
{/each}
</div>
</div>
@ -70,7 +73,7 @@
<section class="section">
<div class="container">
<CaseTable cases={filteredCases}></CaseTable>
<CaseTable {cases}></CaseTable>
</div>
</section>

106
src/stores/filters.js Обычный файл
Просмотреть файл

@ -0,0 +1,106 @@
import { writable } from 'svelte/store';
//import { uniq } from 'lodash';
function createRangeFilter() {
const { subscribe, set, update } = writable([0, 0]);
return {
subscribe,
set,
setMin: (value) => update((f) => f[0] = value),
setMax: (value) => update((f) => f[1] = value)
};
}
function createInclusiveFilter() {
const { subscribe, set, update } = writable([]);
const select = (id) => update((f) => f.map((d) => ({...d, selected: [id].flat().includes(d.id) ? true : d.selected})));
const unselectAll = () => update((f) => f.map((d) => ({...d, selected: false})));
const applyBoolArray = (arr) => {
const tmpArr = [...arr].reverse();
update((f) => f.reverse().map((d, i) => ({...d, selected: tmpArr[i] !== undefined ? tmpArr[i] : false})).reverse());
};
return {
subscribe,
set: (value) => set(value),
//init: (values, id) => set(uniq(values.map((d) => d[id]).flat()).map((id) => ({id, name: id, selected: true}))),
init: (values, id) => set([...new Set(values.map((d) => d[id]).flat())].map((id) => ({id, name: id, selected: true}))),
select,
selectOne: (id) => {
unselectAll();
select(id)
},
selectAll: () => update((f) => f.map((d) => ({...d, selected: true}))),
unselect: (id) => update((f) => f.map((d) => ({...d, selected: [id].flat().includes(d.id) ? false : d.selected}))),
unselectAll,
applyBoolArray
};
}
function createTextSearchFilter() {
const { subscribe, set } = writable('');
return {
subscribe,
set,
reset: () => set('')
};
}
export const disinformantNationFilter = createInclusiveFilter();
export const platformFilter = createInclusiveFilter();
export const methodFilter = createInclusiveFilter();
export const sourceFilter = createInclusiveFilter();
export const sourceCategoryFilter = createInclusiveFilter();
export const tagFilter = createInclusiveFilter();
export const attributionScoreFilter = createRangeFilter();
export const attributionScoreDef = [0, 18];
export const polarizationFilter = createRangeFilter();
export const polarizationDef = [-2, 2];
export const unselectAllFilters = (disinformantNation = true) => {
if (disinformantNation) disinformantNationFilter.unselectAll();
platformFilter.unselectAll();
methodFilter.unselectAll();
sourceFilter.unselectAll();
sourceCategoryFilter.unselectAll();
tagFilter.unselectAll();
attributionScoreFilter.set(attributionScoreDef);
polarizationFilter.set(polarizationDef);
};
export const selectAllFilters = (disinformantNation = true) => {
if (disinformantNation) disinformantNationFilter.selectAll();
platformFilter.selectAll();
methodFilter.selectAll();
sourceFilter.selectAll();
sourceCategoryFilter.selectAll();
tagFilter.selectAll();
attributionScoreFilter.set(attributionScoreDef);
polarizationFilter.set(polarizationDef);
textSearchFilter.reset();
caseIdFilter.set(undefined);
};
export const textSearchFilter = createTextSearchFilter();
export const contextData = createInclusiveFilter();
export const brushed = writable(false);
export const originalTimeDomain = writable(null);
export const caseIdFilter = writable();
export const highlightPolarization = writable(false);
export const highlightCib = writable(false);