death-notifier/src/main/php/TrackingManager.php

168 lines
5.7 KiB
PHP

<?php
namespace php;
use Monolog\Logger;
use PDO;
/**
* Manages interaction with the database in the context of trackings.
*/
class TrackingManager
{
/**
* The maximum length of a Wikipedia page title.
*/
private const MAX_TITLE_LENGTH = 255;
/**
* @var Logger The logger to use for logging.
*/
private Logger $logger;
/**
* @var string The filename of the database to interact with.
*/
private string $db_filename;
/**
* @var Mediawiki The Mediawiki instance to use for interacting with Wikipedia.
*/
private Mediawiki $mediawiki;
/**
* Constructs a new tracking manager.
*
* @param Logger $logger the logger to use for logging
* @param string $db_filename the filename of the database to interact with
*/
public function __construct(Logger $logger, Mediawiki $mediawiki, string $db_filename)
{
$this->logger = $logger;
$this->mediawiki = $mediawiki;
$this->db_filename = $db_filename;
}
/**
* Populates the database with the necessary structures for users.
*
* @return void
*/
public function install(): void
{
$conn = Database::connect($this->db_filename);
$conn->exec("CREATE TABLE trackings(user_uuid text not null, person_name text not null, status text not null, PRIMARY KEY (user_uuid, person_name));");
}
/**
* Adds a tracking to the database.
*
* @param string $user_uuid the user to whom the tracking belongs
* @param string $person_name the name of the person to track
* @return Response a response with no message, and a status indicating whether the insertion succeeded
*/
public function add_tracking(string $user_uuid, string $person_name): Response
{
if (strlen($person_name) > self::MAX_TITLE_LENGTH)
return new Response("Invalid page name: too long.", false);
if (!$this->mediawiki->pages_exist([$person_name])[$person_name])
return new Response("Page does not exist.", false);
$status = $this->mediawiki->people_statuses([$person_name])[$person_name];
if ($status === null)
return new Response("Page does not refer to a person.", false);
$conn = Database::connect($this->db_filename);
$conn->beginTransaction();
$stmt = $conn->prepare("SELECT COUNT(*) as count FROM trackings WHERE user_uuid=:user_uuid AND person_name=:person_name;");
$stmt->bindValue(":user_uuid", $user_uuid);
$stmt->bindValue(":person_name", $person_name);
$stmt->execute();
if ($stmt->fetch(PDO::FETCH_ASSOC)["count"] > 0) {
$conn->rollBack();
return new Response("Tracking already exists.", false);
}
$stmt = $conn->prepare("INSERT INTO trackings (user_uuid, person_name, status) VALUES (:user_uuid, :person_name, :status);");
$stmt->bindValue(":user_uuid", $user_uuid);
$stmt->bindValue(":person_name", $person_name);
$stmt->bindValue(":status", $status);
$stmt->execute();
$conn->commit();
return new Response(null, true);
}
/**
* Removes a tracking from the database.
*
* @param string $user_uuid the user to whom the tracking belongs
* @param string $person_name the name of the tracked person to remove
* @return Response a response with no message, and a status indicating whether the removal succeeded
*/
public function remove_tracking(string $user_uuid, string $person_name): Response
{
if (strlen($person_name) > self::MAX_TITLE_LENGTH)
return new Response(null, false);
$conn = Database::connect($this->db_filename);
$stmt = $conn->prepare("DELETE FROM trackings WHERE user_uuid=:user_uuid AND person_name=:person_name;");
$stmt->bindValue(":user_uuid", $user_uuid);
$stmt->bindValue(":person_name", $person_name);
$stmt->execute();
return new Response(null, true);
}
/**
* Lists all trackings of the indicated user.
*
* @param string $user_uuid the user to return the trackings of
* @return Response a response with all trackings of the indicated user
*/
public function list_trackings(string $user_uuid): Response
{
$conn = Database::connect($this->db_filename);
$stmt = $conn->prepare("SELECT person_name, status FROM trackings WHERE user_uuid=:user_uuid;");
$stmt->bindValue(":user_uuid", $user_uuid);
$stmt->execute();
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
return new Response($results, true);
}
/**
* Lists all unique names being tracked in the database.
*
* @return string[] the unique names in the database
*/
public function list_all_trackings(): array
{
$conn = Database::connect($this->db_filename);
$stmt = $conn->prepare("SELECT DISTINCT person_name FROM trackings;");
$stmt->execute();
return array_column($stmt->fetchAll(PDO::FETCH_ASSOC), "person_name");
}
/**
* Updates trackings for the given names.
*
* @param string[] $people_names the names of the pages to update the tracking of
* @return void
*/
public function update_trackings(array $people_names): void
{
// TODO: Handle removed pages
$people_statuses = $this->mediawiki->people_statuses($people_names);
$conn = Database::connect($this->db_filename);
$stmt = $conn->prepare("UPDATE trackings SET status=:status WHERE person_name=:person_name;");
$stmt->bindParam(":status", $person_status);
$stmt->bindParam(":person_name", $person_name);
foreach ($people_statuses as $person_name => $person_status)
$stmt->execute();
}
}