webservices-app/src/routes/+page.svelte

451 lines
15 KiB
Svelte
Raw Normal View History

<script>
// get the data from the JSON Webservices feed in +page.js
export let data;
const { webservices } = data;
import { references } from '$lib/references.js';
import { colours } from '$lib/colours.js';
// the filters
import { setFilter, getFilter } from '$lib/components/filter.js';
import Filterable from '$lib/components/Filterable.svelte';
// collapsible sections
2024-09-23 09:34:14 +12:00
import CollapsibleSection from '$lib/components/CollapsibleSection.svelte';
import Tile from '$lib/components/Tile.svelte';
//console.log('colours: ', colours);
//
// digest and return useful info from the Webservices JSON feed
function processData(webservices) {
let stats = { 'instances': 0, 'technologies': 0, 'licenses': 0,'weak': 0, 'copyleft': 0 };
let all = []; // all services
let current = []; // those with active instances
let future = []; // those that are planned
// properties of technologies
let category_list = [];
let analogue_list = [];
let license_list = [];
// properties of instances
let status_list = [];
let affiliate_list = [];
let host_list = [];
// actual objects
let hosts = [];
let affiliates = [];
//
// just a trivial reassignment
//let hosts = webservices.hosts;
//
// pull out relevant info in useful chunks.
for (let key in webservices.technologies) {
let tech = webservices.technologies[key];
tech['name'] = key;
console.log('tech '+key+' ('+tech.categories.length+'):', tech.categories);
if (tech.hasOwnProperty('categories') && tech.categories.constructor === Array) {
//console.log('tech '+key+' ('+tech.categories.length+'):', tech.categories);
tech.categories.forEach(function (category, index) {
if (category_list.hasOwnProperty(category)) category_list[category]++;
else category_list[category] = 1;
});
}
if (tech.hasOwnProperty('analogues') && tech.analogues.constructor === Array) {
tech.analogues.forEach(function (analogue, index) {
if (analogue_list.hasOwnProperty(analogue)) analogue_list[analogue]++;
else analogue_list[analogue] = 1;
});
}
if (tech.hasOwnProperty('license')) {
let license = tech.license;
if (license_list.hasOwnProperty(license)) license_list[license]++;
else license_list[license] = 1;
}
if (hasInstances(tech)) {
current.push(tech);
//console.log(tech.name + ': ' + tech.instances.length + ' instances...');
tech.instances.forEach(function (instance, i) {
stats.instances++;
if (instance.hasOwnProperty('status')) {
let tag = instance.status;
if (status_list.hasOwnProperty(tag)) status_list[tag]++;
else status_list[tag] = 1;
}
if (instance.hasOwnProperty('affiliation')) {
let tag = instance.affiliation;
2024-09-05 11:07:29 +12:00
if (affiliate_list.hasOwnProperty(tag)) affiliate_list[tag]++;
else affiliate_list[tag] = 1;
}
if (instance.hasOwnProperty('host')) {
let tag = instance.host;
if (host_list.hasOwnProperty(tag)) host_list[tag]++;
else host_list[tag] = 1;
}
});
} else {
future[key] = tech;
}
all[key] = tech;
}
for (let key in webservices.hosts) {
//console.log('key: ', key);
let host = webservices.hosts[key];
host['name'] = key;
//console.log('host: ', host);
if (
host.hasOwnProperty('domain') &&
!(host.hasOwnProperty('status') && host.status == 'retired')
) {
hosts[key] = host;
}
}
for (let key in webservices.affiliates) {
let affiliate = webservices.affiliates[key];
affiliates[key] = affiliate;
}
// assign stats based on gathered data...
stats.current = current.length;
stats.future = future.length;
console.log('license_list: ', license_list);
stats.licenses = license_list.length;
return {
stats: stats,
all_services: all,
active_services: current,
candidate_services: future,
tech_lists: {
category_list: category_list,
analogue_list: analogue_list,
license_list: license_list
},
instance_lists: {
status_list: status_list,
affiliate_list: affiliate_list,
host_list: host_list
},
hosts: hosts,
affiliates: affiliates
};
}
// return an object with ob's keys ordered alphabetically, with an id from an object
function getSortedFilter(ob) {
let keys = [];
let i = 0;
let key_array = [];
// first create a flat array.
for (let key in ob) {
keys.push(key);
}
// sort the array alphabetically
keys.sort();
// then create a dictionary of them
keys.forEach(function(name) {
key_array.push({ "id": i++, "name": name, "active": true });
});
return key_array;
}
// return true if a tech object includes valid instances
function hasInstances(tech) {
if (
tech.hasOwnProperty('instances') &&
tech.instances.constructor === Array &&
tech.instances.length
) return true;
return false;
}
// get intersection: ref https://bobbyhadz.com/blog/javascript-get-intersection-of-two-arrays
function getIntersection(a, b) {
const set1 = new Set(a);
const set2 = new Set(b);
const intersection = [...set1].filter((element) => set2.has(element));
return intersection;
}
// assign colours from a set of differentiated colours to a list of tags...
// this one is for 'hosts'
//
// host_list is in the form of
function hostColours(host_list, colours, hosts) {
let host_array = {};
let i = 0;
console.log('hosts:', hosts);
console.log('host_list:', host_list);
for (let index in host_list) {
console.log(host_list[index]);
let hostname = host_list[index].name;
if (hosts[hostname].hasOwnProperty('domain') && hosts[hostname].hasOwnProperty('affiliation')) {
host_array[hostname] = {
colour: colours[i++],
domain: hosts[hostname].domain,
affiliation: hosts[hostname].affiliation
};
}
}
return host_array;
}
// assign colours from a set of differentiated colours to a list of tags...
// this one is for 'affiliates'
function affiliateColours(affiliate_list, colours, affiliates) {
let affiliate_result = {};
let i = 0;
//console.log('affiliates:', affiliates);
console.log('affiliate_list:', affiliate_list);
for (let index in affiliate_list) {
//console.log('affiliate:', affiliate);
let affiliate = affiliate_list[index].name;
if (
affiliates[affiliate].hasOwnProperty('name') &&
affiliates[affiliate].hasOwnProperty('website')
) {
affiliate_result[affiliate] = {
colour: colours[i++],
name: affiliates[affiliate].name,
website: affiliates[affiliate].website
};
}
}
return affiliate_result;
}
// sort technologies alphabetically by name
function sortTechnologies(technologies) {
return technologies.sort((a, b) => a.name.localeCompare(b.name));
}
// generic function to find the intersection of two arrays
// approach ref: https://bobbyhadz.com/blog/javascript-get-intersection-of-two-arrays
function inCommon(a, b) {
const set1 = new Set(a);
const set2 = new Set(b);
const intersection = [...set1].filter((element) => set2.has(element));
//console.log('intersection = ', intersection);
return intersection;
}
// converts a filter with id, name, and active fields into an array of names
function flattenFilter(filter) {
let flat = [];
if (filter.constructor === Array) {
filter.forEach(function(e) { if (e.active) flat.push(e.name) });
}
return flat;
}
// filter technologies based on a list of Categories
function filterTechnologiesByCategoryList(technologies, list) {
//console.log('looking for tech in categories: ', list);
const included = [];
technologies.forEach(function (tech) {
if (hasInstances(tech)) {
const intersection = inCommon(tech.categories, list);
console.log('for tech: ', tech.name);
console.log('categories: ', tech.categories);
console.log('list: ', list);
console.log('intersection: ', intersection);
if (intersection && intersection.constructor === Array) {
//console.log('found intersection!', intersection.constructor);
console.log('intersection length: ', intersection.length);
if (intersection.length > 0) {
//console.log('including tech: ', tech);
included.push(tech);
}
} else {
console.log('intersection not array');
}
}
});
console.log('found ' + included.length + ' technologies.');
return included;
}
const results = processData(webservices);
//const technologies = webservices.technologies;
//console.log(technologies);
// set up filters for each type
setFilter('categories');
setFilter('analogues');
setFilter('licenses');
setFilter('statuses');
setFilter('affiliates');
setFilter('hosts');
// define the handle for each writable
const categoryFilter = getFilter('categories');
const analogueFilter = getFilter('analogues');
const licenseFilter = getFilter('licenses');
const statusFilter = getFilter('statuses');
const affiliateFilter = getFilter('affiliates');
const hostFilter = getFilter('hosts');
// populate the writable with actual data
$categoryFilter = getSortedFilter(results.tech_lists.category_list);
$analogueFilter = getSortedFilter(results.tech_lists.analogue_list);
$licenseFilter = getSortedFilter(results.tech_lists.license_list);
$statusFilter = getSortedFilter(results.instance_lists.status_list);
$affiliateFilter = getSortedFilter(results.instance_lists.affiliate_list);
$hostFilter = getSortedFilter(results.instance_lists.host_list);
const hosts = results.hosts;
console.log('host_data: ', results.hosts);
const affiliates = results.affiliates;
console.log('affiliate_data: ', results.affiliates);
const host_colours = hostColours($hostFilter, colours, hosts);
console.log('host_colours:', host_colours);
colours.sort();
const affiliate_colours = affiliateColours($affiliateFilter, colours, affiliates);
console.log('affiliate_colours:',affiliate_colours);
let filteredTechnologies;
let technologies;
// reactive stuff...
$: {
//console.log('about to filterTechnologiesByCategoryList');
filteredTechnologies = filterTechnologiesByCategoryList(results.active_services, flattenFilter($categoryFilter));
//filteredTechnologies = filterTechnologiesByCategoryList(results.all_services, flattenFilter($categoryFilter));
const technologies = sortTechnologies(filteredTechnologies);
}
/*function flipOn(list, id) {
console.log('list: ' + list + ', id: ' + id);
return true;
}*/
</script>
<div class="webservices">
<div class="header">
<div class="introduction">
<h1>Web Services</h1>
<p>This sites exists to provide an 'always-up-to-date', in-depth description of the <a
href="https://tech.oeru.org/foss-libresoftware-its-about-clarity-and-values"
title="What do I mean by 'libre' software?">libre software</a> web services <a href="https://davelane.nz" title="Who is this Dave Lane character?">I</a> have set
up and maintain.
</p>
</div>
<div class="summary">
<h2>Statistics</h2>
<ul>
<li>Number of individual services: {results.stats.instances}</li>
<li>Number of individual technologies: {results.stats.current}</li>
<li>Number of new technologies to be added: {results.stats.future}</li>
<li>Number of libre licenses used by these technologies: {results.stats.licenses}</li>
<li>&nbsp;&nbsp;'weak' corporate-exploitation-friendly open source licenses : {results.stats.weak}</li>
<li>&nbsp;&nbsp;'strong' user-protecting copyleft licenses: {results.stats.copyleft}</li>
<li>Total number of services: {results.stats.instances}</li>
</ul>
</div>
</div>
2024-09-23 09:34:14 +12:00
<CollapsibleSection headerText={'Filters'}>
<div class="filters">
<CollapsibleSection headerText={'Software Catergory Filter'}>
2024-09-23 09:34:14 +12:00
<div class="filter-group categories">
<Filterable filterValues={categoryFilter}
histogram={results.tech_lists.category_list} context={'categories'}>
<h2 slot="h2">Categories</h2>
</Filterable>
</div>
</CollapsibleSection>
2024-09-23 09:34:14 +12:00
<CollapsibleSection headerText={'Software Analogue Filter'}>
<div class="filter-group analogues">
<Filterable filterValues={analogueFilter}
histogram={results.tech_lists.analogue_list} context={'analogues'}>
<h2 slot="h2">Analogues</h2>
</Filterable>
</div>
</CollapsibleSection>
2024-09-23 09:34:14 +12:00
<CollapsibleSection headerText={'License Filter'}>
<div class="filter-group licenses">
<Filterable filterValues={licenseFilter}
histogram={results.tech_lists.license_list} context={'licenses'}>
<h2 slot="h2">Open Source & Copyleft Licenses</h2>
</Filterable>
</div>
</CollapsibleSection>
2024-09-23 09:34:14 +12:00
<CollapsibleSection headerText={'Service Status Filter'}>
<div class="filter-group statuses">
<Filterable filterValues={statusFilter}
histogram={results.instance_lists.status_list} context={'statuses'}>
<h2 slot="h2">Statuses</h2>
</Filterable>
</div>
</CollapsibleSection>
2024-09-23 09:34:14 +12:00
<CollapsibleSection headerText={'Service Affiliates Filter'}>
<div class="filter-group affilates">
<Filterable filterValues={affiliateFilter}
histogram={results.instance_lists.affiliate_list} context={'affiliates'}>
<h2 slot="h2">Affiliates</h2>
</Filterable>
</div>
</CollapsibleSection>
2024-09-23 09:34:14 +12:00
<CollapsibleSection headerText={'Service Host filter'}>
<div class="filter-group hosts">
<Filterable filterValues={hostFilter}
histogram={results.instance_lists.host_list} context={'hosts'}>
<h2 slot="h2">Hosts</h2>
</Filterable>
</div>
</CollapsibleSection>
</div>
</CollapsibleSection>
<div class="tiles">
{#each filteredTechnologies as technology}
<Tile technology={technology} affiliate_colours={affiliate_colours} />
{/each}
</div>
</div>
<style>
.webservices {
display: grid;
margin: 0 auto 3em auto;
padding: 0;
position: relative;
}
.introduction {
width: 50%;
}
.summary {
border-radius: 15px;
/*position: absolute;
right: 20px;*/
background-color: #ccc;
/*display: block;*/
width: 50%;
border: 2px #aaa solid;
margin: 0;
padding: 0 20px;
}
2024-09-23 09:34:14 +12:00
/* .filters { margin-bottom: 1em;}
.filters .tags {
margin: 1em 0;
display: block;
2024-09-23 09:34:14 +12:00
}*/
.tiles {
/*display: grid;
grid-gap: 15px;
grid-template-columns: repeat(auto-fit, minmax(270px, 1fr));*/
/*grid-template-columns: repeat(auto-fit);*/
}
</style>