Przeglądaj źródła

Initialise repository

primary
Mandragorat Wandystanu 2 lat temu
commit
61b4d67351
15 zmienionych plików z 639 dodań i 0 usunięć
  1. +13
    -0
      .htaccess
  2. +49
    -0
      byPeriodDataset.php
  3. +110
    -0
      byPeriodSlice.php
  4. +33
    -0
      common.php
  5. +106
    -0
      context.jsonld
  6. +2
    -0
      doc/Makefile
  7. +1
    -0
      doc/icon-author.svg
  8. +1
    -0
      doc/icon-date.svg
  9. BIN
      doc/icon-json-ld.png
  10. +87
    -0
      doc/index.html
  11. +67
    -0
      doc/index.markdown
  12. +15
    -0
      doc/style.css
  13. +104
    -0
      properties.jsonld
  14. +14
    -0
      sender.php
  15. +37
    -0
      structures.jsonld

+ 13
- 0
.htaccess Wyświetl plik

@@ -0,0 +1,13 @@
AddType application/ld+json .jsonld
Header set Access-Control-Allow-Origin *

RewriteEngine On
RewriteBase /statistics/

RewriteRule ^by-period/$ byPeriodDataset.php
RewriteRule ^by-period/(.*)/(.*) byPeriodSlice.php?date-start=$1&date-end=$2 [B]
RewriteRule ^sender/(.*) sender.php?hash=$1 [B]

RewriteRule ^$ doc/ [L,R=301]
RewriteRule ^(key|structure)/ structures.jsonld [L,R=303]
RewriteRule ^property/ properties.jsonld [L,R=303]

+ 49
- 0
byPeriodDataset.php Wyświetl plik

@@ -0,0 +1,49 @@
<?php
require_once 'common.php';

// load document with month statistics
try {
$document = load_remote_dom_document('https://wandystan.groups.io/g/wandystan/months');
} catch (Exception $e) {
exit_error($e->getMessage());
}

$xpath = new DOMXPath($document);

// find all months with non-zero number of posts
$months = [];
$rows = $xpath->query('//table/tbody/tr');

foreach ($rows as $row) {
$cols = $xpath->query('td', $row);
$year = $cols[0]->nodeValue;

for ($i = 1; $i < $cols->length; $i++) {
if (
$cols[$i]->firstChild->nodeType === XML_ELEMENT_NODE &&
$cols[$i]->firstChild->tagName === 'a'
) {
$months []= $year . '-' . str_pad($i, 2, '0', STR_PAD_LEFT);
}
}
}

// construct a linked data object corresponding to gathered data
$ld = [
'@context' => 'https://wandystan.eu/statistics/context.jsonld',
'@type' => 'DataSet',
'title' => [
'en' => 'Posts by date and sender',
'pl' => 'Wiadomości wg daty i wysyłającego'
],
'description' => [
'en' => 'Statistics of number of posts sent by a given sender on a given date, grouped by period in which they were sent.',
'pl' => 'Statystyki liczby wiadomości wysłanych przez danego wysyłającego w danym dniu, pogrupowane wg okresu w jakim zostały wysłane.'
],
'publisher' => 'https://wandystan.eu/',
'structure' => 'structure/by-period',
'slices' => array_map(function ($month) { return "by-period/$month-01/P1M"; }, $months)
];

header('Content-type: application/ld+json');
print json_encode($ld);

+ 110
- 0
byPeriodSlice.php Wyświetl plik

@@ -0,0 +1,110 @@
<?php
require_once 'common.php';

// get request data and validate it
$param_date_start = $_GET['date-start'];
$param_date_end = $_GET['date-end'];

if (! $param_date_start) {
exit_error('No start date specified.');
} elseif (! $param_date_end) {
exit_error('No end date specified.');
} elseif (! mb_ereg_match('^' . REGEXP_ISO8601_DATE_OR_INTERVAL . '$', $param_date_start)) {
exit_error('Invalid start date.');
} elseif (! mb_ereg_match('^' . REGEXP_ISO8601_DATE_OR_INTERVAL . '$', $param_date_end)) {
exit_error('Invalid end date.');
} elseif (starts_with($param_date_start, 'P') && starts_with($param_date_end, 'P')) {
exit_error('Both start and end dates cannot be periods.');
}

// create start and end date objects
$tz = new DateTimeZone('UTC');

