667 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			667 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | #!/usr/bin/env perl | ||
|  | #=============================================================================== | ||
|  | # | ||
|  | #         FILE: edit_series | ||
|  | # | ||
|  | #        USAGE: ./edit_series [-help] [-debug=N] [-[no]update] [-config=FILE] | ||
|  | #               [-series_id=id] [-[no]regex] [-[no]description] [-[no]private] | ||
|  | #               [-[no]valid] series_name | ||
|  | # | ||
|  | #  DESCRIPTION: A simple editor for the HPR miniseries table | ||
|  | # | ||
|  | #      OPTIONS: --- | ||
|  | # REQUIREMENTS: --- | ||
|  | #         BUGS: --- | ||
|  | #        NOTES: Based on edit_host | ||
|  | #       AUTHOR: Dave Morriss (djm), Dave.Morriss@gmail.com | ||
|  | #      VERSION: 0.0.3 | ||
|  | #      CREATED: 2020-06-21 17:58:19 | ||
|  | #     REVISION: 2021-06-23 22:11:13 | ||
|  | # | ||
|  | #=============================================================================== | ||
|  | 
 | ||
|  | use 5.010; | ||
|  | use strict; | ||
|  | use warnings; | ||
|  | use utf8; | ||
|  | 
 | ||
|  | use Getopt::Long; | ||
|  | use Pod::Usage; | ||
|  | 
 | ||
|  | use Config::General; | ||
|  | use File::Temp; | ||
|  | use File::Slurper qw{ read_text }; | ||
|  | use SQL::Abstract; | ||
|  | use DBI; | ||
|  | 
 | ||
|  | use Data::Dumper; | ||
|  | 
 | ||
|  | # | ||
|  | # Version number (manually incremented) | ||
|  | # | ||
|  | our $VERSION = '0.0.3'; | ||
|  | 
 | ||
|  | # | ||
|  | # Script and directory names | ||
|  | # | ||
|  | ( my $PROG = $0 ) =~ s|.*/||mx; | ||
|  | ( my $DIR  = $0 ) =~ s|/?[^/]*$||mx; | ||
|  | $DIR = '.' unless $DIR; | ||
|  | 
 | ||
|  | #------------------------------------------------------------------------------- | ||
|  | # Declarations | ||
|  | #------------------------------------------------------------------------------- | ||
|  | # | ||
|  | # Constants and other declarations | ||
|  | # | ||
|  | my $basedir    = "$ENV{HOME}/HPR/Database"; | ||
|  | my $configfile = "$basedir/.hpr_db.cfg"; | ||
|  | 
 | ||
|  | # | ||
|  | # Declarations | ||
|  | # | ||
|  | my ( $dbh, $sth1, $h1, $rc ); | ||
|  | my ( $series_name, %changes ); | ||
|  | 
 | ||
|  | # | ||
|  | # Enable Unicode mode | ||
|  | # | ||
|  | binmode STDOUT, ":encoding(UTF-8)"; | ||
|  | binmode STDERR, ":encoding(UTF-8)"; | ||
|  | 
 | ||
|  | # | ||
|  | # Options and arguments | ||
|  | # | ||
|  | my %options; | ||
|  | Options( \%options ); | ||
|  | 
 | ||
|  | # | ||
|  | # Default help | ||
|  | # | ||
|  | pod2usage( -msg => "Version $VERSION\n", -exitval => 1 ) | ||
|  |     if ( $options{'help'} ); | ||
|  | 
 | ||
|  | # | ||
|  | # Collect options | ||
|  | # | ||
|  | my $DEBUG = ( $options{'debug'} ? $options{'debug'} : 0 ); | ||
|  | 
 | ||
|  | my $cfgfile | ||
|  |     = ( defined( $options{config} ) ? $options{config} : $configfile ); | ||
|  | 
 | ||
|  | my $updatedb = ( defined( $options{'updatedb'} ) ? $options{'updatedb'} : 0 ); | ||
|  | my $regexp   = ( defined( $options{'regexp'} )   ? $options{'regexp'}   : 0 ); | ||
|  | 
 | ||
