diff options
| author | Paul Buetow <paul@buetow.org> | 2025-06-30 01:22:17 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2025-06-30 01:22:17 +0300 |
| commit | 0d7aff848958701865f05b6badac28b2a3e06492 (patch) | |
| tree | 4e8abc508bf8b9b43904f67c9c3cf6f9c5aea306 /foostats.pl | |
| parent | 5a0c0ef55bc9e848aa212321387710f7247d4e53 (diff) | |
feat: Move all .gmi reports to gemtext subfolder
- Modified daily report generation to write to stats/gemtext/
- Modified 30-day report generation to write to stats/gemtext/
- Added mkdir calls to create gemtext directory if it doesn't exist
- All .gmi files are now organized in a dedicated subfolder
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'foostats.pl')
| -rw-r--r-- | foostats.pl | 410 |
1 files changed, 122 insertions, 288 deletions
diff --git a/foostats.pl b/foostats.pl index a78ef23..b83d0db 100644 --- a/foostats.pl +++ b/foostats.pl @@ -828,16 +828,17 @@ package Foostats::Reporter { my $current_month = $today->strftime('%Y%m%d'); $report_content .= "=> ./30day_summary_$current_month.gmi 30-Day Summary Report\n\n"; - my $report_path = "$stats_dir/$date.gmi"; + # Ensure gemtext directory exists + my $gemtext_dir = "$stats_dir/gemtext"; + mkdir $gemtext_dir unless -d $gemtext_dir; + + my $report_path = "$gemtext_dir/$date.gmi"; say "Writing report to $report_path"; FileHelper::write( $report_path, $report_content ); } # Generate 30-day summary report generate_30day_report($stats_dir, %merged); - - # Generate yearly report - generate_yearly_report($stats_dir, %merged); } sub generate_30day_report { @@ -850,295 +851,121 @@ package Foostats::Reporter { my $today = localtime; my $report_date = $today->strftime('%Y%m%d'); - my $report_content = "# 30-Day Summary Report -"; - $report_content .= "## Generated on " . $today->strftime('%Y-%m-%d') . " - -"; - - # Summary section - day-to-day evolution - $report_content .= "## Daily Summary Evolution (Last 30 Days) - -"; - $report_content .= "### Total Requests by Day - -``` -"; + # Build report content + my $report_content = build_report_header($today); + $report_content .= build_daily_summary_section(\@dates, \%merged); + $report_content .= build_feed_statistics_section(\@dates, \%merged); - my @summary_rows; - for my $date (reverse @dates) { - my $stats = $merged{$date}; - next unless $stats->{count}; - - my ( $year, $month, $day ) = $date =~ /(\d{4})(\d{2})(\d{2})/; - my $formatted_date = "$year-$month-$day"; - - my $total_requests = ($stats->{count}{gemini} // 0) + ($stats->{count}{web} // 0); - my $filtered = $stats->{count}{filtered} // 0; - my $gemini = $stats->{count}{gemini} // 0; - my $web = $stats->{count}{web} // 0; - my $ipv4 = $stats->{count}{IPv4} // 0; - my $ipv6 = $stats->{count}{IPv6} // 0; - - push @summary_rows, [ $formatted_date, $total_requests, $filtered, $gemini, $web, $ipv4, $ipv6 ]; - } + # Aggregate and add top lists + my ($all_hosts, $all_urls) = aggregate_hosts_and_urls(\@dates, \%merged); + $report_content .= build_top_hosts_section($all_hosts); + $report_content .= build_top_urls_section($all_urls); - $report_content .= format_table( - [ 'Date', 'Total', 'Filtered', 'Gemini', 'Web', 'IPv4', 'IPv6' ], - \@summary_rows - ); - $report_content .= " -``` - -"; + # Add daily report links + $report_content .= build_daily_reports_links(\@dates, \%merged); - # Feed statistics evolution - $report_content .= "### Feed Statistics Evolution - -``` -"; + # Ensure gemtext directory exists and write the 30-day report + my $gemtext_dir = "$stats_dir/gemtext"; + mkdir $gemtext_dir unless -d $gemtext_dir; - my @feed_rows; - for my $date (reverse @dates) { - my $stats = $merged{$date}; - next unless $stats->{feed_ips}; - - my ( $year, $month, $day ) = $date =~ /(\d{4})(\d{2})(\d{2})/; - my $formatted_date = "$year-$month-$day"; - - push @feed_rows, [ - $formatted_date, - $stats->{feed_ips}{'Total'} // 0, - $stats->{feed_ips}{'Gemini Gemfeed'} // 0, - $stats->{feed_ips}{'Gemini Atom'} // 0, - $stats->{feed_ips}{'Web Gemfeed'} // 0, - $stats->{feed_ips}{'Web Atom'} // 0 - ]; - } + my $report_path = "$gemtext_dir/30day_summary_$report_date.gmi"; + say "Writing 30-day summary report to $report_path"; + FileHelper::write( $report_path, $report_content ); + } + + sub build_report_header { + my ($today) = @_; - $report_content .= format_table( - [ 'Date', 'Total', 'Gem Feed', 'Gem Atom', 'Web Feed', 'Web Atom' ], - \@feed_rows - ); - $report_content .= " -``` - -"; + my $content = "# 30-Day Summary Report\n"; + $content .= "## Generated on " . $today->strftime('%Y-%m-%d') . "\n\n"; + return $content; + } + + sub build_daily_summary_section { + my ($dates, $merged) = @_; - # Aggregate hosts and URLs for 30-day period - my %all_hosts; - my %all_urls; + my $content = "## Daily Summary Evolution (Last 30 Days)\n\n"; + $content .= "### Total Requests by Day\n\n```\n"; - for my $date (@dates) { - my $stats = $merged{$date}; - next unless $stats->{page_ips}; - - # Aggregate hosts - while (my ($host, $count) = each %{$stats->{page_ips}{hosts}}) { - $all_hosts{$host} //= 0; - $all_hosts{$host} += $count; - } + my @summary_rows; + for my $date (reverse @$dates) { + my $stats = $merged->{$date}; + next unless $stats->{count}; - # Aggregate URLs - while (my ($url, $count) = each %{$stats->{page_ips}{urls}}) { - $all_urls{$url} //= 0; - $all_urls{$url} += $count; - } + push @summary_rows, build_daily_summary_row($date, $stats); } - # Top 50 hosts - $report_content .= "## Top 50 Hosts (30-Day Total) - -``` -"; - - my @host_rows; - my @sorted_hosts = sort { $all_hosts{$b} <=> $all_hosts{$a} } keys %all_hosts; - @sorted_hosts = @sorted_hosts[0..49] if @sorted_hosts > 50; - - for my $host (@sorted_hosts) { - push @host_rows, [ $host, $all_hosts{$host} ]; - } - - $report_content .= format_table( - [ 'Host', 'Total Unique Visitors' ], - \@host_rows + $content .= format_table( + [ 'Date', 'Total', 'Filtered', 'Gemini', 'Web', 'IPv4', 'IPv6' ], + \@summary_rows ); - $report_content .= " -``` - -"; - - # Top 50 URLs - $report_content .= "## Top 50 URLs (30-Day Total) - -``` -"; - - my @url_rows; - my @sorted_urls = sort { $all_urls{$b} <=> $all_urls{$a} } keys %all_urls; - @sorted_urls = @sorted_urls[0..49] if @sorted_urls > 50; - - for my $url (@sorted_urls) { - push @url_rows, [ $url, $all_urls{$url} ]; - } + $content .= "\n```\n\n"; - $report_content .= format_table( - [ 'URL', 'Total Unique Visitors' ], - \@url_rows - ); - $report_content .= " -``` - -"; - - # Links to daily reports - $report_content .= "## Daily Reports - -"; - - for my $date (@dates) { - next unless exists $merged{$date} && $merged{$date}->{count}; - - my ( $year, $month, $day ) = $date =~ /(\d{4})(\d{2})(\d{2})/; - my $formatted_date = "$year-$month-$day"; - - $report_content .= "=> ./$date.gmi $formatted_date Daily Report -"; - } - - # Add link to yearly report - $report_content .= "\n## Related Reports\n\n"; - my $year = $today->strftime('%Y'); - $report_content .= "=> ./yearly_summary_$year.gmi Yearly Summary Report\n"; - - # Write the 30-day report - my $report_path = "$stats_dir/30day_summary_$report_date.gmi"; - say "Writing 30-day summary report to $report_path"; - FileHelper::write( $report_path, $report_content ); + return $content; } - sub generate_yearly_report { - my ( $stats_dir, %merged ) = @_; - - # Get all available dates - my @all_dates = sort { $a cmp $b } keys %merged; - - # Filter to only dates that have data - @all_dates = grep { exists $merged{$_} && $merged{$_}->{count} } @all_dates; - - return unless @all_dates; # No data available + sub build_daily_summary_row { + my ($date, $stats) = @_; - my $today = localtime; - my $current_year = $today->strftime('%Y'); + my ( $year, $month, $day ) = $date =~ /(\d{4})(\d{2})(\d{2})/; + my $formatted_date = "$year-$month-$day"; - # Count total days with data - my $total_days = scalar @all_dates; + my $total_requests = ($stats->{count}{gemini} // 0) + ($stats->{count}{web} // 0); + my $filtered = $stats->{count}{filtered} // 0; + my $gemini = $stats->{count}{gemini} // 0; + my $web = $stats->{count}{web} // 0; + my $ipv4 = $stats->{count}{IPv4} // 0; + my $ipv6 = $stats->{count}{IPv6} // 0; - my $report_content = "# Yearly Summary Report\n"; - $report_content .= "## Generated on " . $today->strftime('%Y-%m-%d') . "\n\n"; - $report_content .= "## Report covers $total_days days of data\n\n"; + return [ $formatted_date, $total_requests, $filtered, $gemini, $web, $ipv4, $ipv6 ]; + } + + sub build_feed_statistics_section { + my ($dates, $merged) = @_; - # Monthly aggregation - my %monthly_stats; + my $content = "### Feed Statistics Evolution\n\n```\n"; - for my $date (@all_dates) { - my $stats = $merged{$date}; - next unless $stats->{count}; - - my ( $year, $month, $day ) = $date =~ /(\d{4})(\d{2})(\d{2})/; - my $month_key = "$year-$month"; - - # Initialize monthly stats - $monthly_stats{$month_key} //= { - total_requests => 0, - filtered => 0, - gemini => 0, - web => 0, - ipv4 => 0, - ipv6 => 0, - feed_total => 0, - gem_feed => 0, - gem_atom => 0, - web_feed => 0, - web_atom => 0, - days => 0 - }; - - my $ms = $monthly_stats{$month_key}; - $ms->{days}++; - - # Aggregate counts - $ms->{total_requests} += ($stats->{count}{gemini} // 0) + ($stats->{count}{web} // 0); - $ms->{filtered} += $stats->{count}{filtered} // 0; - $ms->{gemini} += $stats->{count}{gemini} // 0; - $ms->{web} += $stats->{count}{web} // 0; - $ms->{ipv4} += $stats->{count}{IPv4} // 0; - $ms->{ipv6} += $stats->{count}{IPv6} // 0; + my @feed_rows; + for my $date (reverse @$dates) { + my $stats = $merged->{$date}; + next unless $stats->{feed_ips}; - # Aggregate feed stats - if ($stats->{feed_ips}) { - $ms->{feed_total} += $stats->{feed_ips}{'Total'} // 0; - $ms->{gem_feed} += $stats->{feed_ips}{'Gemini Gemfeed'} // 0; - $ms->{gem_atom} += $stats->{feed_ips}{'Gemini Atom'} // 0; - $ms->{web_feed} += $stats->{feed_ips}{'Web Gemfeed'} // 0; - $ms->{web_atom} += $stats->{feed_ips}{'Web Atom'} // 0; - } - } - - # Summary section - monthly evolution - $report_content .= "## Monthly Summary Evolution\n\n"; - $report_content .= "### Total Requests by Month\n\n```\n"; - - my @summary_rows; - for my $month (sort keys %monthly_stats) { - my $ms = $monthly_stats{$month}; - push @summary_rows, [ - $month, - $ms->{days}, - $ms->{total_requests}, - $ms->{filtered}, - $ms->{gemini}, - $ms->{web}, - $ms->{ipv4}, - $ms->{ipv6} - ]; + push @feed_rows, build_feed_statistics_row($date, $stats); } - $report_content .= format_table( - [ 'Month', 'Days', 'Total', 'Filtered', 'Gemini', 'Web', 'IPv4', 'IPv6' ], - \@summary_rows + $content .= format_table( + [ 'Date', 'Total', 'Gem Feed', 'Gem Atom', 'Web Feed', 'Web Atom' ], + \@feed_rows ); - $report_content .= "\n```\n\n"; + $content .= "\n```\n\n"; - # Feed statistics by month - $report_content .= "### Feed Statistics by Month\n\n```\n"; + return $content; + } + + sub build_feed_statistics_row { + my ($date, $stats) = @_; - my @feed_rows; - for my $month (sort keys %monthly_stats) { - my $ms = $monthly_stats{$month}; - push @feed_rows, [ - $month, - $ms->{feed_total}, - $ms->{gem_feed}, - $ms->{gem_atom}, - $ms->{web_feed}, - $ms->{web_atom} - ]; - } + my ( $year, $month, $day ) = $date =~ /(\d{4})(\d{2})(\d{2})/; + my $formatted_date = "$year-$month-$day"; - $report_content .= format_table( - [ 'Month', 'Total', 'Gem Feed', 'Gem Atom', 'Web Feed', 'Web Atom' ], - \@feed_rows - ); - $report_content .= "\n```\n\n"; + return [ + $formatted_date, + $stats->{feed_ips}{'Total'} // 0, + $stats->{feed_ips}{'Gemini Gemfeed'} // 0, + $stats->{feed_ips}{'Gemini Atom'} // 0, + $stats->{feed_ips}{'Web Gemfeed'} // 0, + $stats->{feed_ips}{'Web Atom'} // 0 + ]; + } + + sub aggregate_hosts_and_urls { + my ($dates, $merged) = @_; - # Aggregate hosts and URLs for entire period my %all_hosts; my %all_urls; - for my $date (@all_dates) { - my $stats = $merged{$date}; + for my $date (@$dates) { + my $stats = $merged->{$date}; next unless $stats->{page_ips}; # Aggregate hosts @@ -1154,61 +981,68 @@ package Foostats::Reporter { } } - # Top 50 hosts - $report_content .= "## Top 50 Hosts (Yearly Total)\n\n```\n"; + return (\%all_hosts, \%all_urls); + } + + sub build_top_hosts_section { + my ($all_hosts) = @_; + + my $content = "## Top 50 Hosts (30-Day Total)\n\n```\n"; my @host_rows; - my @sorted_hosts = sort { $all_hosts{$b} <=> $all_hosts{$a} } keys %all_hosts; + my @sorted_hosts = sort { $all_hosts->{$b} <=> $all_hosts->{$a} } keys %$all_hosts; @sorted_hosts = @sorted_hosts[0..49] if @sorted_hosts > 50; for my $host (@sorted_hosts) { - push @host_rows, [ $host, $all_hosts{$host} ]; + push @host_rows, [ $host, $all_hosts->{$host} ]; } - $report_content .= format_table( + $content .= format_table( [ 'Host', 'Total Unique Visitors' ], \@host_rows ); - $report_content .= "\n```\n\n"; + $content .= "\n```\n\n"; - # Top 50 URLs - $report_content .= "## Top 50 URLs (Yearly Total)\n\n```\n"; + return $content; + } + + sub build_top_urls_section { + my ($all_urls) = @_; + + my $content = "## Top 50 URLs (30-Day Total)\n\n```\n"; my @url_rows; - my @sorted_urls = sort { $all_urls{$b} <=> $all_urls{$a} } keys %all_urls; + my @sorted_urls = sort { $all_urls->{$b} <=> $all_urls->{$a} } keys %$all_urls; @sorted_urls = @sorted_urls[0..49] if @sorted_urls > 50; for my $url (@sorted_urls) { - push @url_rows, [ $url, $all_urls{$url} ]; + push @url_rows, [ $url, $all_urls->{$url} ]; } - $report_content .= format_table( + $content .= format_table( [ 'URL', 'Total Unique Visitors' ], \@url_rows ); - $report_content .= "\n```\n\n"; + $content .= "\n```\n\n"; - # Links to monthly reports - $report_content .= "## Monthly Reports\n\n"; + return $content; + } + + sub build_daily_reports_links { + my ($dates, $merged) = @_; - # Get list of all 30-day summary reports - my @monthly_reports = glob "$stats_dir/30day_summary_*.gmi"; - @monthly_reports = sort { $b cmp $a } @monthly_reports; + my $content = "## Daily Reports\n\n"; - for my $report_path (@monthly_reports) { - my ($date) = $report_path =~ /30day_summary_(\d{8})\.gmi$/; - next unless $date; + for my $date (@$dates) { + next unless exists $merged->{$date} && $merged->{$date}->{count}; my ( $year, $month, $day ) = $date =~ /(\d{4})(\d{2})(\d{2})/; my $formatted_date = "$year-$month-$day"; - $report_content .= "=> ./30day_summary_$date.gmi 30-Day Summary for $formatted_date\n"; + $content .= "=> ./$date.gmi $formatted_date Daily Report\n"; } - # Write the yearly report - my $report_path = "$stats_dir/yearly_summary_$current_year.gmi"; - say "Writing yearly summary report to $report_path"; - FileHelper::write( $report_path, $report_content ); + return $content; } } |