try {
if (! starts_with($param_date_start, 'P'))
$date_start = new DateTimeImmutable($param_date_start, $tz);

if (! starts_with($param_date_end, 'P'))
$date_end = new DateTimeImmutable($param_date_end, $tz);

if (starts_with($param_date_start, 'P')) {
$interval = new DateInterval($param_date_start);
$date_start = $date_end->sub($interval);
}

if (starts_with($param_date_end, 'P')) {
$interval = new DateInterval($param_date_end);
$date_end = $date_start->add($interval);
}
} catch (Exception $e) {
exit_error('Invalid date specification: ' . $e->getMessage());
}

// get page with the search results for the given period
try {
$document = load_remote_dom_document('https://wandystan.groups.io/g/wandystan/search?p=Created,,,10000,2,0,0&d=6&startdate=' . $date_start->format('m/d/Y') . '&enddate=' . $date_end->modify('-1 second')->format('m/d/Y'));
} catch (Exception $e) {
exit_error($e->getMessage());
}

$xpath = new DOMXPath($document);

// find all messages in search results and summarise them
$messages = $xpath->query('//div[@id = "maincontent"]/table//td[span[@class = "subject"]]');
$summary = [];

foreach ($messages as $message) {
$sender = trim($xpath->evaluate('substring-before(substring-after(normalize-space(div[@class = "hidden-xs"]/following-sibling::text()[1]), "By "), " · #")', $message), '"');
$timestamp = $xpath->evaluate('substring-before(substring-after(.//script[@class = "timedisp"], "DisplayShortTime( "), " ,")', $message);

$date = new DateTime(null, $tz);
$date->setTimestamp($timestamp / 1000000000);

$key = serialize([
'date' => $date->format('Y-m-d'),
'sender' => $sender
]);
$summary[$key]++;
}

// construct a linked data object corresponding to gathered data
$ld = [
'@context' => 'https://wandystan.eu/statistics/context.jsonld',
'@type' => 'Slice',
'key' => 'key/by-period',
'period' => [
'@type' => 'Interval',
'hasBeginning' => [
'@type' => 'Instant',
'timestamp' => $date_start->format(DATE_ATOM)
],
'hasEnd' => [
'@type' => 'Instant',
'timestamp' => $date_end->format(DATE_ATOM)
]
]
];

$observations = [];
foreach ($summary as $key => $entry) {
$key = unserialize($key);
$nameHash = hash('fnv1a32', $key['sender']);
$observations []= [
'@id' => 'by-period/' . $key['date'] . '/P1D#' . $nameHash,
'@type' => 'Observation',
'dataset' => "by-period/",
'date' => $key['date'],
'sender' => [
'@id' => 'sender/' . $nameHash,
'@type' => 'Agent',
'name' => $key['sender'],
'name_fnv1a32sum' => $nameHash
],
'posts' => $entry
];
}

$ld['observations'] = $observations;

header('Content-type: application/ld+json');
print json_encode($ld);

+ 33
- 0
common.php Wyświetl plik

@@ -0,0 +1,33 @@
<?php
define('REGEXP_ISO8601_DATE', '\d{4}(-\d{2}|-?(\d{2}-?\d{2}|W\d{2}(-\d)?|\d{3}))');
define('REGEXP_ISO8601_INTERVAL', 'P(\d+Y(\d+M)?(\d+D)?|\d+M(\d+D)?|\d+[DW])');
define('REGEXP_ISO8601_DATE_OR_INTERVAL', '(' . REGEXP_ISO8601_DATE . '|' . REGEXP_ISO8601_INTERVAL . ')');

function exit_error($message) {
http_response_code(500);
header('Content-type: text/plain; charset=utf-8');
print 'ERROR: ' . $message . PHP_EOL;
exit;
}

function exit_error_handler($exception) {
exit_error($exception->getMessage());
}
set_exception_handler('exit_error_handler');

function load_remote_dom_document($uri) {
$content = file_get_contents($uri);
$document = new DOMDocument;

if ($content === false)
throw new Exception("Cannot load remote URI: $uri");

if (! $document->loadHTML($content, LIBXML_NOWARNING | LIBXML_NOERROR))
throw new Exception("Cannot parse remote URI as HTML: $uri");

return $document;
}

function starts_with($haystack, $needle) {
return substr_compare($haystack, $needle, 0, strlen($needle)) === 0;
}