|  | my $description = $options{'description'}; | ||
|  | my $private     = $options{'private'}; | ||
|  | my $valid       = $options{'valid'}; | ||
|  | 
 | ||
|  | # | ||
|  | # There must be at least one field to change. We check for the definition here | ||
|  | # because the values returned may be zero or one or the variable may be | ||
|  | # undefined. | ||
|  | # | ||
|  | die "Select one of -[no]description, -[no]private and -[no]valid\n" | ||
|  |     unless ( defined($description) || defined($private) || defined($valid) ); | ||
|  | 
 | ||
|  | # | ||
|  | # Was a series id provided (through an option)? | ||
|  | # | ||
|  | my $series_id = $options{'series_id'}; | ||
|  | 
 | ||
|  | # | ||
|  | # Deal with the two routes: one via the unique series id, and the other vai the | ||
|  | # less unique series name | ||
|  | # | ||
|  | unless ($series_id) { | ||
|  |     # | ||
|  |     # Get the arg | ||
|  |     # | ||
|  |     $series_name = shift; | ||
|  |     pod2usage( -msg => "Specify the series name\n", -exitval => 1 ) | ||
|  |         unless $series_name; | ||
|  | } | ||
|  | 
 | ||
|  | # | ||
|  | # Sanity check | ||
|  | # | ||
|  | die "Unable to find $cfgfile\n" unless ( -e $cfgfile ); | ||
|  | 
 | ||
|  | # | ||
|  | # Load configuration data | ||
|  | # | ||
|  | my $conf = new Config::General( | ||
|  |     -ConfigFile      => $cfgfile, | ||
|  |     -InterPolateVars => 1, | ||
|  |     -ExtendedAccess  => 1 | ||
|  | ); | ||
|  | my %config = $conf->getall(); | ||
|  | 
 | ||
|  | # | ||
|  | # Connect to the database | ||
|  | # | ||
|  | my $dbhost = $config{database}->{host} // '127.0.0.1'; | ||
|  | my $dbport = $config{database}->{port} // 3306; | ||
|  | my $dbname = $config{database}->{name}; | ||
|  | my $dbuser = $config{database}->{user}; | ||
|  | my $dbpwd  = $config{database}->{password}; | ||
|  | 
 | ||
|  | # 2022-04-12 The MariaDB driver was there one minute and then it wasn't! | ||
|  | # | ||
|  | #$dbh = DBI->connect( "DBI:MariaDB:database=$dbname;host=$dbhost;port=$dbport", | ||
|  | #    $dbuser, $dbpwd, { AutoCommit => 1 } ) | ||
|  | #    or die $DBI::errstr; | ||
|  | 
 | ||
|  | $dbh = DBI->connect( "dbi:mysql:database=$dbname;host=$dbhost;port=$dbport", | ||
|  |     $dbuser, $dbpwd, { AutoCommit => 1 } ) | ||
|  |     or die $DBI::errstr; | ||
|  | 
 | ||
|  | # | ||
|  | # Enable client-side UTF8 | ||
|  | # | ||
|  | $dbh->{mysql_enable_utf8} = 1; | ||
|  | 
 | ||
