diff --git a/Community_News/make_email b/Community_News/make_email index b369244..adbf5af 100755 --- a/Community_News/make_email +++ b/Community_News/make_email @@ -87,7 +87,7 @@ our $VERSION = '0.3.4'; ( my $basedir = abs_path($0) ) =~ s|/?[^/]*$||mx; my $configfile = "$basedir/.${PROG}.cfg"; -my ( %recdates, $rdfh ); +my ( %recdates ); # # Run in the script's directory @@ -627,7 +627,7 @@ elsif ( ! -e $cfg_recdatefile) { # Load the recording dates # if ($cfg_recdatefile) { - %recdates = load_cache($cfg_recdatefile, $rdfh); + %recdates = load_cache($cfg_recdatefile); } _debug($DEBUG >= 2, diff --git a/Community_News/make_shownotes b/Community_News/make_shownotes index 5d7d353..f91eaab 100755 --- a/Community_News/make_shownotes +++ b/Community_News/make_shownotes @@ -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 @@ -103,14 +105,13 @@ my $defseries_id = 47; my $defcache = "$basedir/recording_dates.dat"; 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 ( $t_time, $missed_comments, $missed_count ); -my ( $comments, $comment_count, $past_count, $ignore_count ); -my ( %past, %current ); +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, %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 ); #------------------------------------------------------------------------------- # The structure of the JSON to be sent to the HPR server @@ -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? #------------------------------------------------------------------------------- @@ -238,9 +245,9 @@ $review_month[2] = 1; #------------------------------------------------------------------------------- emit( $silent, "Configuration file: ", $cfgfile, "\n" ); my $conf = Config::General->new( - -ConfigFile => $cfgfile, - -InterPolateVars => 1, - -ExtendedAccess => 1, + -ConfigFile => $cfgfile, + -InterPolateVars => 1, + -ExtendedAccess => 1, -UseApacheInclude => 1, ); my %config = $conf->getall(); @@ -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 version 0.4.4 +This documentation refers to B 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. By default -the name of this file is B, and its contents are managed -when the script B is run. +As mentioned for B<-full-html=FILE>, and later in the I +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. By default the name of this file is +B, and its contents are managed when the script +B 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 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 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 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, 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 diff --git a/Community_News/recording_dates.dat b/Community_News/recording_dates.dat index 20c5304..892feab 100644 --- a/Community_News/recording_dates.dat +++ b/Community_News/recording_dates.dat @@ -31,4 +31,8 @@ 2025-01-01,2025-01-31 15:00:00 2025-02-01,2025-02-28 16:00:00 2025-03-01,2025-04-04 16:00:00 -2025-04-01,2025-05-02 16:00:00 +2025-04-01,2025-05-02 15:00:00 +2025-05-01,2025-05-30 15:00:00 +2025-06-01,2025-07-04 15:00:00 +2025-07-01,2025-08-01 15:00:00 +2025-08-01,2025-08-29 15:00:00 diff --git a/Community_News/shownote_template12.tpl b/Community_News/shownote_template12.tpl index 0929319..84f3c31 100644 --- a/Community_News/shownote_template12.tpl +++ b/Community_News/shownote_template12.tpl @@ -161,9 +161,6 @@ by [%- END -%] -[%- IF mark_comments == 1 || ctext == 1 -%] -

Updated on [% date.format(date.now,'%Y-%m-%d %H:%M:%S') %]

-[%- END -%] [%- END %] [%# ---------------------------------------------------------------------------------------- -%] [% cc = (comment_count - past_count) -%] diff --git a/Community_News/shownotes_container.tpl b/Community_News/shownotes_container.tpl index bdf2082..5ff65ff 100644 --- a/Community_News/shownotes_container.tpl +++ b/Community_News/shownotes_container.tpl @@ -1,5 +1,6 @@ [%# /home/cendjm/HPR/Community_News/shownotes_container.tpl 2024-06-22 -%] [%# Container to display Community News shownotes as an HTML page -%] +[%- USE date -%] [% DEFAULT shownotes = "" episode = "" month_year = "" @@ -52,7 +53,8 @@

Your ideas, projects, opinions - podcasted.

New episodes every weekday Monday through Friday.
- Temporary version of the Community News notes for hosts to use when recording

+ Temporary version of the Community News notes for hosts to use when recording
+ Updated on [% date.format(date.now,'%Y-%m-%d %H:%M:%S') %]