+ 106
- 0
context.jsonld Wyświetl plik

@@ -0,0 +1,106 @@
{
"@context": {
"@base": "https://wandystan.eu/statistics/",
"@vocab": "http://purl.org/linked-data/cube#",
"dcterms": "http://purl.org/dc/terms/",
"foaf": "http://xmlns.com/foaf/0.1/",
"prop": "https://wandystan.eu/statistics/property/",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
"time": "http://www.w3.org/2006/time#",
"xsd": "http://www.w3.org/2001/XMLSchema#",

"Agent": "foaf:Agent",
"Instant": "time:Instant",
"Interval": "time:Interval",
"comment": {
"@id": "rdfs:comment",
"@container": "@language"
},
"componentAttachment": {
"@type": "@vocab"
},
"componentProperty": {
"@type": "@vocab"
},
"dataset": {
"@id": "dataSet",
"@type": "@id"
},
"date": {
"@id": "prop:date",
"@type": "xsd:date"
},
"description": {
"@id": "dcterms:description",
"@container": "@language"
},
"dimension": {
"@type": "@id"
},
"hasBeginning": {
"@id": "time:hasBeginning",
"@type": "@id"
},
"hasEnd": {
"@id": "time:hasEnd",
"@type": "@id"
},
"issued": {
"@id": "dcterms:issued",
"@type": "xsd:date"
},
"key": {
"@id": "sliceStructure",
"@type": "@id"
},
"label": {
"@id": "rdfs:label",
"@container": "@language"
},
"measure": {
"@type": "@id"
},
"name": "foaf:name",
"name_fnv1a32sum": {
"@id": "prop:name_fnv1a32sum",
"@type": "xsd:hexBinary"
},
"observations": "observation",
"period": {
"@id": "prop:period",
"@type": "prop:ISO8601-1"
},
"posts": {
"@id": "prop:posts",
"@type": "xsd:nonNegativeInteger"
},
"publisher": {
"@id": "dcterms:publisher",
"@type": "@id"
},
"sender": "prop:sender",
"sliceKey": {
"@type": "@id"
},
"slices": {
"@id": "slice",
"@type": "@id"
},
"structure": {
"@id": "structure",
"@type": "@id"
},
"subject": {
"@id": "dcterms:subject",
"@type": "@id"
},
"timestamp": {
"@id": "time:inXSDDateTimeStamp",
"@type": "xsd:dateTimeStamp"
},
"title": {
"@id": "dcterms:title",
"@container": "@language"
}
}
}

+ 2
- 0
doc/Makefile Wyświetl plik

@@ -0,0 +1,2 @@
index.html: index.markdown
pandoc -s -c style.css -M lang=pl --shift-heading-level-by 1 -o $@ $<

+ 1
- 0
doc/icon-author.svg Wyświetl plik

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/><path d="M0 0h24v24H0z" fill="none"/></svg>

+ 1
- 0
doc/icon-date.svg Wyświetl plik

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 0 24 24" width="24"><path d="M20 3h-1V1h-2v2H7V1H5v2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 18H4V8h16v13z"/><path d="M0 0h24v24H0z" fill="none"/></svg>

BIN
doc/icon-json-ld.png Wyświetl plik

Przed Po
Szerokość: 24  |  Wysokość: 24  |  Rozmiar: 491 B

+ 87
- 0
doc/index.html Wyświetl plik

