PHP Classes

File: scripts/qwkconfmapreport.php

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   scripts/qwkconfmapreport.php   Download  
File: scripts/qwkconfmapreport.php
Role: Example script
Content type: text/plain
Description: Example script
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 9,872 bytes
 

Contents

Class file image Download
#!/usr/bin/env php
<?php

declare(strict_types=1);

require_once
__DIR__ . '/../vendor/autoload.php';
require_once
__DIR__ . '/../src/functions.php';

use
BinktermPHP\Database;

main($argv);

function
main(array $argv): void
{
   
$args = parseArgs($argv);

    if (!empty(
$args['help'])) {
       
printUsage();
        exit(
0);
    }

   
$db = Database::getInstance()->getPdo();

   
$params = [];
   
$where = [];

    if (
$args['user_id'] !== null) {
       
$where[] = 'q.user_id = :user_id';
       
$params[':user_id'] = $args['user_id'];
    }

    if (
$args['username'] !== null) {
       
$where[] = 'LOWER(u.username) = LOWER(:username)';
       
$params[':username'] = $args['username'];
    }

   
$sql = "
        SELECT q.id,
               q.user_id,
               q.downloaded_at,
               q.message_count,
               q.packet_size,
               q.conference_map,
               u.username,
               u.real_name
        FROM qwk_download_log q
        LEFT JOIN users u ON u.id = q.user_id
    "
;

    if (!empty(
$where)) {
       
$sql .= ' WHERE ' . implode(' AND ', $where);
    }

   
$sql .= ' ORDER BY q.user_id ASC, q.downloaded_at ASC, q.id ASC';

   
$stmt = $db->prepare($sql);
   
$stmt->execute($params);
   
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);

    if (empty(
$rows)) {
       
fwrite(STDOUT, "No QWK download log rows matched the requested filters.\n");
        exit(
0);
    }

   
$report = buildReport($rows, !empty($args['changes_only']));
   
renderReport($report, !empty($args['changes_only']));
}

function
parseArgs(array $argv): array
{
   
$args = [
       
'help' => false,
       
'user_id' => null,
       
'username' => null,
       
'changes_only' => false,
    ];

    foreach (
array_slice($argv, 1) as $arg) {
        if (
$arg === '--help' || $arg === '-h') {
           
$args['help'] = true;
            continue;
        }

        if (
$arg === '--changes-only') {
           
$args['changes_only'] = true;
            continue;
        }

        if (
str_starts_with($arg, '--user-id=')) {
           
$value = substr($arg, strlen('--user-id='));
            if (
$value === '' || !ctype_digit($value)) {
               
fwrite(STDERR, "Invalid --user-id value.\n");
                exit(
2);
            }
           
$args['user_id'] = (int)$value;
            continue;
        }

        if (
str_starts_with($arg, '--username=')) {
           
$value = trim(substr($arg, strlen('--username=')));
            if (
$value === '') {
               
fwrite(STDERR, "Invalid --username value.\n");
                exit(
2);
            }
           
$args['username'] = $value;
            continue;
        }

       
fwrite(STDERR, "Unknown option: {$arg}\n");
       
printUsage();
        exit(
2);
    }

    return
$args;
}

function
printUsage(): void
{
    echo <<<TXT
QWK conference map history report

Usage:
  php scripts/qwkconfmapreport.php [--user-id=N] [--username=NAME] [--changes-only]

Options:
  --user-id=N Limit report to one user id
  --username=NAME Limit report to one username (case-insensitive)
  --changes-only Show only conference entries whose number changed
  --help, -h Show this help

Examples:
  php scripts/qwkconfmapreport.php
  php scripts/qwkconfmapreport.php --username=sysop
  php scripts/qwkconfmapreport.php --user-id=42 --changes-only

TXT;
}

function
buildReport(array $rows, bool $changesOnly): array
{
   
$report = [];

    foreach (
$rows as $row) {
       
$userId = (int)$row['user_id'];
        if (!isset(
$report[$userId])) {
           
$report[$userId] = [
               
'user_id' => $userId,
               
'username' => $row['username'] ?? '(unknown)',
               
'real_name' => $row['real_name'] ?? '',
               
'downloads' => [],
               
'changes' => 0,
               
'areas_changed' => [],
               
'previous_numbers' => [],
            ];
        }

       
$map = json_decode((string)$row['conference_map'], true);
        if (!
is_array($map)) {
           
$map = [];
        }

       
ksort($map, SORT_NUMERIC);
       
$entries = [];

        foreach (
$map as $conferenceNumberRaw => $conf) {
            if (!
is_array($conf)) {
                continue;
            }

           
$conferenceNumber = (int)$conferenceNumberRaw;
           
$areaKey = buildAreaKey($conf, $conferenceNumber);
           
$display = buildAreaDisplayName($conf, $conferenceNumber);
           
$previous = $report[$userId]['previous_numbers'][$areaKey] ?? null;
           
$changed = $previous !== null && $previous !== $conferenceNumber;

            if (
$changed) {
               
$report[$userId]['changes']++;
               
$report[$userId]['areas_changed'][$areaKey] = true;
            }

            if (!
$changesOnly || $changed) {
               
$entries[] = [
                   
'conference_number' => $conferenceNumber,
                   
'previous_number' => $previous,
                   
'changed' => $changed,
                   
'area_key' => $areaKey,
                   
'display' => $display,
                   
'echoarea_id' => isset($conf['echoarea_id']) ? (int)$conf['echoarea_id'] : null,
                   
'tag' => (string)($conf['tag'] ?? ''),
                   
'domain' => (string)($conf['domain'] ?? ''),
                   
'is_netmail' => !empty($conf['is_netmail']),
                ];
            }

           
$report[$userId]['previous_numbers'][$areaKey] = $conferenceNumber;
        }

        if (!
$changesOnly || !empty($entries)) {
           
$report[$userId]['downloads'][] = [
               
'id' => (int)$row['id'],
               
'downloaded_at' => $row['downloaded_at'],
               
'message_count' => (int)$row['message_count'],
               
'packet_size' => (int)$row['packet_size'],
               
'entries' => $entries,
            ];
        }
    }

    foreach (
$report as $userId => $userReport) {
        unset(
$report[$userId]['previous_numbers']);
       
$report[$userId]['areas_changed_count'] = count($report[$userId]['areas_changed']);
    }

    return
$report;
}