|  | # | ||
|  | # Prepare to read the database either for the series_id or the exact or | ||
|  | # approximate name | ||
|  | # | ||
|  | if ($series_id) { | ||
|  |     # | ||
|  |     # Simple series_id query | ||
|  |     # | ||
|  |     $sth1 = $dbh->prepare(q{SELECT * FROM miniseries WHERE id = ?}); | ||
|  |     $sth1->execute($series_id); | ||
|  |     if ( $dbh->err ) { | ||
|  |         warn $dbh->errstr; | ||
|  |     } | ||
|  | } | ||
|  | else { | ||
|  |     # | ||
|  |     # Series name query | ||
|  |     # | ||
|  |     if ($regexp) { | ||
|  |         # | ||
|  |         # Regexp match requested. Count how many matches there are | ||
|  |         # | ||
|  |         $sth1 = $dbh->prepare( | ||
|  |             q{SELECT count(*) AS count FROM miniseries WHERE name REGEXP ?}); | ||
|  |         $sth1->execute($series_name); | ||
|  |         if ( $dbh->err ) { | ||
|  |             warn $dbh->errstr; | ||
|  |         } | ||
|  |         if ( $h1 = $sth1->fetchrow_hashref ) { | ||
|  |             my $matches = $h1->{count}; | ||
|  |             if ($matches > 1) { | ||
|  |                 die "Too many matches to regex '$series_name' ($matches)\n"; | ||
|  |             } elsif ($matches == 0) { | ||
|  |                 die "No matches to regex '$series_name'\n"; | ||
|  |             } | ||
|  |         } | ||
|  |         else { | ||
|  |             die "Unable to find series matching regex '$series_name'\n"; | ||
|  |         } | ||
|  | 
 | ||
|  |         $sth1 = $dbh->prepare(q{SELECT * FROM miniseries WHERE name REGEXP ?}); | ||
|  |     } | ||
|  |     else { | ||
|  |         $sth1 = $dbh->prepare(q{SELECT * FROM miniseries WHERE name = ?}); | ||
|  |     } | ||
|  | 
 | ||
|  |     # | ||
|  |     # Execute the query | ||
|  |     # | ||
|  |     $sth1->execute($series_name); | ||
|  |     if ( $dbh->err ) { | ||
|  |         warn $dbh->errstr; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | # | ||
|  | # Did we find it? | ||
|  | # | ||
|  | if ( $h1 = $sth1->fetchrow_hashref ) { | ||
|  |     # | ||
|  |     # Found. Save the series_id to simplify the update if we don't already have | ||
|  |     # it | ||
|  |     # | ||
|  |     $series_id //= $h1->{id}; | ||
|  | 
 | ||
|  |     # | ||
|  |     # Report on the series details | ||
|  |     # | ||
|  |     printf "Series details\n" . | ||
|  |         "Id:          %s\n" . | ||
|  |         "Name:        %s\n" . | ||
|  |         "Description: %s\n" . | ||
|  |         "Private:     %s\n" . | ||
|  |         "Image:       '%s'\n" . | ||
|  |         "Valid:       %s\n", | ||
|  |         $h1->{id}, | ||
|  |         $h1->{name}, | ||
|  |         (   length( $h1->{description} ) > 80 | ||
|  |             ? substr( $h1->{description}, 0, 80 ) . '...' | ||
|  |             : $h1->{description} ), | ||
|  |         $h1->{private}, | ||
|  |         $h1->{image}, | ||
|  |         $h1->{valid}; | ||
|  | 
 | ||
|  |     # | ||
|  |     # So what needs changing? | ||
|  |     # | ||
|  |     #<<< [perltidy messes up the following] | ||
|  |     if ($description) { | ||
|  |         $changes{description} = check_field( 'description', | ||
|  |             scalar( run_editor( $h1->{description}, ['+set paste'] ) ), 1500, qr{(\n)} ); | ||
|  |     } | ||
|  |     if (defined($private)) { | ||
|  |         $changes{private} = $private if ($h1->{private} ne $private); | ||
|  |     } | ||
|  |     if (defined($valid)) { | ||
|  |         $changes{valid} = $valid if ($h1->{valid} ne $valid); | ||
|  |     } | ||
|  |     #>>> | ||
|  |     print Dumper( \%changes ), "\n" if ( $DEBUG > 2 ); | ||
|  | 
 | ||
|  |     # | ||
|  |     # Are we updating the database then? | ||
|  |     # | ||
|  |     if ($updatedb) { | ||
|  |         # | ||
|  |         # Was anything edited? | ||
|  |         # | ||
|  |         if (%changes) { | ||
|  |             # | ||
|  |             # Did the edits do anything? If not delete the element from the | ||
|  |             # hash since there's no point writing it to the database | ||
|  |             # | ||
|  |             for my $key ( keys(%changes) ) { | ||
|  |                 if ( $changes{$key} eq $h1->{$key} ) { | ||
|  |                     print "No change made to $key, ignored\n"; | ||
|  |                     delete( $changes{$key} ); | ||
|  |                 } | ||
|  |             } | ||
|  |             print Dumper( \%changes ), "\n" if ( $DEBUG > 2 ); | ||
|  |         } | ||
|  | 
 | ||
|  |         # | ||
|  |         # If there's anything left apply the changes | ||
|  |         # | ||
|  |         if (%changes) { | ||
|  |             # | ||
|  |             # Go into transaction mode here so we can fail safely | ||
|  |             # | ||
|  |             $rc = $dbh->begin_work or die $dbh->errstr; | ||
|  | 
 | ||
|  |             my $sql   = SQL::Abstract->new; | ||
|  |             my %where = ( id => $series_id ); | ||
|  |             my ( $stmt, @bind ) | ||
|  |                 = $sql->update( 'miniseries', \%changes, \%where ); | ||
|  |             #print "$stmt\n"; | ||
|  |             #print join( ",", map {"'$_'"} @bind ), "\n"; | ||
|  | 
 | ||
|  |             my $sth = $dbh->prepare($stmt); | ||
|  |             my $rv  = $sth->execute(@bind); | ||
|  | 
 | ||
|  |             # | ||
|  |             # Respond to any error by rolling back | ||
|  |             # | ||
|  |             if ( $dbh->err ) { | ||
|  |                 warn $dbh->errstr; | ||
|  |                 eval { $dbh->rollback }; | ||
|  |                 $rv = 0; | ||
|  |             } | ||
|  |             else { | ||
|  |                 $dbh->commit; | ||
|  |             } | ||
|  |             $rv = 0 if ( $rv eq '0E0' ); | ||
|  | 
 | ||
|  |             # | ||
|  |             # Update the log file | ||
|  |             # | ||
|  |             if ($rv) { | ||
|  |                 print "Updated database\n"; | ||
|  |                 print "Changed fields: ", | ||
|  |                     join( ", ", sort( keys(%changes) ) ), "\n"; | ||
|  |             } | ||
|  |             else { | ||
|  |                 print "Series not updated due to error\n"; | ||
|  |             } | ||
|  | 
 | ||
|  |         } | ||
|  |         else { | ||
|  |             print "There was nothing to do\n"; | ||
|  |         } | ||
|  |     } | ||
|  |     else { | ||
|  |         print "Database not updated\n"; | ||
|  |     } | ||
|  | } | ||
|  | else { | ||
|  |     if ($series_id) { | ||
|  |         print "Unable to find series number $series_id\n"; | ||
|  |     } | ||
|  |     else { | ||
|  |         print "Unable to find series name $series_name\n"; | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | exit; | ||
|  | 
 | ||
|  | #===  FUNCTION  ================================================================ | ||
|  | #         NAME: run_editor | ||
|  | #      PURPOSE: Run an interactive vim editor on a string | ||
|  | #   PARAMETERS: $string         String to edit | ||
|  | #               $options        An arrayref containing options for vim | ||
|  | #                               (optional). Example '+set paste'. Each option | ||
|  | #                               (such as '-c startinsert') needs to be | ||
|  | #                               a separate array element. | ||
|  | #      RETURNS: Edited string | ||
|  | #  DESCRIPTION: Makes a temporary file with File::Temp ensuring that the file | ||
|  | #               is in utf8 mode. Writes the edit string to the file and invokes | ||
|  | #               the 'vim' editor on it. The resulting file is then read back | ||
|  | #               into a string and returned to the caller, again taking care to | ||
|  | #               retain utf8 mode. | ||
|  | #       THROWS: No exceptions | ||
|  | #     COMMENTS: File::Slurp and UTF-8 don't go well together. Moved to | ||
|  | #               File::Slurper instead | ||
|  | #     SEE ALSO: N/A | ||
|  | #=============================================================================== | ||
|  | sub run_editor { | ||
|  |     my ( $string, $options ) = @_; | ||
|  | 
 | ||
|  |     # | ||
|  |     # Build an arguments array for 'system' | ||
|  |     # | ||
|  |     my @args; | ||
|  |     push( @args, @$options ) if $options; | ||
|  | 
 | ||
|  |     # | ||
|  |     # Make a temporary file | ||
|  |     # | ||
|  |     my $tfh = File::Temp->new; | ||
|  |     binmode $tfh, ":encoding(UTF-8)"; | ||
|  |     my $tfn = $tfh->filename; | ||
|  |     print $tfh $string if $string; | ||
|  |     $tfh->close; | ||
|  | 
 | ||
|  |     # | ||
|  |     # Add the filename to the arguments | ||
|  |     # | ||
|  |     push( @args, $tfn ); | ||
|  | 
 | ||
|  |     die "Edit failed\n" | ||
|  |         unless ( system( ( 'vim', @args ) ) == 0 ); | ||
|  | 
 | ||
|  |     return read_text($tfn); | ||
|  | } | ||
|  | 
 | ||
|  | #===  FUNCTION  ================================================================ | ||
|  | #         NAME: check_field | ||
|  | #      PURPOSE: Checks the a field is not too long and doesn't contain certain | ||
|  | #               characters | ||
|  | #   PARAMETERS: $name           name of field | ||
|  | #               $field          string to be checked | ||
|  | #               $maxlen         maximum string length | ||
|  | #               $regex          regex containing illegal characters to be removed | ||
|  | #      RETURNS: The input string truncated and with any illegal characters | ||
|  | #               removed. | ||
|  | #  DESCRIPTION: Runs a substitution on the string then truncates the result if | ||
|  | #               it is too long. | ||
|  | #       THROWS: No exceptions | ||
|  | #     COMMENTS: None | ||
|  | #     SEE ALSO: N/A | ||
|  | #=============================================================================== | ||
|  | sub check_field { | ||
|  |     my ( $name, $field, $maxlen, $regex ) = @_; | ||
|  | 
 | ||
|  |     return unless $field; | ||
|  | 
 | ||
|  |     $field =~ s/$regex//g; | ||
|  |     if ( length($field) > $maxlen ) { | ||
|  |         warn "Field '$name' too long (" | ||
|  |             . length($field) | ||
|  |             . "); truncated to " | ||
|  |             . $maxlen . "\n"; | ||
|  |         $field = substr( $field, 0, $maxlen ); | ||
|  |     } | ||
|  |     return $field; | ||
|  | } | ||
|  | 
 | ||
|  | #===  FUNCTION  ================================================================ | ||
|  | #         NAME: Options | ||
|  | #      PURPOSE: Processes command-line options | ||
|  | #   PARAMETERS: $optref     Hash reference to hold the options | ||
|  | #      RETURNS: Undef | ||
|  | #  DESCRIPTION: | ||
|  | #       THROWS: no exceptions | ||
|  | #     COMMENTS: none | ||
|  | #     SEE ALSO: n/a | ||
|  | #=============================================================================== | ||
|  | sub Options { | ||
|  |     my ($optref) = @_; | ||
|  | 
 | ||
|  |     my @options = ( | ||
|  |         "help",        "debug=i", "updatedb!",    "config=s", | ||
|  |         "series_id=i", "regexp",  "description!", "private!", | ||
|  |         "valid!", | ||
|  |     ); | ||
|  | 
 | ||
|  |     if ( !GetOptions( $optref, @options ) ) { | ||
|  |         pod2usage( -msg => "Version $VERSION\n", -exitval => 1 ); | ||
|  |     } | ||
|  | 
 | ||
|  |     return; | ||
|  | } | ||
|  | 
 | ||
|  | __END__ | ||
|  | 
 | ||
|  | 
 | ||
|  | #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | ||
|  | #  Application Documentation | ||
|  | #%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% | ||
|  | #{{{ | ||
|  | 
 | ||
|  | =head1 NAME | ||
|  | 
 | ||
|  | edit_series - edit one or more fields in the database for a given series | ||
|  | 
 | ||
|  | =head1 VERSION | ||
|  | 
 | ||
|  | This documentation refers to edit_series version 0.0.3 | ||
|  | 
 | ||
|  | 
 | ||
|  | =head1 USAGE | ||
|  | 
 | ||
|  |     edit_series [-h] [-debug=N] [-[no]updatedb] [-series_id=ID] [-regexp] | ||
|  |     [-[no]description] [-[no]private] [-[no]valid] [-config=FILE] series_name | ||
|  | 
 | ||
|  |     edit_series -updatedb -description GIMP | ||
|  |     edit_series -updatedb -description -regexp Awk | ||
|  |     edit_series -updatedb -noprivate -valid -series_id=102 | ||
|  | 
 | ||
|  | 
 | ||
|  | =head1 REQUIRED ARGUMENTS | ||
|  | 
 | ||
|  | =over 4 | ||
|  | 
 | ||
|  | =item B<series_name> | ||
|  | 
 | ||
|  | Unless the B<-series_id=ID> option is given (see the OPTIONS section) it is | ||
|  | necessary to provide a series name. | ||
|  | 
 | ||
|  | Unless the B<-regexp> option is provided (see the OPTIONS section) the | ||
|  | series name must match exactly, otherwise it is regarded as a MySQL regular | ||
|  | expression. | ||
|  | 
 | ||
|  | =back | ||
|  | 
 | ||
|  | =head1 OPTIONS | ||
|  | 
 | ||
|  | =over 4 | ||
|  | 
 | ||
|  | =item B<-[no]updatedb> | ||
|  | 
 | ||
|  | This option is required to make the script apply any changes that are made to | ||
|  | the database. By default no updates are applied (B<-noupdatedb>). | ||
|  | 
 | ||
|  | =item B<-regexp> | ||
|  | 
 | ||
|  | This option causes the B<series_name> argument to be interpreted as a regular | ||
|  | expression for the MySQL database. By default the argument is treated as if it | ||
|  | is an exact match. | ||
|  | 
 | ||
|  | =item B<-series_id=ID> | ||
|  | 
 | ||
|  | The series can be specified by the series ID through this route. If this used then | ||
|  | the B<series_name> argument is not required (and is ignored if given). | ||
|  | 
 | ||
|  | =item B<-[no]description> | ||
|  | 
 | ||
|  | This option, if given (as B<-description>), indicates that the 'description' | ||
|  | field is to be edited.  The Vim editor is invoked to make changes. The default | ||
|  | is B<-nodescription> meaning that this field is not to be edited. | ||
|  | 
 | ||
|  | =item B<-[no]private> | ||
|  | 
 | ||
|  | This option, if given (as B<-private>), indicates that the 'private' field is | ||
|  | to be set to 'true'. If given as B<-noprivate> this field is set to 'false'. | ||
|  | If omitted altogether then the field is not changed. | ||
|  | 
 | ||
|  | =item B<-[no]valid> | ||
|  | 
 | ||
|  | This option, if given (as B<-valid>), indicates that the 'valid' field is to | ||
|  | be set to 'true'. If given as B<-novalid> this field is set to 'false'. | ||
|  | If omitted altogether then the field is not changed. | ||
|  | 
 | ||
|  | =item B<-config=FILE> | ||
|  | 
 | ||
|  | This option allows an alternative configuration file to be used. This file | ||
|  | defines the location of the database, its port, its name and the username and | ||
|  | password to be used to access it. This feature was added to allow the script | ||
|  | to access alternative databases or the live database over an SSH tunnel. | ||
|  | 
 | ||
|  | See the CONFIGURATION AND ENVIRONMENT section below for the file format. | ||
|  | 
 | ||
|  | If the option is omitted the default file is used: B<.hpr_db.cfg> | ||
|  | 
 | ||
|  | =back | ||
|  | 
 | ||
|  | =head1 DESCRIPTION | ||
|  | 
 | ||
|  | The script B<edit_series> provides an editor interface to certain fields in | ||
|  | the B<miniseries> table of the HPR database. The fields are: | ||
|  | 
 | ||
|  | =over 4 | ||
|  | 
 | ||
|  | =item B<description> | ||
|  | 
 | ||
|  | This field is stored in a 'TEXT' field in the database. It is possible for | ||
|  | the field to contain HTML, and some series do. In general it is better to keep | ||
|  | this field short since it is displayed in its entirety before the notes for | ||
|  | each show in the series. | ||
|  | 
 | ||
|  | =back | ||
|  | 
 | ||
|  | =head1 DIAGNOSTICS | ||
|  | 
 | ||
|  | =over 4 | ||
|  | 
 | ||
|  | =item B<Select one of -description, -private and -valid> | ||
|  | 
 | ||
|  | At least one of these options is required. This a fatal error. | ||
|  | 
 | ||
|  | =item B<Specify the series name> | ||
|  | 
 | ||
|  | If no series name has been provided, and the B<-series_id=ID> option has not been | ||
|  | used the script is unable to determine the series to edit. This is a fatal | ||
|  | error. | ||
|  | 
 | ||
|  | =item B<Unable to find ...> | ||
|  | 
 | ||
|  | The configuration file containing details of the database cannot be found. | ||
|  | This is a fatal error. | ||
|  | 
 | ||
|  | =item B<DBI connect ... failed: Access denied for user ... > | ||
|  | 
 | ||
|  | The database connection has been denied. Check the configuration details (see | ||
|  | below). This a fatal error. | ||
|  | 
 | ||
|  | =item B<Too many matches to regex ...> | ||
|  | 
 | ||
|  | In B<-regex> mode a series name has been provided that matches too many series in | ||
|  | the database. Try again with a less ambiguous name. This a fatal error. | ||
|  | 
 | ||
|  | =item B<Unable to find series matching regex ...> | ||
|  | 
 | ||
|  | In B<-regex> mode a series name has been provided that matches no qseries in the | ||
|  | database. Try again. This a fatal error. | ||
|  | 
 | ||
|  | =item B<Edit failed> | ||
|  | 
 | ||
|  | If the Vim edit session fails in some way the script reports it this way. This | ||
|  | a fatal error. | ||
|  | 
 | ||
|  | =item B<Field '...' is too long (...); truncated to ...> | ||
|  | 
 | ||
|  | The string provided for the field is greater than the limit and has been | ||
|  | truncated. This is a warning. | ||
|  | 
 | ||
|  | =back | ||
|  | 
 | ||
|  | =head1 CONFIGURATION AND ENVIRONMENT | ||
|  | 
 | ||
|  | The script obtains the credentials it requires to open the HPR database from | ||
|  | a configuration file. The name of the file it expects is B<.hpr_db.cfg> in the | ||
|  | directory holding the script. To change this will require changing the script. | ||
|  | 
 | ||
|  | The configuration file format is as follows: | ||
|  | 
 | ||
|  |  <database> | ||
|  |      host = 127.0.0.1 | ||
|  |      port = PORT | ||
|  |      name = DATABASE | ||
|  |      user = USERNAME | ||
|  |      password = PASSWORD | ||
|  |  </database> | ||
|  | 
 | ||
|  | =head1 DEPENDENCIES | ||
|  | 
 | ||
|  |  Config::General | ||
|  |  DBI | ||
|  |  Data::Dumper | ||
|  |  File::Slurper | ||
|  |  File::Temp | ||
|  |  Getopt::Long | ||
|  |  Pod::Usage | ||
|  |  SQL::Abstract | ||
|  | 
 | ||
|  | =head1 BUGS AND LIMITATIONS | ||
|  | 
 | ||
|  | There are no known bugs in this module. | ||
|  | Please report problems to Dave Morriss  (Dave.Morriss@gmail.com) | ||
|  | Patches are welcome. | ||
|  | 
 | ||
|  | =head1 AUTHOR | ||
|  | 
 | ||
|  | Dave Morriss (Dave.Morriss@gmail.com) | ||
|  | 
 | ||
|  | =head1 LICENCE AND COPYRIGHT | ||
|  | 
 | ||
|  | Copyright (c) 2020 Dave Morriss (Dave.Morriss@gmail.com). All rights reserved. | ||
|  | 
 | ||
|  | This module is free software; you can redistribute it and/or | ||
|  | modify it under the same terms as Perl itself. See perldoc perlartistic. | ||
|  | 
 | ||
|  | This program is distributed in the hope that it will be useful, | ||
|  | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
|  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
|  | 
 | ||
|  | =cut | ||
|  | 
 | ||
|  | #}}} | ||
|  | 
 | ||
|  | # [zo to open fold, zc to close] | ||
|  | 
 | ||
|  | # vim: syntax=perl:ts=8:sw=4:et:ai:tw=78:fo=tcrqn21:fdm=marker | ||
|  | 
 |