@@ -0,0 +1,87 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="pl" xml:lang="pl">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<meta name="author" content="Polyna" />
<meta name="dcterms.date" content="2020-03-12" />
<title>Statystyki Listy Dyskusyjnej Mandragoratu Wandystanu</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
</style>
<link rel="stylesheet" href="style.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
</head>
<body>
<header id="title-block-header">
<h1 class="title">Statystyki Listy Dyskusyjnej Mandragoratu Wandystanu</h1>
<p class="author"><a href="/w/profile,196">Polyna</a></p>
<p class="date">2020-03-12</p>
</header>
<p>Statystyki <a href="https://wandystan.groups.io/g/wandystan/">Listy Dyskusyjnej Mandragoratu Wandystanu</a> są udostępniane jako dane połączone, przy użyciu słownictwa <a href="https://www.w3.org/TR/vocab-data-cube/" lang="en">RDF Data Cube</a>. Zapisane są one w postaci <a href="https://json-ld.org/" lang="en"><img src="icon-json-ld.png" style="vertical-align:bottom" /> JSON-LD</a> z <a href="../context.jsonld">odpowiednim kontekstem</a>, i mogą być przetwarzane zarówno za pomocą narzędzi przeznaczonych dla JSON-LD, jak i bezpośrednio, tak jak zwykłe dokumenty <a href="https://pl.wikipedia.org/wiki/JSON">JSON</a>.</p>
<p>Wszystkie odnośniki podane w postaci względnej posiadają podstawę <a href="../"><code>https://wandystan.eu/statistics/</code></a> – przykładowo, <code>"key": "key/by-period"</code> oznacza odnośnik do adresu <a href="../key/by-period"><code>https://wandystan.eu/statistics/key/by-period</code></a>.</p>
<p>Udostępniane są następujące rodzaje danych:</p>
<h2 id="wiadomości-wg-daty-i-wysyłającego">Wiadomości wg daty i wysyłającego</h2>
<p>Statystyki liczby wiadomości wysłanych przez danego wysyłającego w danym dniu, pogrupowane wg okresu w jakim zostały wysłane.</p>
<p>Ze względu na ogromną ilość udostępnianych danych, statystyki podzielone są na wiele dokumentów:</p>
<h3 id="zbiór-danych">Zbiór danych</h3>
<p>Dokument opisujący udostępniany zbiór danych wraz z odnośnikami do jego wycinków obejmujących okres pojedynczego miesiąca.</p>
<p>Adres dokumentu: <a href="../by-period/"><code>https://wandystan.eu/statistics/by-period/</code></a>.</p>
<p>Układ dokumentu:</p>
<ul>
<li><code>@type</code> – rodzaj obiektu: zbiór danych statystycznych.</li>
<li><code>title</code> – tytuł zbioru danych; obiekt którego kluczami są kody języków, a wartościami – tytuł zapisany w tym języku.</li>
<li><code>description</code> – opis zbioru danych; obiekt którego kluczami są kody języków, a wartościami – opis zapisany w tym języku.</li>
<li><code>publisher</code> – odnośnik do wydawcy dokumentu.</li>
<li><code>structure</code> – odnośnik do formalnego opisu układu danych statystycznych.</li>
<li><code>slices</code> – tablica odnośników do wycinków zbioru danych obejmujących okres pojedynczego miesiąca.</li>
</ul>
<h3 id="wycinki-danych">Wycinki danych</h3>
<p>Dokumenty zawierające dane statystyczne z wybranego okresu.</p>
<p>Adres dokumentu: <code>https://wandystan.eu/statistics/by-period/<var>⟨przedział⟩</var></code>; gdzie <code><var>⟨przedział⟩</var></code> to przedział dat w zapisie <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>, z podaną co najmniej jedną datą początkową lub/i jedną datą końcową, bez podanego czasu. Data początkowa jest zawsze uznawana jako zawierająca się w danym okresie, zaś data końcowa jako niezawierająca się. Przykłady poprawnie zapisanych przedziałów:</p>
<ul>
<li><code>2018-12-11/2019-04-21</code> – okres od 11 grudnia 2018 r. (włącznie) do 21 kwietnia 2019 r. (wyłącznie).</li>
<li><code>2020-02/P1M</code> – cały luty 2020 r.</li>
<li><code>P2M5D/2020-W03</code> – 2 miesiące i 5 dni poprzedzające trzeci tydzień 2020 r.</li>
</ul>
<p>Jeżeli w podanym przedziale napisano więcej niż 10 000 wiadomości, pod uwagę brane będą tylko te najpóźniej wysłane, więc nie jest zalecane podawanie zbyt długich przedziałów.</p>
<p>Układ dokumentu:</p>
<ul>
<li><code>@type</code> – rodzaj obiektu: wycinek zbioru danych statystycznych.</li>
<li><code>key</code> – odnośnik do klucza danych wycinka.</li>
<li><code>period</code> – okres, którego dotyczy dany wycinek.
<ul>
<li><code>@type</code> – rodzaj obiektu: przedział czasu.</li>
<li><code>hasBeginning</code> – początek przedziału czasu.
<ul>
<li><code>@type</code> – rodzaj obiektu: chwila w czasie.</li>
<li><code>timestamp</code> – znacznik czasu w zapisie <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>.</li>
</ul></li>
<li><code>hasEnd</code> – koniec przedziału czasu; obiekt o takim samym układzie co <code>hasBeginning</code>.</li>
</ul></li>
<li><code>observations</code> – poszczególne dane jednostkowe (spostrzeżenia) dotyczące danego okresu – tablica obiektów o następującej strukturze:
<ul>
<li><code>@id</code> – oznaczenie przypisane do danego spostrzeżenia.</li>
<li><code>@type</code> – rodzaj obiektu: dana jednostkowa (spostrzeżenie).</li>
<li><code>dataset</code> – odnośnik do zbioru, do którego przypisana jest dana.</li>
<li><code>date</code> – dzień w którym zostały wysłane wiadomości na listę, w zapisie <a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601</a>.</li>
<li><code>sender</code> – wysyłający wiadomości na listę w danym dniu.
<ul>
<li><code>@id</code> – oznaczenie przypisane do danego wysyłającego.</li>
<li><code>@type</code> – rodzaj obiektu: osoba bądź inna jednostka (organizacja, program samoczynnie wysyłający wiadomości, itp.) mająca możliwość podejmowania działań.</li>
<li><code>name</code> – nazwa tej osoby bądź jednostki.</li>
<li><code>name_fnv1a32sum</code> – skrót nazwy utworzony za pomocą <a href="https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">algorytmu FNV-1a</a> w odmianie 32-bitowej, zapisany w postaci szesnastkowej. Może być przekształcony w równoważny ciąg bitów, bądź 32-bitową liczbę bez znaku.</li>
</ul></li>
<li><code>posts</code> – ilość postów wysłanych na listę przez danego wysyłającego w danym dniu.</li>
</ul></li>
</ul>
</body>
</html>

