feat: add coffee stats to about page

This commit is contained in:
Victor Westerlund 2025-03-09 14:17:08 +01:00
parent c2596a0681
commit ded2ea4f5f
11 changed files with 273 additions and 4 deletions

38
endpoints/coffee/GET.php Normal file
View 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
View 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);
}
}

View 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());
}
}

View 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);
}
}

View file

@ -1,5 +1,6 @@
<?php <?php
use VLW\Database\Models\Coffee\Stats;
use VLW\Database\Models\About\Language; use VLW\Database\Models\About\Language;
use const VLW\{ use const VLW\{
FORGEJO_HREF, FORGEJO_HREF,
@ -8,6 +9,7 @@
}; };
require_once VV::root("src/Consts.php"); 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"); require_once VV::root("src/Database/Models/About/Language.php");
$languages = new class extends Language { $languages = new class extends Language {
@ -33,7 +35,19 @@
return round($format) . " " . FORGEJO_SI_BYTE_MULTIPLE[$factor]; 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> <style><?= VV::css("public/assets/css/pages/about") ?></style>
@ -44,8 +58,8 @@
<hr aria-hidden="true"> <hr aria-hidden="true">
<section class="about"> <section class="about">
<p>I&ZeroWidthSpace;'m a full-stack web developer from Sweden, and welcome to my little personal corner of the Internet!</p> <p>I&ZeroWidthSpace;'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 &lt;programming/markup/command/whatever&gt;-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 used to list the &lt;programming/markup/command/whatever&gt;-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, and probably a few others as well. Check out this page for a comprehensive list of all the tech that I use.</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>
<section class="languages"> <section class="languages">
<stacked-bar-chart> <stacked-bar-chart>
@ -87,7 +101,7 @@
</section> </section>
<section class="about"> <section class="about">
<h2>Personal</h2> <h2>Personal</h2>
<p>One thing is true.. Coffee, lots of coffee. In fact, I've had X cups of coffee in the last 7 days! That's less than my average of X 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>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>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>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 weird networking stuff.</p> <p>Another silent passion of mine that comes out every few years is building computers and fiddling with weird networking stuff.</p>

View file

@ -10,9 +10,11 @@
case WORK = "/work"; case WORK = "/work";
case SEARCH = "/search"; case SEARCH = "/search";
case COFFEE = "/coffee";
case MESSAGES = "/messages"; case MESSAGES = "/messages";
case WORK_TAGS = "/work/tags"; case WORK_TAGS = "/work/tags";
case WORK_ACTIONS = "/work/actions"; case WORK_ACTIONS = "/work/actions";
case COFFEE_STATS = "/coffee/stats";
case WORK_TIMELINE = "/work/timeline"; case WORK_TIMELINE = "/work/timeline";
case ABOUT_LANGUAGES = "/about/languages"; case ABOUT_LANGUAGES = "/about/languages";
} }

View file

@ -24,6 +24,12 @@
const TIMELINE_PREVIEW_LIMIT_PARAM = "limit"; const TIMELINE_PREVIEW_LIMIT_PARAM = "limit";
const TIMELINE_PREVIEW_LIMIT_COUNT = 5; const TIMELINE_PREVIEW_LIMIT_COUNT = 5;
/**
* # Coffee
* Constants related to the coffee endpoints
*/
const COFFEE_STATS_UPDATE_PARAM = "update";
/** /**
* # Forgejo * # Forgejo
* Constants related to the fetching and caching of real-time prog. language use on Forgejo * Constants related to the fetching and caching of real-time prog. language use on Forgejo

View 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());
}
}

View 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);
}
public function week_average(): int {
return $this->get(StatsTable::COUNT_WEEK_AVERAGE->value);
}
}

View 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";
}

View 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";
}