function
buildAreaKey(array $conf, int $conferenceNumber): string
{
    if (!empty(
$conf['is_netmail'])) {
        return
'netmail';
    }

    if (isset(
$conf['echoarea_id']) && $conf['echoarea_id'] !== null && $conf['echoarea_id'] !== '') {
        return
'echoarea_id:' . (int)$conf['echoarea_id'];
    }

   
$tag = strtoupper(trim((string)($conf['tag'] ?? '')));
   
$domain = strtoupper(trim((string)($conf['domain'] ?? '')));
    if (
$tag !== '') {
        return
$domain !== '' ? $tag . '@' . $domain : $tag;
    }

   
$name = strtoupper(trim((string)($conf['name'] ?? '')));
    if (
$name !== '') {
        return
'name:' . $name;
    }

    return
'conf:' . $conferenceNumber;
}

function
buildAreaDisplayName(array $conf, int $conferenceNumber): string
{
    if (!empty(
$conf['is_netmail'])) {
        return
'Personal Mail';
    }

   
$tag = strtoupper(trim((string)($conf['tag'] ?? '')));
   
$domain = strtoupper(trim((string)($conf['domain'] ?? '')));
    if (
$tag !== '') {
        return
$domain !== '' ? $tag . '@' . $domain : $tag;
    }

   
$name = trim((string)($conf['name'] ?? ''));
    if (
$name !== '') {
        return
$name;
    }

    return
'Conference ' . $conferenceNumber;
}

function
renderReport(array $report, bool $changesOnly): void
{
   
$totalUsers = count($report);
   
$totalDownloads = 0;
   
$totalChanges = 0;

    foreach (
$report as $userReport) {
       
$totalDownloads += count($userReport['downloads']);
       
$totalChanges += $userReport['changes'];
    }

    echo
'QWK Conference Map Report' . PHP_EOL;
    echo
'Users: ' . $totalUsers . PHP_EOL;
    echo
'Downloads: ' . $totalDownloads . PHP_EOL;
    echo
'Conference-number changes detected: ' . $totalChanges . PHP_EOL;
    echo
'Mode: ' . ($changesOnly ? 'changes-only' : 'full history') . PHP_EOL;

    foreach (
$report as $userReport) {
        echo
PHP_EOL;
        echo
'User #' . $userReport['user_id'] . ' ' . $userReport['username'];
        if (
$userReport['real_name'] !== '') {
            echo
' (' . $userReport['real_name'] . ')';
        }
        echo
PHP_EOL;
        echo
'Downloads: ' . count($userReport['downloads'])
            .
' | Changes: ' . $userReport['changes']
            .
' | Areas changed: ' . $userReport['areas_changed_count']
            .
PHP_EOL;

        if (empty(
$userReport['downloads'])) {
            echo
" No matching download rows after filters.\n";
            continue;
        }

        foreach (
$userReport['downloads'] as $download) {
            echo
PHP_EOL;
            echo
' Download #' . $download['id']
                .
' at ' . $download['downloaded_at']
                .
' | messages=' . $download['message_count']
                .
' | packet_size=' . $download['packet_size']
                .
PHP_EOL;

            if (empty(
$download['entries'])) {
                echo
" No conference entries matched.\n";
                continue;
            }

            foreach (
$download['entries'] as $entry) {
               
$line = ' '
                   
. str_pad((string)$entry['conference_number'], 5, ' ', STR_PAD_LEFT)
                    .
' '
                   
. $entry['display'];

                if (
$entry['changed']) {
                   
$line .= ' CHANGED from ' . $entry['previous_number'];
                } elseif (
$entry['previous_number'] !== null) {
                   
$line .= ' same as previous (' . $entry['previous_number'] . ')';
                } else {
                   
$line .= ' first seen';
                }

                if (
$entry['echoarea_id'] !== null) {
                   
$line .= ' [echoarea_id=' . $entry['echoarea_id'] . ']';
                }

                echo
$line . PHP_EOL;
            }
        }
    }
}