Wired filter
Этот коммит содержится в:
родитель
c9a418f0b7
Коммит
64e741ce69
@ -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
Обычный файл
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);
|
||||
Загрузка…
x
Ссылка в новой задаче
Block a user