+ 67
- 0
doc/index.markdown Wyświetl plik

@@ -0,0 +1,67 @@
% Statystyki Listy Dyskusyjnej Mandragoratu Wandystanu
% [Polyna](/w/profile,196)
% 2020-03-12

Statystyki [Listy Dyskusyjnej Mandragoratu Wandystanu](https://wandystan.groups.io/g/wandystan/) są udostępniane jako dane połączone, przy użyciu słownictwa [RDF Data Cube](https://www.w3.org/TR/vocab-data-cube/){lang=en}. Zapisane są one w postaci [![](icon-json-ld.png){style=vertical-align:bottom} JSON-LD](https://json-ld.org/){lang=en} z [odpowiednim kontekstem](../context.jsonld), i mogą być przetwarzane zarówno za pomocą narzędzi przeznaczonych dla JSON-LD, jak i bezpośrednio, tak jak zwykłe dokumenty [JSON](https://pl.wikipedia.org/wiki/JSON).

Wszystkie odnośniki podane w postaci względnej posiadają podstawę [`https://wandystan.eu/statistics/`](../) – przykładowo, `"key": "key/by-period"` oznacza odnośnik do adresu [`https://wandystan.eu/statistics/key/by-period`](../key/by-period).

Udostępniane są następujące rodzaje danych:

Wiadomości wg daty i wysyłającego
=================================

Statystyki liczby wiadomości wysłanych przez danego wysyłającego w danym dniu, pogrupowane wg okresu w jakim zostały wysłane.

Ze względu na ogromną ilość udostępnianych danych, statystyki podzielone są na wiele dokumentów:

Zbiór danych
------------

Dokument opisujący udostępniany zbiór danych wraz z odnośnikami do jego wycinków obejmujących okres pojedynczego miesiąca.

Adres dokumentu: [`https://wandystan.eu/statistics/by-period/`](../by-period/).

Układ dokumentu:

* `@type` – rodzaj obiektu: zbiór danych statystycznych.
* `title` – tytuł zbioru danych; obiekt którego kluczami są kody języków, a wartościami – tytuł zapisany w tym języku.
* `description` – opis zbioru danych; obiekt którego kluczami są kody języków, a wartościami – opis zapisany w tym języku.
* `publisher` – odnośnik do wydawcy dokumentu.
* `structure` – odnośnik do formalnego opisu układu danych statystycznych.
* `slices` – tablica odnośników do wycinków zbioru danych obejmujących okres pojedynczego miesiąca.

Wycinki danych
--------------

Dokumenty zawierające dane statystyczne z wybranego okresu.

Adres dokumentu: <code>https://wandystan.eu/statistics/by-period/<var>⟨przedział⟩</var></code>; gdzie <code><var>⟨przedział⟩</var></code> to przedział dat w zapisie [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601), z podaną co najmniej jedną datą początkową lub/i jedną datą końcową, bez podanego czasu. Data początkowa jest zawsze uznawana jako zawierająca się w danym okresie, zaś data końcowa jako niezawierająca się. Przykłady poprawnie zapisanych przedziałów:

* `2018-12-11/2019-04-21` – okres od 11 grudnia 2018 r. (włącznie) do 21 kwietnia 2019 r. (wyłącznie).
* `2020-02/P1M` – cały luty 2020 r.
* `P2M5D/2020-W03` – 2 miesiące i 5 dni poprzedzające trzeci tydzień 2020 r.

Jeżeli w podanym przedziale napisano więcej niż 10 000 wiadomości, pod uwagę brane będą tylko te najpóźniej wysłane, więc nie jest zalecane podawanie zbyt długich przedziałów.

Układ dokumentu:

* `@type` – rodzaj obiektu: wycinek zbioru danych statystycznych.
* `key` – odnośnik do klucza danych wycinka.
* `period` – okres, którego dotyczy dany wycinek.
* `@type` – rodzaj obiektu: przedział czasu.
* `hasBeginning` – początek przedziału czasu.
* `@type` – rodzaj obiektu: chwila w czasie.
* `timestamp` – znacznik czasu w zapisie [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).
* `hasEnd` – koniec przedziału czasu; obiekt o takim samym układzie co `hasBeginning`.
* `observations` – poszczególne dane jednostkowe (spostrzeżenia) dotyczące danego okresu – tablica obiektów o następującej strukturze:
* `@id` – oznaczenie przypisane do danego spostrzeżenia.
* `@type` – rodzaj obiektu: dana jednostkowa (spostrzeżenie).
* `dataset` – odnośnik do zbioru, do którego przypisana jest dana.
* `date` – dzień w którym zostały wysłane wiadomości na listę, w zapisie [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601).
* `sender` – wysyłający wiadomości na listę w danym dniu.
* `@id` – oznaczenie przypisane do danego wysyłającego.
* `@type` – rodzaj obiektu: osoba bądź inna jednostka (organizacja, program samoczynnie wysyłający wiadomości, itp.) mająca możliwość podejmowania działań.
* `name` – nazwa tej osoby bądź jednostki.
* `name_fnv1a32sum` – skrót nazwy utworzony za pomocą [algorytmu FNV-1a](https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function) w odmianie 32-bitowej, zapisany w postaci szesnastkowej. Może być przekształcony w równoważny ciąg bitów, bądź 32-bitową liczbę bez znaku.
* `posts` – ilość postów wysłanych na listę przez danego wysyłającego w danym dniu.

+ 15
- 0
doc/style.css Wyświetl plik

@@ -0,0 +1,15 @@
body {
width: 50em;
font-family: sans-serif;
line-height: 1.5;
}

.author {
padding-left: 2em;
background: url('icon-author.svg') left center / contain no-repeat;
}

.date {
padding-left: 2em;
background: url('icon-date.svg') left center / contain no-repeat;
}

+ 104
- 0
properties.jsonld Wyświetl plik

@@ -0,0 +1,104 @@
{
"@context": {
"@language": "en",
"dct": "http://purl.org/dc/terms/",
"owl": "http://www.w3.org/2002/07/owl#",
"prop": "https://wandystan.eu/statistics/property/",
"qb": "http://purl.org/linked-data/cube#",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
"sdmx-concept": "http://purl.org/linked-data/sdmx/2009/concept#",
"sdmx-dimension": "http://purl.org/linked-data/sdmx/2009/dimension#",
"sdmx-measure": "http://purl.org/linked-data/sdmx/2009/measure#",
"xsd": "http://www.w3.org/2001/XMLSchema#",

"Datatype": "rdfs:Datatype",
"DatatypeProperty": "owl:DatatypeProperty",
"DimensionProperty": "qb:DimensionProperty",
"FunctionalProperty": "owl:FunctionalProperty",
"DimensionProperty": "qb:DimensionProperty",
"MeasureProperty": "owl:MeasureProperty",
"Ontology": "owl:Ontology",
"Property": "rdf:Property",
"defines": {
"@reverse": "rdfs:isDefinedBy"
},
"comment": "rdfs:comment",
"concept": {
"@id": "qb:concept",
"@type": "@id"
},
"created": {
"@id": "dct:created",
"@type": "xsd:date"
},
"creator": {
"@id": "dct:creator",
"@type": "@id"
},
"description": "dct:description",
"isDefinedBy": {
"@id": "rdfs:isDefinedBy",
"@type": "@id"
},
"label": "rdfs:label",
"range": {
"@id": "rdfs:range",
"@type": "@id"
},
"seeAlso": {
"@id": "rdfs:seeAlso",
"@type": "@id"
},
"subPropertyOf": {
"@id": "rdfs:subPropertyOf",
"@type": "@id"
},
"title": "dct:title"
},
"@id": "prop:",
"@type": "Ontology",
"title": "Statistical ontology for on-line discussion spaces",
"created": "2020-03-06",
"creator": "https://wandystan.eu/B196",
"description": "This ontology defines properties used for publication of the statistical data about posts published in on-line discussion groups, lists and fora according to the RDF Data Cube model <https://www.w3.org/TR/vocab-data-cube/>.",
"defines": [
{
"@id": "prop:date",
"@type": ["Property", "DatatypeProperty", "DimensionProperty", "FunctionalProperty"],
"label": "Date",
"comment": "Date in which posts were was sent.",
"concept": "sdmx-concept:refPeriod",
"range": "xsd:date",
"subPropertyOf": ["sdmx-dimension:refPeriod", "dct:date"]
}, {
"@id": "prop:name_fnv1a32sum",
"@type": ["Property", "DatatypeProperty"],
"label": "32-bit FNV-1a sum of a name",
"comment": "A name for some thing hashed with the 32-bit flavor of the FNV-1a algorithm.",
"range": "xsd:hexBinary"
}, {
"@id": "prop:period",
"@type": ["Property", "DimensionProperty", "ObjectProperty"],
"label": "Period",
"comment": "Period in which posts were sent.",
"concept": "sdmx-concept:refPeriod",
"range": "http://www.w3.org/2006/time#Interval",
"subPropertyOf": ["sdmx-dimension:refPeriod", "dct:date"]
}, {
"@id": "prop:posts",
"@type": ["Property", "DatatypeProperty", "FunctionalProperty", "MeasureProperty"],
"label": "Number of posts",
"comment": "Number of posts sent in a certain period of time.",
"concept": "sdmx-concept:obsValue",
"range": "xsd:nonNegativeInteger",
"subPropertyOf": "sdmx-measure:obsValue"
}, {
"@id": "prop:sender",
"@type": ["Property", "DimensionProperty", "FunctionalProperty", "ObjectProperty"],
"label": "Sender",
"comment": "Sender of the post (not necessarily a person, may be e.g. an automated posting agent).",
"range": "dct:Agent"
}
]
}

+ 14
- 0
sender.php Wyświetl plik

@@ -0,0 +1,14 @@
<?php
require_once 'common.php';

$hash = $_GET['hash'];
if (! mb_ereg_match('^[0-9a-f]{8}$', $hash))
exit_error('Invalid hash.');

header('Content-type: application/ld+json');
print json_encode([
'@context' => 'https://wandystan.eu/statistics/context.jsonld',
'@id' => 'sender/' . $hash,
'@type' => 'Agent',
'name_fnv1a32sum' => $hash
]);

+ 37
- 0
structures.jsonld Wyświetl plik

@@ -0,0 +1,37 @@
{
"@context": "https://wandystan.eu/statistics/context.jsonld",
"@graph": [
{
"@id": "structure/by-period",
"@type": "DataStructureDefinition",
"component": [
{
"measure": "prop:posts"
}, {
"dimension": "prop:sender",
"order": 1
}, {
"dimension": "prop:date",
"order": 2
}, {
"componentAttachment": "Slice",
"dimension": "prop:period",
"order": 3
}
],
"sliceKey": "key/by-period"
}, {
"@id": "key/by-period",
"@type": "SliceKey",
"label": {
"en": "time slice",
"pl": "wycinek czasu"
},
"comment": {
"en": "Slice grouping data from a given time period.",
"pl": "Wycinek grupujący dane z danego okresu czasowego."
},
"componentProperty": "prop:period"
}
]
}

Ładowanie…
Anuluj
Zapisz