mirror of
https://codeberg.org/vlw/vlw.se.git
synced 2025-09-13 21:13:40 +02:00
feat: add coffee stats endpoints and counter to about page (#28)
This PR refactors some texts on the about page (again), and also a adds two new endpoints for a database table that I have now made public that tracks the coffee cups I've had. The endpoint itself is not public now but I might make a page (something like `/about/coffee`) that presents it in a not-ugly way. Reviewed-on: https://codeberg.org/vlw/vlw.se/pulls/28
This commit is contained in:
parent
f76962e2ac
commit
f4279c0343
12 changed files with 281 additions and 18 deletions
38
endpoints/coffee/GET.php
Normal file
38
endpoints/coffee/GET.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
use vlw\MySQL\Order;
|
||||
use Reflect\{Response, Path, Call};
|
||||
use ReflectRules\{Ruleset, Rules, Type};
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
use VLW\Database\Database;
|
||||
use VLW\Database\Tables\Coffee\CoffeeTable;
|
||||
|
||||
require_once Path::root("src/API/Endpoints.php");
|
||||
require_once Path::root("src/Database/Database.php");
|
||||
require_once Path::root("src/Database/Tables/Coffee/Coffee.php");
|
||||
|
||||
class GET_Coffee extends Database {
|
||||
protected readonly Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(CoffeeTable::ID->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::SIZE_UINT32)
|
||||
]);
|
||||
|
||||
$this->ruleset->validate_or_exit();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
return $this->list(CoffeeTable::NAME, CoffeeTable::values(), [
|
||||
CoffeeTable::ID->value => Order::DESC
|
||||
]);
|
||||
}
|
||||
}
|
38
endpoints/coffee/POST.php
Normal file
38
endpoints/coffee/POST.php
Normal file
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
use Reflect\{Response, Path, Call};
|
||||
use ReflectRules\{Ruleset, Rules, Type};
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
use VLW\Database\Database;
|
||||
use VLW\Database\Tables\Coffee\CoffeeTable;
|
||||
|
||||
require_once Path::root("src/API/Endpoints.php");
|
||||
require_once Path::root("src/Database/Database.php");
|
||||
require_once Path::root("src/Database/Tables/Coffee/Coffee.php");
|
||||
|
||||
class POST_Coffee extends Database {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->POST([
|
||||
(new Rules(CoffeeTable::ID->value))
|
||||
->type(Type::NUMBER)
|
||||
->min(1)
|
||||
->max(parent::SIZE_UINT32)
|
||||
->default(time()),
|
||||
]);
|
||||
|
||||
$this->ruleset->validate_or_exit();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
return $this->db->for(CoffeeTable::NAME)->insert($_POST) === true
|
||||
? new Response(null, 201)
|
||||
: new Response("Database error", 500);
|
||||
}
|
||||
}
|
42
endpoints/coffee/stats/GET.php
Normal file
42
endpoints/coffee/stats/GET.php
Normal file
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
use vlw\MySQL\Order;
|
||||
use Reflect\{Response, Path, Call};
|
||||
use ReflectRules\{Ruleset, Rules, Type};
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
use VLW\Database\Database;
|
||||
use const VLW\COFFEE_STATS_UPDATE_PARAM;
|
||||
use VLW\Database\Tables\Coffee\StatsTable;
|
||||
|
||||
require_once Path::root("src/Consts.php");
|
||||
require_once Path::root("src/API/Endpoints.php");
|
||||
require_once Path::root("src/Database/Database.php");
|
||||
require_once Path::root("src/Database/Tables/Coffee/Stats.php");
|
||||
|
||||
class GET_CoffeeStats extends Database {
|
||||
protected readonly Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
|
||||
$this->ruleset->GET([
|
||||
(new Rules(COFFEE_STATS_UPDATE_PARAM))
|
||||
->type(Type::BOOLEAN)
|
||||
->default(false)
|
||||
]);
|
||||
|
||||
$this->ruleset->validate_or_exit();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
// Freshen cache if update flag is set
|
||||
if ($_GET[COFFEE_STATS_UPDATE_PARAM]) {
|
||||
(new Call(Endpoints::COFFEE_STATS->value))->post();
|
||||
}
|
||||
|
||||
return $this->list(StatsTable::NAME, StatsTable::values());
|
||||
}
|
||||
}
|
35
endpoints/coffee/stats/POST.php
Normal file
35
endpoints/coffee/stats/POST.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
use Reflect\{Response, Path, Call};
|
||||
use ReflectRules\{Ruleset, Rules, Type};
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
use VLW\Database\Database;
|
||||
use VLW\Database\Tables\Coffee\{CoffeeTable, StatsTable};
|
||||
|
||||
require_once Path::root("src/API/Endpoints.php");
|
||||
require_once Path::root("src/Database/Database.php");
|
||||
require_once Path::root("src/Database/Tables/Coffee/Stats.php");
|
||||
require_once Path::root("src/Database/Tables/Coffee/Coffee.php");
|
||||
|
||||
class POST_CoffeeStats extends Database {
|
||||
protected Ruleset $ruleset;
|
||||
|
||||
public function __construct() {
|
||||
$this->ruleset = new Ruleset(strict: true);
|
||||
$this->ruleset->validate_or_exit();
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function main(): Response {
|
||||
$truncate = $this->db->execute_query("DELETE FROM `" . StatsTable::NAME . "`");
|
||||
|
||||
// Add a dummy row to run the MariaDB INSERT AFTER Trigger on the coffee database table
|
||||
$insert = $this->db->for(CoffeeTable::NAME)->insert([CoffeeTable::ID->value => 0]);
|
||||
// Remove the dummy row
|
||||
$remove = $this->db->for(CoffeeTable::NAME)->where([CoffeeTable::ID->value => 0])->delete();
|
||||
|
||||
return $truncate && $insert && $remove ? new Response() : new Response("Error", 500);
|
||||
}
|
||||
}
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use VLW\Database\Models\Coffee\Stats;
|
||||
use VLW\Database\Models\About\Language;
|
||||
use const VLW\{
|
||||
FORGEJO_HREF,
|
||||
|
@ -8,6 +9,7 @@
|
|||
};
|
||||
|
||||
require_once VV::root("src/Consts.php");
|
||||
require_once VV::root("src/Database/Models/Coffee/Stats.php");
|
||||
require_once VV::root("src/Database/Models/About/Language.php");
|
||||
|
||||
$languages = new class extends Language {
|
||||
|
@ -33,7 +35,19 @@
|
|||
|
||||
return round($format) . " " . FORGEJO_SI_BYTE_MULTIPLE[$factor];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
$coffee = new class extends Stats {
|
||||
public function week_average_string(): string {
|
||||
$diff = $this->week() - $this->week_average();
|
||||
|
||||
return match (true) {
|
||||
$diff < 1 => "less than",
|
||||
$diff === 1 => "the same as",
|
||||
$diff > 1 => "more than"
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
?>
|
||||
<style><?= VV::css("public/assets/css/pages/about") ?></style>
|
||||
|
@ -43,8 +57,9 @@
|
|||
</section>
|
||||
<hr aria-hidden="true">
|
||||
<section class="about">
|
||||
<p>I​'m a full-stack web developer from Sweden.</p>
|
||||
<p>I used to list the <programming/markup/command/whatever>-languages here that I use the most and order them by guesstimating how much I use each one. But then I thought it would be better to just show you instead using this chart that <a href="https://git.vlw.se/config/vlw.se">automatically pulls the total bytes</a> for each language from my public mirrors and sources on <a href="https://git.vlw.se/vlw">Forgejo</a>.</p>
|
||||
<p>I​'m a full-stack web developer from Sweden, and welcome to my little personal corner of the Internet!</p>
|
||||
<p>I used to list the <programming/markup/command/whatever>-languages here that I use the most and order them by guesstimating how much I use each one. But then I thought it would be better to just show you instead using this chart that automatically pulls the total bytes for each language from my public repos on <a href="https://git.vlw.se/vlw">Forgejo</a>.</p>
|
||||
<p>Some other noteworthy techologies that I work a decent amount with are: Debian, MariaDB, SQLite, DNS, Redis, and probably a few others as well. Check out this page for a comprehensive list of all the tech that I use.</p>
|
||||
</section>
|
||||
<section class="languages">
|
||||
<stacked-bar-chart>
|
||||
|
@ -81,30 +96,27 @@
|
|||
</section>
|
||||
<section class="about">
|
||||
<h2>This website</h2>
|
||||
<p>This site and all of its components are <a href="https://codeberg.org/vlw/vlw.se">100% free and open source software</a>. The website is designed and built by me from the ground up using my <a href="https://vegvisir.vlw.se">web</a> and <a href="https://reflect.vlw.se">API</a> frameworks as the foundation. You will find no cookies or trackers here. The only information I have about you is your public IP-address and which resources on this site your browser requests. None of this data is used for any kind of analytics.</p>
|
||||
<p><a href="https://srv.vlw.se"><i>See detailed information about all servers/services on this domain</i></a></p>
|
||||
<p>This site and all of its components, including texts and graphics have been created by me and are all <a href="https://codeberg.org/vlw/vlw.se">100% free and open source</a>. Feel free to use anything you see on this website in your own projects as long as it's under the same GNU GPLv3-or-later license. The website is designed by me on top of my own <a href="https://vegvisir.vlw.se">web framework</a> and <a href="https://reflect.vlw.se">API framework</a>.</p>
|
||||
<p>You will never find cookies or trackers on this site. The only information I have about you are in the standard NGINX web server logs, which get overwritten automatically after some time.</p>
|
||||
</section>
|
||||
<section class="about">
|
||||
<h2>Personal</h2>
|
||||
<p>Coffee, of course.. and..</p>
|
||||
<p>At times, I become a true, amateur, armchair detective for a <span class="interests">variety of your typical-nerdy topics that I find interesting</span>. And will spend a disproportionate to real-world-personal-use amount of time reading about that stuff too.</p>
|
||||
<p>Another silent passion of mine that comes out every few years is building computers and fiddling with weird networking stuff.</p>
|
||||
<p>And then of course I don't mind some occational gaming, and watching movies and TV-series.</p>
|
||||
<p>One thing is true.. Coffee, lots of coffee. In fact, I've had <?= $coffee->week() ?> cup<?= $coffee->week() === 1 ? "" : "s" ?> of coffee in the last 7 days! That's <?= $coffee->week_average_string() ?> my average of <?= $coffee->week_average() ?> per week, impressive! Even though you just read that.. I don't consider myself <i>too much</i> of a coffee snob! As long as it's dark roast and warm, I'm probably happy to have it.</p>
|
||||
<p>At times, I become a true, amateur, armchair detective for a <span class="interests">variety of your typical-nerdy topics that I find interesting</span> and you can bet I spend way more time reading about those things than I will ever have use for in life.</p>
|
||||
<p>My coding happens almost exclusivly in <a href="https://github.com/coder/code-server">code-server</a>, which is a fork of VSCode that runs entirely in the browser. I keep my development environment tucked away in a lightweight Debian VA that I can tote around to whatever host machine I happen to work on. If I can't do that for whatever reason, I have my <a href="https://codeberg.org/vlw/dotfiles">dotfiles</a> ready to get things set up the way I like it.</p>
|
||||
<p>Another silent passion of mine that comes out every few years is building computers and fiddling with networking stuff.</p>
|
||||
</section>
|
||||
<section class="about">
|
||||
<h2>Projects</h2>
|
||||
<p>Here are some projects I'm working on right now:</p>
|
||||
<p>* <a href="https://vegvisir.vlw.se">Vegvisir</a>: A web navigation framework for PHP.</p>
|
||||
<p>* <a href="https://reflect.vlw.se">Reflect</a>: A REST API framework for PHP developers.</p>
|
||||
<p>There is more stuff on my <a href="work">works page</a> and even more stuff on <a href="https://codeberg.org/vlw">my Codeberg profile</a>.</p>
|
||||
<p><a href="https://git.vlw.se/vlw"><i>and even EVEN more stuff on my Forgejo</i></a></p>
|
||||
<p>Check out this <a href="/work/timeline">timeline</a> for a somewhat complete list of everything I have done.</p>
|
||||
</section>
|
||||
<hr>
|
||||
<section class="about">
|
||||
<h3>GitHub</h3>
|
||||
<p>I have <a href="https://giveupgithub.com" target="_blank" rel="noopener noreferer">given up GitHub</a> for their increasing number of injustices againts its users, last betrayal being GitHub's for-profit "Copilot" product which was illegaly trained on copylefted software on its platform.</p>
|
||||
<p>I signed up and started using GitHub before I became aware of how opressive to to its users and deceptive their business model is. I wasn't aware of the situation.</p>
|
||||
<p>While I am a bit skeptical to do this in case history repeats itself; [most of] <a href="https://codeberg.org/vlw">my work is now on Codeberg</a> instead. Unfortunately some things like old pull-requests, issues, and branch archives can not be migrated completely.</p>
|
||||
<p>I have <a href="https://giveupgithub.com" target="_blank" rel="noopener noreferer">given up GitHub</a> and moved most of my free software to <a href="https://codeberg.org/vlw">Codeberg</a>. You can still find my <a href="https://github.com/VictorWesterlund">GitHub profile here</a> but I don't use it for source control anymore.</p>
|
||||
</section>
|
||||
<hr>
|
||||
<section>
|
||||
|
|
|
@ -60,10 +60,6 @@ section.about span.interests {
|
|||
animation: interests-hue 5s infinite linear;
|
||||
}
|
||||
|
||||
section.about p i:not(:hover) {
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
/* ## Languages */
|
||||
|
||||
section.languages {
|
||||
|
|
|
@ -10,9 +10,11 @@
|
|||
|
||||
case WORK = "/work";
|
||||
case SEARCH = "/search";
|
||||
case COFFEE = "/coffee";
|
||||
case MESSAGES = "/messages";
|
||||
case WORK_TAGS = "/work/tags";
|
||||
case WORK_ACTIONS = "/work/actions";
|
||||
case COFFEE_STATS = "/coffee/stats";
|
||||
case WORK_TIMELINE = "/work/timeline";
|
||||
case ABOUT_LANGUAGES = "/about/languages";
|
||||
}
|
|
@ -24,6 +24,12 @@
|
|||
const TIMELINE_PREVIEW_LIMIT_PARAM = "limit";
|
||||
const TIMELINE_PREVIEW_LIMIT_COUNT = 5;
|
||||
|
||||
/**
|
||||
* # Coffee
|
||||
* Constants related to the coffee endpoints
|
||||
*/
|
||||
const COFFEE_STATS_UPDATE_PARAM = "update";
|
||||
|
||||
/**
|
||||
* # Forgejo
|
||||
* Constants related to the fetching and caching of real-time prog. language use on Forgejo
|
||||
|
|
35
src/Database/Models/Coffee/Coffee.php
Normal file
35
src/Database/Models/Coffee/Coffee.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\Database\Models\Coffee;
|
||||
|
||||
use \VV;
|
||||
use \DateTimeImmutable;
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
use VLW\Database\Models\Model;
|
||||
use VLW\Database\Tables\Coffee\CoffeeTable;
|
||||
|
||||
require_once VV::root("src/API/Endpoints.php");
|
||||
require_once VV::root("src/Database/Models/Model.php");
|
||||
require_once VV::root("src/Database/Tables/Coffee/Coffee.php");
|
||||
require_once VV::root("src/Database/Models/Coffee/Coffee.php");
|
||||
|
||||
class Coffee extends Model {
|
||||
public function __construct(public readonly string $id) {
|
||||
parent::__construct(Endpoints::COFFEE, [
|
||||
CoffeeTable::ID->value => $this->id
|
||||
]);
|
||||
}
|
||||
|
||||
public static function all(array $params = []): array {
|
||||
return array_map(fn(array $item): Coffee => new Coffee($item[CoffeeTable::ID->value]), parent::list(Endpoints::COFFEE, $params));
|
||||
}
|
||||
|
||||
public function timestamp(): int {
|
||||
return $this->get(CoffeeTable::ID->value);
|
||||
}
|
||||
|
||||
public function datetime(): DateTimeImmutable {
|
||||
return DateTimeImmutable::createFromFormat("U", $this->timestamp());
|
||||
}
|
||||
}
|
32
src/Database/Models/Coffee/Stats.php
Normal file
32
src/Database/Models/Coffee/Stats.php
Normal file
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\Database\Models\Coffee;
|
||||
|
||||
use \VV;
|
||||
|
||||
use VLW\API\Endpoints;
|
||||
use VLW\Database\Models\Model;
|
||||
use VLW\Database\Tables\Coffee\StatsTable;
|
||||
|
||||
require_once VV::root("src/API/Endpoints.php");
|
||||
require_once VV::root("src/Database/Models/Model.php");
|
||||
require_once VV::root("src/Database/Tables/Coffee/Stats.php");
|
||||
require_once VV::root("src/Database/Models/Coffee/Stats.php");
|
||||
|
||||
class Stats extends Model {
|
||||
public function __construct() {
|
||||
parent::__construct(Endpoints::COFFEE_STATS);
|
||||
}
|
||||
|
||||
public static function all(array $params = []): array {
|
||||
return [];
|
||||
}
|
||||
|
||||
public function week(): int {
|
||||
return $this->get(StatsTable::COUNT_WEEK->value) ?? 0;
|
||||
}
|
||||
|
||||
public function week_average(): int {
|
||||
return $this->get(StatsTable::COUNT_WEEK_AVERAGE->value) ?? 0;
|
||||
}
|
||||
}
|
13
src/Database/Tables/Coffee/Coffee.php
Normal file
13
src/Database/Tables/Coffee/Coffee.php
Normal file
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\Database\Tables\Coffee;
|
||||
|
||||
use vlw\xEnum;
|
||||
|
||||
enum CoffeeTable: string {
|
||||
use xEnum;
|
||||
|
||||
const NAME = "coffee";
|
||||
|
||||
case ID = "id";
|
||||
}
|
14
src/Database/Tables/Coffee/Stats.php
Normal file
14
src/Database/Tables/Coffee/Stats.php
Normal file
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace VLW\Database\Tables\Coffee;
|
||||
|
||||
use vlw\xEnum;
|
||||
|
||||
enum StatsTable: string {
|
||||
use xEnum;
|
||||
|
||||
const NAME = "coffee_stats";
|
||||
|
||||
case COUNT_WEEK = "count_week";
|
||||
case COUNT_WEEK_AVERAGE = "count_week_average";
|
||||
}
|
Loading…
Add table
Reference in a new issue