|
|
|
@@ -29,10 +29,10 @@
|
|
|
|
|
# Where pod2pdf comes from App::pod2pdf
|
|
|
|
|
#
|
|
|
|
|
# AUTHOR: Dave Morriss (djm), Dave.Morriss@gmail.com
|
|
|
|
|
# VERSION: 0.4.4
|
|
|
|
|
# VERSION: 0.4.5
|
|
|
|
|
# ORIGINAL: 2014-04-24 16:08:30
|
|
|
|
|
# CREATED: 2025-03-13 15:07:35
|
|
|
|
|
# REVISION: 2025-04-17 15:12:27
|
|
|
|
|
# REVISION: 2025-08-09 11:28:22
|
|
|
|
|
#
|
|
|
|
|
#===============================================================================
|
|
|
|
|
|
|
|
|
@@ -51,6 +51,8 @@ use Pod::Usage qw(pod2usage); # Use colour-capable Pod::Text
|
|
|
|
|
|
|
|
|
|
use Config::General;
|
|
|
|
|
|
|
|
|
|
use File::Copy;
|
|
|
|
|
|
|
|
|
|
use Date::Parse;
|
|
|
|
|
use Date::Calc qw{:all};
|
|
|
|
|
use DateTime;
|
|
|
|
@@ -71,7 +73,7 @@ use Data::Dumper;
|
|
|
|
|
#
|
|
|
|
|
# Version number (manually incremented)
|
|
|
|
|
#
|
|
|
|
|
our $VERSION = '0.4.4';
|
|
|
|
|
our $VERSION = '0.4.5';
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Various constants
|
|
|
|
@@ -106,8 +108,7 @@ my $defcontainer = "$basedir/shownotes_container.tpl";
|
|
|
|
|
my ( $dbh, $sth1, $h1 );
|
|
|
|
|
my ( @review_month, $releasedate, @releasedate, $hosts, $shows, $episode );
|
|
|
|
|
my ( @dc_lr, $dt_lr, @dc_lm, $dt_lm, @dc_rd, $dt_rd );
|
|
|
|
|
my ( %attributes );
|
|
|
|
|
my ( %date_cache, $date_offset, @deftime );
|
|
|
|
|
my ( %attributes, %date_cache, $lr_option_status, $date_offset, @deftime );
|
|
|
|
|
my ( $t_time, $missed_comments, $missed_count );
|
|
|
|
|
my ( $comments, $comment_count, $past_count, $ignore_count );
|
|
|
|
|
my ( %past, %current );
|
|
|
|
@@ -195,8 +196,8 @@ my $json_outfile = $options{json};
|
|
|
|
|
die "Unable to find configuration file $cfgfile\n" unless ( -e $cfgfile );
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# We're receiving the datetime for the last recording (that's the recording
|
|
|
|
|
# for the previous month), which isn't appropriate unless we're marking
|
|
|
|
|
# If we're receiving the datetime for the last recording (that's the recording
|
|
|
|
|
# for the previous month), it isn't appropriate unless we're marking
|
|
|
|
|
# comments.
|
|
|
|
|
#
|
|
|
|
|
if (defined($lastrecording)) {
|
|
|
|
@@ -204,6 +205,11 @@ if (defined($lastrecording)) {
|
|
|
|
|
unless defined($full_html_outfile);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Record whether or not we got a -lastrecording=DATETIME option
|
|
|
|
|
#
|
|
|
|
|
$lr_option_status = defined($lastrecording);
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# One at least of the output files must be present
|
|
|
|
|
#
|
|
|
|
@@ -213,7 +219,7 @@ unless ($full_html_outfile || $html_outfile || $json_outfile) {
|
|
|
|
|
die "Missing output file option\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
say "DEBUG level is %d", $DEBUG if $DEBUG > 0;
|
|
|
|
|
say "DEBUG level is $DEBUG" if $DEBUG > 0;
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
# Use the date provided for the review month or use today's date as the default
|
|
|
|
@@ -231,6 +237,7 @@ else {
|
|
|
|
|
@review_month = Today();
|
|
|
|
|
}
|
|
|
|
|
$review_month[2] = 1;
|
|
|
|
|
_debug( $DEBUG > 1, '@review_month = ' . Dumper( \@review_month ) );
|
|
|
|
|
@dc_lm = @review_month; # TODO: Is this right?
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
@@ -291,6 +298,11 @@ $tags = [$tags] unless ( ref($tags) eq 'ARRAY' );
|
|
|
|
|
@deftime = split( ':', $start_time );
|
|
|
|
|
my $release_dow = Decode_Day_of_Week($release_day);
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Report the date cache name before working on it.
|
|
|
|
|
#
|
|
|
|
|
emit( $silent, "Date cache: ", $date_cache_name, "\n" );
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
# Set last recording date and time from option or cache
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
@@ -298,18 +310,38 @@ my $release_dow = Decode_Day_of_Week($release_day);
|
|
|
|
|
# recording for the previous month) as an option, or we'll check the
|
|
|
|
|
# cache.
|
|
|
|
|
#
|
|
|
|
|
if (defined($lastrecording)) {
|
|
|
|
|
if ($lr_option_status) {
|
|
|
|
|
#
|
|
|
|
|
# Parse and perform rudimentary validation on the -lastrecording option
|
|
|
|
|
# The -lastrecording option is present, so parse and perform rudimentary
|
|
|
|
|
# validation on the values.
|
|
|
|
|
#
|
|
|
|
|
emit( $silent, "Last recording from option: ", $lastrecording, "\n" );
|
|
|
|
|
_debug( $DEBUG > 1, '$lastrecording = ' . $lastrecording );
|
|
|
|
|
|
|
|
|
|
# Compute the last month, with a date
|
|
|
|
|
@dc_lm = find_last_month(\@review_month);
|
|
|
|
|
_debug( $DEBUG > 1, '@dc_lm = ' . Dumper( \@dc_lm ) );
|
|
|
|
|
|
|
|
|
|
@dc_lr = parse_to_dc( $lastrecording, \@deftime );
|
|
|
|
|
_debug( $DEBUG > 1, '@dc_lr = ' . Dumper( \@dc_lr ) );
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Validate the provided lastrecording date which should be a few days
|
|
|
|
|
# before the start of the review month or into the next month. This will
|
|
|
|
|
# make it between 26 and 35 days from the start of the previous month.
|
|
|
|
|
#
|
|
|
|
|
my $lr_delta_days
|
|
|
|
|
= abs( Delta_Days( @dc_lm[ 0 .. 2 ], @dc_lr[ 0 .. 2 ] ) );
|
|
|
|
|
_debug(
|
|
|
|
|
$DEBUG > 1,
|
|
|
|
|
'Difference between @dc_lm and @dc_lr: ' . $lr_delta_days
|
|
|
|
|
);
|
|
|
|
|
if ($lr_delta_days < 26 || $lr_delta_days > 35) {
|
|
|
|
|
say "Problem with -lastrecording=DATETIME specification.";
|
|
|
|
|
say "Difference between given date and start of the month before ",
|
|
|
|
|
"the review month is $lr_delta_days days";
|
|
|
|
|
die "Can't continue\n";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
emit( $silent, "Getting last recording from cache\n" );
|
|
|
|
@@ -317,7 +349,6 @@ else {
|
|
|
|
|
#
|
|
|
|
|
# Load the cache
|
|
|
|
|
#
|
|
|
|
|
emit( $silent, "Date cache: ", $date_cache_name, "\n" );
|
|
|
|
|
%date_cache = load_cache($date_cache_name);
|
|
|
|
|
#_debug( $DEBUG > 1, '%date_cache = ' . Dumper(\%date_cache) );
|
|
|
|
|
|
|
|
|
@@ -331,8 +362,8 @@ else {
|
|
|
|
|
# Abort if the cache didn't have the date
|
|
|
|
|
#
|
|
|
|
|
unless (defined($lastrecording)) {
|
|
|
|
|
say "The date and time of the last recording is not in the cache";
|
|
|
|
|
say "Use option -lastrecording=DATETIME (or -lr=DATETIME) instead";
|
|
|
|
|
say "The date and time of the last recording is not in the cache. Use";
|
|
|
|
|
say "option -lastrecording=DATETIME (or -lr=DATETIME) to define them.";
|
|
|
|
|
die "Can't continue\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@@ -752,7 +783,7 @@ if ($show_comments) {
|
|
|
|
|
#
|
|
|
|
|
if ( $full_html_outfile ) {
|
|
|
|
|
{
|
|
|
|
|
$/ = '';
|
|
|
|
|
local $/ = '';
|
|
|
|
|
chomp($row->{comment_text}); # NOTE: experimental
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
@@ -909,6 +940,48 @@ my $tt = Template->new(
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
# Update the cache from the -lastrecording=DATETIME option if needed
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
if ($lr_option_status) {
|
|
|
|
|
#
|
|
|
|
|
# We were given the last recording as an option. The most likely reason is
|
|
|
|
|
# that it's not in the cache, but it may contain a correction. Look to
|
|
|
|
|
# see if it is in the cache, and if so, whether it's the same. Add it if
|
|
|
|
|
# it's missing or update it unless it agrees.
|
|
|
|
|
#
|
|
|
|
|
emit( $silent, "Loading recording date cache\n" );
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Load the cache
|
|
|
|
|
#
|
|
|
|
|
%date_cache = load_cache($date_cache_name);
|
|
|
|
|
#_debug( $DEBUG > 1, '%date_cache = ' . Dumper(\%date_cache) );
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Create the month key from the month before the review month, then see if
|
|
|
|
|
# it's already in the cache. The date of the start of last month is in
|
|
|
|
|
# $dt_lm.
|
|
|
|
|
#
|
|
|
|
|
my $monthkey = sprintf( "%d-%02d-01", $dt_lm->year, $dt_lm->month );
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# If the key is not in the %date_cache OR if it's already there but the
|
|
|
|
|
# value part doesn't match the last recording specification run
|
|
|
|
|
# update_cache to make changes. The tests are made in this order so we
|
|
|
|
|
# don't try and reference a non-existent element.
|
|
|
|
|
#
|
|
|
|
|
if (!exists( $date_cache{$monthkey} )
|
|
|
|
|
|| ( exists( $date_cache{$monthkey} )
|
|
|
|
|
&& ( $date_cache{$monthkey} ne $lastrecording ) )
|
|
|
|
|
)
|
|
|
|
|
{
|
|
|
|
|
$date_cache{$monthkey} = $lastrecording;
|
|
|
|
|
update_cache( $date_cache_name, \%date_cache );
|
|
|
|
|
emit( $silent, "Updated date cache\n" );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
|
# Generate the HTML fragment and add it to the JSON if that output is requested
|
|
|
|
|
#-------------------------------------------------------------------------------
|
|
|
|
@@ -1307,7 +1380,14 @@ sub dc_to_dt {
|
|
|
|
|
# PURPOSE: Load the date cache into a hash
|
|
|
|
|
# PARAMETERS: $cache_name Name of file holding the cache
|
|
|
|
|
# RETURNS: Contents of cache as a hash
|
|
|
|
|
# DESCRIPTION:
|
|
|
|
|
# DESCRIPTION: Opens the nominated file, parses each record, and adds the
|
|
|
|
|
# data to a hash. The record should contain the following:
|
|
|
|
|
# * 'YYYY-MM-01' the month for which the details are being
|
|
|
|
|
# recorded
|
|
|
|
|
# * ',' comma field separator
|
|
|
|
|
# * 'YYYY-MM-DD HH:MM:SS' timestamp of the recording
|
|
|
|
|
# The file is closed once it has been scanned. The function
|
|
|
|
|
# returns the completed hash.
|
|
|
|
|
# THROWS: No exceptions
|
|
|
|
|
# COMMENTS: None
|
|
|
|
|
# SEE ALSO: N/A
|
|
|
|
@@ -1317,24 +1397,102 @@ sub load_cache {
|
|
|
|
|
|
|
|
|
|
my ( $month, $datetime, %result );
|
|
|
|
|
|
|
|
|
|
open( my $dc, '<', $cache_name )
|
|
|
|
|
or die "$0 : failed to open '$cache_name': $!\n";
|
|
|
|
|
#
|
|
|
|
|
# Open the file in read mode
|
|
|
|
|
#
|
|
|
|
|
open( my $dcfh, '<', $cache_name )
|
|
|
|
|
or die "$PROG: failed to open '$cache_name': $!\n";
|
|
|
|
|
|
|
|
|
|
while ( my $line = <$dc> ) {
|
|
|
|
|
while ( my $line = <$dcfh> ) {
|
|
|
|
|
chomp($line);
|
|
|
|
|
if ( ( $month, $datetime )
|
|
|
|
|
= ( $line =~ /^(\d{4}-\d{2}-\d{2}),(.*)$/ ) )
|
|
|
|
|
{
|
|
|
|
|
$result{$month} = $datetime;
|
|
|
|
|
}
|
|
|
|
|
# TODO: Report any errors found in the file
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close($dc)
|
|
|
|
|
or warn "$0 : failed to close '$cache_name': $!\n";
|
|
|
|
|
close($dcfh)
|
|
|
|
|
or warn "${PROG}: failed to close '$cache_name': $!\n";
|
|
|
|
|
|
|
|
|
|
return %result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#=== FUNCTION ================================================================
|
|
|
|
|
# NAME: append_cache
|
|
|
|
|
# PURPOSE: Append a new line to the cache
|
|
|
|
|
# PARAMETERS: $cache_name Name of file holding the cache
|
|
|
|
|
# $line New record to add
|
|
|
|
|
# RETURNS: Nothing
|
|
|
|
|
# DESCRIPTION: Opens the nominated file and appends the new record in $line.
|
|
|
|
|
# The file is closed once it has been updated.
|
|
|
|
|
# THROWS: No exceptions
|
|
|
|
|
# COMMENTS: None
|
|
|
|
|
# SEE ALSO: N/A
|
|
|
|
|
#===============================================================================
|
|
|
|
|
sub append_cache {
|
|
|
|
|
my ( $cache_name, $line ) = @_;
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Open the file in append mode
|
|
|
|
|
#
|
|
|
|
|
open( my $dcfh, '>>', $cache_name )
|
|
|
|
|
or die "$PROG: failed to open '$cache_name': $!\n";
|
|
|
|
|
|
|
|
|
|
say $dcfh $line;
|
|
|
|
|
|
|
|
|
|
close($dcfh)
|
|
|
|
|
or warn "${PROG}: failed to close '$cache_name': $!\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#=== FUNCTION ================================================================
|
|
|
|
|
# NAME: update_cache
|
|
|
|
|
# PURPOSE: Make changes to an existing line in the cache
|
|
|
|
|
# PARAMETERS: $cache_name Name of file holding the cache
|
|
|
|
|
# $rhash Hashref holding the updated cache contents
|
|
|
|
|
# RETURNS: Nothing
|
|
|
|
|
# DESCRIPTION: Makes a backup of the nominated file. Opens, truncates it and
|
|
|
|
|
# positions for writing (using 'seek'). The now empty file is
|
|
|
|
|
# filled with data from the hash and closed.
|
|
|
|
|
# THROWS: No exceptions
|
|
|
|
|
# COMMENTS: Uses 'copy' from File::Copy
|
|
|
|
|
# SEE ALSO: N/A
|
|
|
|
|
#===============================================================================
|
|
|
|
|
sub update_cache {
|
|
|
|
|
my ( $cache_name, $rhash ) = @_;
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Copy the cache file to a backup
|
|
|
|
|
#
|
|
|
|
|
copy($cache_name,"${cache_name}~")
|
|
|
|
|
or die "Unable to back up '$cache_name'\n";
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Open the original file in write mode
|
|
|
|
|
#
|
|
|
|
|
open( my $dcfh, '>', $cache_name )
|
|
|
|
|
or die "${PROG}: failed to open '$cache_name': $!\n";
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Truncate the file and seek to the start again
|
|
|
|
|
#
|
|
|
|
|
truncate($dcfh,0)
|
|
|
|
|
or die "${PROG}: failed to truncate '$cache_name': $!\n";
|
|
|
|
|
seek($dcfh,0,0)
|
|
|
|
|
or die "$PROG: failed to seek in '$cache_name': $!\n";
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
|
# Write the cache data to the file
|
|
|
|
|
#
|
|
|
|
|
for my $key (sort(keys(%$rhash))) {
|
|
|
|
|
say $dcfh sprintf("%s,%s",$key, $rhash->{$key});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
close($dcfh)
|
|
|
|
|
or warn "${PROG}: failed to close '$cache_name': $!\n";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#=== FUNCTION ================================================================
|
|
|
|
|
# NAME: find_last_month
|
|
|
|
|
# PURPOSE: Finds the previous month for working out marks
|
|
|
|
@@ -1811,7 +1969,7 @@ make_shownotes - Make show notes for the Hacker Public Radio Community News show
|
|
|
|
|
|
|
|
|
|
=head1 VERSION
|
|
|
|
|
|
|
|
|
|
This documentation refers to B<make_shownotes> version 0.4.4
|
|
|
|
|
This documentation refers to B<make_shownotes> version 0.4.5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
=head1 USAGE
|
|
|
|
@@ -1924,19 +2082,49 @@ This will cause the generation of the file:
|
|
|
|
|
|
|
|
|
|
=item B<-lastrecording=DATETIME> or B<-lr=DATETIME>
|
|
|
|
|
|
|
|
|
|
As mentioned for B<-full-html=FILE>, the script needs the date of the last
|
|
|
|
|
recording when marking comments. This can be extracted from the file
|
|
|
|
|
referenced in the configuration data using the setting B<cache>. By default
|
|
|
|
|
the name of this file is B<recording_dates.dat>, and its contents are managed
|
|
|
|
|
when the script B<make_email> is run.
|
|
|
|
|
As mentioned for B<-full-html=FILE>, and later in the I<MARKING COMMENTS>
|
|
|
|
|
section, the script needs the date of the last recording when marking
|
|
|
|
|
comments. This can be extracted from the file referenced in the configuration
|
|
|
|
|
data using the setting B<cache>. By default the name of this file is
|
|
|
|
|
B<recording_dates.dat>, and its contents are managed when the script
|
|
|
|
|
B<make_email> is run and by this script.
|
|
|
|
|
|
|
|
|
|
If for any reason the date and time of the last recording is missing, these
|
|
|
|
|
values can be defined with this option.
|
|
|
|
|
values can be defined with this option, and these values will be written to
|
|
|
|
|
the cache file (or modified, if necessary).
|
|
|
|
|
|
|
|
|
|
The format can be an ISO 8601 date followed by a 24-hour time, such as
|
|
|
|
|
'2020-01-25 15:00'. If the time is omitted it defaults to the value of
|
|
|
|
|
'2020-01-25 15:00:00'. If the time is omitted it defaults to the value of
|
|
|
|
|
I<starttime> in the configuration file.
|
|
|
|
|
|
|
|
|
|
The script will update the cache file with the date and time used in this
|
|
|
|
|
option if the relevant entry is missing. Also, if an entry is present but the
|
|
|
|
|
values are different from those provided with the option, the relevant entry
|
|
|
|
|
will be updated.
|
|
|
|
|
|
|
|
|
|
Note that the B<DATETIME> value must contain the date of the last recording.
|
|
|
|
|
This will be checked, and written to the cache file prefixed by a "key"
|
|
|
|
|
consisting of the first day of the month I<BEFORE> the month being reviewed.
|
|
|
|
|
|
|
|
|
|
For example, when generating the notes for August 2025 the following command
|
|
|
|
|
will be needed if there is no last recording date (for July 2025) in the
|
|
|
|
|
cache:
|
|
|
|
|
|
|
|
|
|
./make_shownotes -from=2025-08-01 -full-html=full_shownotes_%s.html \
|
|
|
|
|
-mail -comments -lr="2025-08-01 15:00:00"
|
|
|
|
|
|
|
|
|
|
Here we need the last recording date for the show reviewing HPR shows in July
|
|
|
|
|
2025. The date and time for this recording was in early August (Friday before
|
|
|
|
|
the first Monday of September 2025-09-01), as shown. This combination will
|
|
|
|
|
result in the addition of the following line to the cache file:
|
|
|
|
|
|
|
|
|
|
2025-07-01,2025-08-01 15:00:00
|
|
|
|
|
|
|
|
|
|
As mentioned, the addition of such date and time information to the cache will
|
|
|
|
|
normally be performed by B<make_email>, which performs the date computations
|
|
|
|
|
itself, unlike this script. This feature in this script is an alternative for
|
|
|
|
|
special cases.
|
|
|
|
|
|
|
|
|
|
=item B<-[no]silent>
|
|
|
|
|
|
|
|
|
|
This option controls whether the script reports details of its progress
|
|
|
|
@@ -2501,6 +2689,7 @@ Modules used:
|
|
|
|
|
Date::Parse
|
|
|
|
|
DateTime::Duration
|
|
|
|
|
DateTime
|
|
|
|
|
File::Copy
|
|
|
|
|
Getopt::Long
|
|
|
|
|
HTML::Entities
|
|
|
|
|
JSON
|
|
|
|
|